This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch hlog in repository https://gitbox.apache.org/repos/asf/camel.git
commit 2d5de4eb9e1a75a379eaf5b9d4a9f37ca22c4b39 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Tue Dec 10 13:10:14 2024 +0100 CAMEL-21468: camel-http - Allow to log request/repsonse HTTP raw data to see what is sent over the wire. --- .../catalog/beans/LoggingHttpActivityListener.json | 2 +- .../LoggingHttpActivityListenerConfigurer.java | 6 ++ .../camel/bean/LoggingHttpActivityListener.json | 2 +- .../apache/camel/component/http/HttpComponent.java | 1 + .../apache/camel/component/http/HttpEndpoint.java | 2 + .../apache/camel/component/http/HttpProducer.java | 22 ++++- .../http/LoggingHttpActivityListener.java | 94 +++++++++++++++------- 7 files changed, 97 insertions(+), 32 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/LoggingHttpActivityListener.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/LoggingHttpActivityListener.json index 19ca316a584..9e6db510a8a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/LoggingHttpActivityListener.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/LoggingHttpActivityListener.json @@ -10,7 +10,7 @@ "groupId": "org.apache.camel", "artifactId": "camel-http", "version": "4.10.0-SNAPSHOT", - "properties": { "showRouteId": { "index": 0, "kind": "property", "displayName": "Show Route Id", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "true", "description": "Show route ID." }, "showRouteGroup": { "index": 1, "kind": "property", "displayName": "Show Route Group", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaul [...] + "properties": { "showRouteId": { "index": 0, "kind": "property", "displayName": "Show Route Id", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "true", "description": "Show route ID." }, "showRouteGroup": { "index": 1, "kind": "property", "displayName": "Show Route Group", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaul [...] } } diff --git a/components/camel-http/src/generated/java/org/apache/camel/component/http/LoggingHttpActivityListenerConfigurer.java b/components/camel-http/src/generated/java/org/apache/camel/component/http/LoggingHttpActivityListenerConfigurer.java index 073035688d4..bdd0202c8d9 100644 --- a/components/camel-http/src/generated/java/org/apache/camel/component/http/LoggingHttpActivityListenerConfigurer.java +++ b/components/camel-http/src/generated/java/org/apache/camel/component/http/LoggingHttpActivityListenerConfigurer.java @@ -36,6 +36,8 @@ public class LoggingHttpActivityListenerConfigurer extends org.apache.camel.supp case "showRouteGroup": target.setShowRouteGroup(property(camelContext, boolean.class, value)); return true; case "showrouteid": case "showRouteId": target.setShowRouteId(property(camelContext, boolean.class, value)); return true; + case "showstreams": + case "showStreams": target.setShowStreams(property(camelContext, boolean.class, value)); return true; case "sourcelocationloggername": case "sourceLocationLoggerName": target.setSourceLocationLoggerName(property(camelContext, boolean.class, value)); return true; default: return false; @@ -58,6 +60,8 @@ public class LoggingHttpActivityListenerConfigurer extends org.apache.camel.supp case "showRouteGroup": return boolean.class; case "showrouteid": case "showRouteId": return boolean.class; + case "showstreams": + case "showStreams": return boolean.class; case "sourcelocationloggername": case "sourceLocationLoggerName": return boolean.class; default: return null; @@ -81,6 +85,8 @@ public class LoggingHttpActivityListenerConfigurer extends org.apache.camel.supp case "showRouteGroup": return target.isShowRouteGroup(); case "showrouteid": case "showRouteId": return target.isShowRouteId(); + case "showstreams": + case "showStreams": return target.isShowStreams(); case "sourcelocationloggername": case "sourceLocationLoggerName": return target.isSourceLocationLoggerName(); default: return null; diff --git a/components/camel-http/src/generated/resources/META-INF/services/org/apache/camel/bean/LoggingHttpActivityListener.json b/components/camel-http/src/generated/resources/META-INF/services/org/apache/camel/bean/LoggingHttpActivityListener.json index 19ca316a584..9e6db510a8a 100644 --- a/components/camel-http/src/generated/resources/META-INF/services/org/apache/camel/bean/LoggingHttpActivityListener.json +++ b/components/camel-http/src/generated/resources/META-INF/services/org/apache/camel/bean/LoggingHttpActivityListener.json @@ -10,7 +10,7 @@ "groupId": "org.apache.camel", "artifactId": "camel-http", "version": "4.10.0-SNAPSHOT", - "properties": { "showRouteId": { "index": 0, "kind": "property", "displayName": "Show Route Id", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "true", "description": "Show route ID." }, "showRouteGroup": { "index": 1, "kind": "property", "displayName": "Show Route Group", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaul [...] + "properties": { "showRouteId": { "index": 0, "kind": "property", "displayName": "Show Route Id", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "true", "description": "Show route ID." }, "showRouteGroup": { "index": 1, "kind": "property", "displayName": "Show Route Group", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaul [...] } } diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpComponent.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpComponent.java index 396cb4773da..218d316bf72 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpComponent.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpComponent.java @@ -1068,6 +1068,7 @@ public class HttpComponent extends HttpCommonComponent implements RestProducerFa clientConnectionManager.close(); clientConnectionManager = null; } + ServiceHelper.stopService(httpActivityListener); super.doStop(); } diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java index 7df8d469e07..3ca8bf6eef8 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java @@ -332,6 +332,8 @@ public class HttpEndpoint extends HttpCommonEndpoint implements LineNumberAware if (httpClient instanceof Closeable closeable) { IOHelper.close(closeable); } + ServiceHelper.stopService(httpActivityListener); + super.doStop(); } // Properties diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java index 93c550035a0..211c61ad244 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java @@ -37,6 +37,7 @@ import java.util.Map.Entry; import org.apache.camel.CamelExchangeException; import org.apache.camel.Exchange; import org.apache.camel.ExchangePropertyKey; +import org.apache.camel.LineNumberAware; import org.apache.camel.Message; import org.apache.camel.RuntimeCamelException; import org.apache.camel.TypeConverter; @@ -83,7 +84,7 @@ import org.apache.hc.core5.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class HttpProducer extends DefaultProducer { +public class HttpProducer extends DefaultProducer implements LineNumberAware { private static final Logger LOG = LoggerFactory.getLogger(HttpProducer.class); @@ -813,4 +814,23 @@ public class HttpProducer extends DefaultProducer { this.httpClient = httpClient; } + @Override + public int getLineNumber() { + return getEndpoint().getLineNumber(); + } + + @Override + public void setLineNumber(int lineNumber) { + // noop + } + + @Override + public String getLocation() { + return getEndpoint().getLocation(); + } + + @Override + public void setLocation(String location) { + // noop + } } diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/LoggingHttpActivityListener.java b/components/camel-http/src/main/java/org/apache/camel/component/http/LoggingHttpActivityListener.java index cda6dc06c85..1b96727b1d2 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/LoggingHttpActivityListener.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/LoggingHttpActivityListener.java @@ -35,8 +35,10 @@ import org.apache.camel.support.processor.DefaultMaskingFormatter; import org.apache.camel.support.service.ServiceSupport; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import static org.apache.camel.support.LoggerHelper.getLineNumberLoggerName; @@ -59,8 +61,13 @@ public class LoggingHttpActivityListener extends ServiceSupport implements Camel private boolean showExchangeId = true; @Metadata(defaultValue = "true", description = "Show the HTTP body.") private boolean showBody = true; + @Metadata(defaultValue = "true", + description = "Whether to show HTTP body that are streaming based. Beware that Camel will have to read the content into memory to print to log, and will re-create the HttpEntity stored on the request/response object. If you have large payloads then this can impact performance.") + private boolean showStreams = true; @Metadata(defaultValue = "true", label = "formatting", description = "Show the HTTP headers.") private boolean showHeaders = true; + @Metadata(defaultValue = "50000", description = "Limits the number of characters logged from the HTTP body.") + private int maxChars = 50000; @Metadata(description = "If true, mask sensitive information like password or passphrase in the log.") private Boolean logMask; @Metadata(defaultValue = "true", description = "If enabled then each information is outputted as separate LOG events.") @@ -79,29 +86,6 @@ public class LoggingHttpActivityListener extends ServiceSupport implements Camel } } - private CamelLogger getLogger(Object source, Exchange exchange) { - String name = null; - if (sourceLocationLoggerName) { - name = getLineNumberLoggerName(source); - } - if (name == null) { - name = LoggingHttpActivityListener.class.getName(); - } - LoggingLevel level = LoggingLevel.INFO; - if (loggingLevel != null && !loggingLevel.equals("INFO")) { - level = LoggingLevel.valueOf(loggingLevel); - } - return new CamelLogger(name, level); - } - - private String getValue(boolean sensitive, Object value) { - String v = value != null ? value.toString() : null; - if (v != null && (sensitive || logMask != null && logMask)) { - v = maskingFormatter.format(v); - } - return v; - } - @Override public void onRequestSubmitted(Object source, Exchange exchange, HttpHost httpHost, ClassicHttpRequest request) { CamelLogger logger = getLogger(source, exchange); @@ -170,12 +154,33 @@ public class LoggingHttpActivityListener extends ServiceSupport implements Camel lines.add(""); // empty line before body try { var e = request != null ? request.getEntity() : response.getEntity(); - if (e != null && e.isRepeatable()) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - e.writeTo(bos); - lines.add(getValue(false, bos.toString())); - } else if (e != null) { - lines.add("WARN: Cannot log HTTP body because the body is not repeatable"); + if (e != null) { + if (e.isStreaming() && !showStreams) { + lines.add("WARN: Cannot log HTTP body because the body is streaming"); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + e.writeTo(bos); + String data = bos.toString(); + if (data.length() > maxChars) { + data = data.substring(0, maxChars) + " ... [Body clipped after " + maxChars + + " chars, total length is " + data.length() + "]"; + } + lines.add(getValue(false, data)); + if (!e.isRepeatable()) { + // need to re-create body as stream is EOL + byte[] arr = bos.toByteArray(); + ContentType ct = null; + if (e.getContentType() != null) { + ct = ContentType.parse(e.getContentType()); + } + e = new ByteArrayEntity(arr, ct); + if (request != null) { + request.setEntity(e); + } else { + response.setEntity(e); + } + } + } } } catch (Exception e) { // ignore @@ -193,6 +198,29 @@ public class LoggingHttpActivityListener extends ServiceSupport implements Camel } } + private CamelLogger getLogger(Object source, Exchange exchange) { + String name = null; + if (sourceLocationLoggerName) { + name = getLineNumberLoggerName(source); + } + if (name == null) { + name = LoggingHttpActivityListener.class.getName(); + } + LoggingLevel level = LoggingLevel.INFO; + if (loggingLevel != null && !loggingLevel.equals("INFO")) { + level = LoggingLevel.valueOf(loggingLevel); + } + return new CamelLogger(name, level); + } + + private String getValue(boolean sensitive, Object value) { + String v = value != null ? value.toString() : null; + if (v != null && (sensitive || logMask != null && logMask)) { + v = maskingFormatter.format(v); + } + return v; + } + @Override public CamelContext getCamelContext() { return camelContext; @@ -243,6 +271,14 @@ public class LoggingHttpActivityListener extends ServiceSupport implements Camel this.showBody = showBody; } + public boolean isShowStreams() { + return showStreams; + } + + public void setShowStreams(boolean showStreams) { + this.showStreams = showStreams; + } + public boolean isShowHeaders() { return showHeaders; }