You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2018/03/09 06:45:25 UTC

[camel] 01/02: Updated documentation of the AWS XRay component Added support for a more comprehensive view of sub/segments by copying over the AWS XRay entity (if provided in the exchange headers) to the new thread. Instead of creating a new segment for the seda route the invoked route will be added as subsegment to the invoking route. This is especially useful if some Amazon services (like the S3 upload) are used, which may occur in its own thread. Fixed some possible NPEs

This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git

commit ba05e0201fdf4164c71441029adf5e0c5642642d
Author: Roman Vottner <ro...@gmx.at>
AuthorDate: Thu Mar 8 17:00:38 2018 +0100

    Updated documentation of the AWS XRay component
    Added support for a more comprehensive view of sub/segments by copying over the AWS XRay entity (if provided in the exchange headers) to the new thread. Instead of creating a new segment for the seda route the invoked route will be added as subsegment to the invoking route. This is especially useful if some Amazon services (like the S3 upload) are used, which may occur in its own thread.
    Fixed some possible NPEs
---
 .../camel-aws-xray/src/main/docs/aws-xray.adoc     | 28 +++++-
 .../aws/xray/TraceAnnotatedTracingStrategy.java    | 17 ++--
 .../camel/component/aws/xray/XRayTracer.java       | 23 +++++
 .../aws/xray/ComprehensiveTrackingTest.java        | 99 ++++++++++++++++++++++
 .../camel/component/aws/xray/bean/SomeBean.java    | 33 ++++++++
 5 files changed, 192 insertions(+), 8 deletions(-)

diff --git a/components/camel-aws-xray/src/main/docs/aws-xray.adoc b/components/camel-aws-xray/src/main/docs/aws-xray.adoc
index 8ac8cac..e980c3b 100644
--- a/components/camel-aws-xray/src/main/docs/aws-xray.adoc
+++ b/components/camel-aws-xray/src/main/docs/aws-xray.adoc
@@ -1,7 +1,7 @@
 [[AWSXRay-AWSXRayComponent]]
 ## AWS XRay Component
 
-*Available as of Camel 2.20*
+*Available as of Camel 2.21*
 
 The camel-aws-xray component is used for tracing and timing incoming and outgoing Camel messages using https://aws.amazon.com/de/xray/[AWS XRay].
 
@@ -20,7 +20,7 @@ To include both, AWS XRay and Camel, dependencies use the following Maven import
       <dependency>
         <groupId>com.amazonaws</groupId>
         <artifactId>aws-xray-recorder-sdk-bom</artifactId>
-        <version>1.2.0</version>
+        <version>1.3.1</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
@@ -52,9 +52,10 @@ The configuration properties for the AWS XRay tracer are:
 |=======================================================================
 |Option |Default |Description
 
-|excludePatterns |  | Sets exclude pattern(s) that will disable tracing for Camel
+|addExcludePatterns | &nbsp; | Sets exclude pattern(s) that will disable tracing for Camel
 messages that matches the pattern. The content is a Set<String> where the key is a pattern matching routeId's. The pattern
 uses the rules from Intercept.
+|setTracingStrategy | NoopTracingStrategy | Allows a custom Camel `InterceptStrategy` to be provided in order to track invoked processor definitions like `BeanDefinition` or `ProcessDefinition`. `TraceAnnotatedTracingStrategy` will track any classes invoked via `.bean(...)` or `.process(...)` that contain a `@XRayTrace` annotation at class level.
 
 |=======================================================================
 
@@ -88,6 +89,27 @@ AWS XRay tracer bean. Camel will automatically discover and use it.
   </bean>
 ---------------------------------------------------------------------------------------------------------
 
+In case of the default `NoopTracingStrategy` only the creation and deletion of exchanges is tracked but not the invocation of certain beans or EIP patterns.
+
+#### Tracking of comprehensive route execution
+
+In order to track the execution of an exchange among multiple routes, on exchange creation a unique trace ID is generated and stored in the headers if no corresponding value was yet available. This trace ID is copied over to new exchanges in order to keep a consistent view of the processed exchange.
+
+As AWS XRay traces work on a thread-local basis the current sub/segment should be copied over to the new thread and set as explained in https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-multithreading.html[in the AWS XRay documentation]. The Camel AWS XRay component therefore provides an additional header field that the component will use in order to set the passed AWS XRay `Entity` to the new thread and thus keep the tracked data to the route rather than exposing a new segm [...]
+
+The component will use the following constants found in the headers of the exchange:
+
+[width="100%",cols="30%,70%",options="header",]
+|=======================================================================
+|Header |Description
+
+| Camel-AWS-XRay-Trace-ID | Contains a reference to the AWS XRay `TraceID` object to provide a comprehensive view of the invoked routes
+| Camel-AWS-XRay-Trace-Entity | Contains a reference to the actual AWS XRay `Segment` or `Subsegment` which is copied over to the new thread. This header should be set in case a new thread is spawned and the performed tasks should be exposed as part of the executed route instead of creating a new unrelated segment.
+
+|=======================================================================
+
+Note that the AWS XRay `Entity` (i.e., `Segment` and `Subsegment`) are not serializable and therefore should not get passed to other JVM processes.
+
 ### Example
 
 You can find an example demonstrating the way to configure AWS XRay tracing within the tests accompanying this project.
\ No newline at end of file
diff --git a/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/TraceAnnotatedTracingStrategy.java b/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/TraceAnnotatedTracingStrategy.java
index 0229d80..fe32771 100644
--- a/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/TraceAnnotatedTracingStrategy.java
+++ b/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/TraceAnnotatedTracingStrategy.java
@@ -50,15 +50,22 @@ public class TraceAnnotatedTracingStrategy implements InterceptStrategy {
 
         if (processorDefinition instanceof BeanDefinition) {
             BeanProcessor beanProcessor = (BeanProcessor) nextTarget;
-            processorClass = beanProcessor.getBean().getClass();
+            if (null != beanProcessor && null != beanProcessor.getBean()) {
+                processorClass = beanProcessor.getBean().getClass();
+            }
         } else if (processorDefinition instanceof ProcessDefinition) {
             DelegateSyncProcessor syncProcessor = (DelegateSyncProcessor) nextTarget;
-            processorClass = syncProcessor.getProcessor().getClass();
+            if (null != syncProcessor && null != syncProcessor.getProcessor()) {
+                processorClass = syncProcessor.getProcessor().getClass();
+            }
         }
 
-        if (!processorClass.isAnnotationPresent(XRayTrace.class)) {
-            LOG.trace("{} does not contain an @Trace annotation. Skipping interception",
-                processorClass.getSimpleName());
+        if (processorClass == null) {
+            LOG.trace("Could not identify processor class on target processor {}", target);
+            return new DelegateAsyncProcessor(target);
+        } else if (!processorClass.isAnnotationPresent(XRayTrace.class)) {
+            LOG.trace("{} does not contain an @XRayTrace annotation. Skipping interception",
+                    processorClass.getSimpleName());
             return new DelegateAsyncProcessor(target);
         }
 
diff --git a/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/XRayTracer.java b/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/XRayTracer.java
index b18a560..128d964 100644
--- a/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/XRayTracer.java
+++ b/components/camel-aws-xray/src/main/java/org/apache/camel/component/aws/xray/XRayTracer.java
@@ -21,6 +21,8 @@ import java.net.URI;
 import java.util.*;
 
 import com.amazonaws.xray.AWSXRay;
+import com.amazonaws.xray.AWSXRayRecorder;
+import com.amazonaws.xray.entities.Entity;
 import com.amazonaws.xray.entities.Segment;
 import com.amazonaws.xray.entities.Subsegment;
 import com.amazonaws.xray.entities.TraceID;
@@ -66,6 +68,8 @@ public class XRayTracer extends ServiceSupport implements RoutePolicyFactory, St
 
     /** Header value kept in the message of the exchange **/
     public static final String XRAY_TRACE_ID = "Camel-AWS-XRay-Trace-ID";
+    // Note that the Entity itself is not serializable, so don't share this object among different VMs!
+    public static final String XRAY_TRACE_ENTITY = "Camel-AWS-XRay-Trace-Entity";
 
     private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -353,6 +357,25 @@ public class XRayTracer extends ServiceSupport implements RoutePolicyFactory, St
                 exchange.getIn().setHeader(XRAY_TRACE_ID, traceID.toString());
             }
 
+            // copy over any available trace entity (i.e. Segment) from the old thread to the new one
+            // according to AWS XRay documentation on multithreading:
+
+            // https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java-multithreading.html
+            //
+            // > If you use multiple threads to handle incoming requests, you can pass the current segment or subsegment
+            // > to the new thread and provide it to the global recorder. This ensures that the information recorded
+            // > within the new thread is associated with the same segment as the rest of the information recorded about
+            // > that request.
+            Entity entity = null;
+            if (exchange.getIn().getHeaders().containsKey(XRAY_TRACE_ENTITY)) {
+                entity = exchange.getIn().getHeader(XRAY_TRACE_ENTITY, Entity.class);
+            }
+
+            if (null != entity) {
+                AWSXRayRecorder recorder = AWSXRay.getGlobalRecorder();
+                recorder.setTraceEntity(entity);
+            }
+
             SegmentDecorator sd = getSegmentDecorator(route.getEndpoint());
             if (!AWSXRay.getCurrentSegmentOptional().isPresent()) {
                 Segment segment = AWSXRay.beginSegment(sanitizeName(route.getId()));
diff --git a/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/ComprehensiveTrackingTest.java b/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/ComprehensiveTrackingTest.java
new file mode 100644
index 0000000..1ff6992
--- /dev/null
+++ b/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/ComprehensiveTrackingTest.java
@@ -0,0 +1,99 @@
+package org.apache.camel.component.aws.xray;
+
+import org.apache.camel.Handler;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.aws.xray.bean.SomeBean;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+
+public class ComprehensiveTrackingTest extends CamelAwsXRayTestSupport {
+
+    private InvokeChecker invokeChecker = new InvokeChecker();
+
+    public ComprehensiveTrackingTest() {
+        super(
+                TestDataBuilder.createTrace().inRandomOrder()
+                        .withSegment(TestDataBuilder.createSegment("start")
+                                .withSubsegment(TestDataBuilder.createSubsegment("direct:a")
+                                        .withSubsegment(TestDataBuilder.createSubsegment("a")
+                                                .withSubsegment(TestDataBuilder.createSubsegment("seda:b"))
+                                                .withSubsegment(TestDataBuilder.createSubsegment("seda:c"))
+                                                // note that the subsegment name matches the routeId
+                                                .withSubsegment(TestDataBuilder.createSubsegment("test"))
+                                                // no tracing of the invoke checker bean as it wasn't annotated with
+                                                // @XRayTrace
+                                        )
+                                )
+                        )
+                        .withSegment(TestDataBuilder.createSegment("b"))
+                        .withSegment(TestDataBuilder.createSegment("c")
+                                // disabled by the LogSegmentDecorator (-> .to("log:...");
+                                //.withSubsegment(TestDataBuilder.createSubsegment("log:test"))
+                        )
+                        .withSegment(TestDataBuilder.createSegment("d"))
+                        // note no test-segment here!
+        );
+    }
+
+    @Test
+    public void testRoute() throws Exception {
+        template.requestBody("direct:start", "Hello");
+
+        verify();
+
+        assertThat(invokeChecker.gotInvoked(), is(equalTo(true)));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").routeId("start")
+                        .wireTap("seda:d")
+                        .to("direct:a");
+
+                from("direct:a").routeId("a")
+                        .log("routing at ${routeId}")
+                        .to("seda:b")
+                        .delay(2000)
+                        .bean(SomeBean.class)
+                        .to("seda:c")
+                        .log("End of routing");
+
+                from("seda:b").routeId("b")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(1000,2000)}"));
+
+                from("seda:c").routeId("c")
+                        .to("log:test")
+                        .delay(simple("${random(0,100)}"));
+
+                from("seda:d").routeId("d")
+                        .log("routing at ${routeId}")
+                        .delay(simple("${random(10,50)}"));
+
+                from("seda:test").routeId("test")
+                        .log("Async invoked route ${routeId} with body: ${body}")
+                        .bean(invokeChecker)
+                        .delay(simple("${random(10,50)}"));
+            }
+        };
+    }
+
+    public static class InvokeChecker {
+
+        private boolean invoked = false;
+
+        @Handler
+        public void invoke() {
+            this.invoked = true;
+        }
+
+        boolean gotInvoked() {
+            return this.invoked;
+        }
+    }
+}
diff --git a/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/bean/SomeBean.java b/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/bean/SomeBean.java
new file mode 100644
index 0000000..684f612
--- /dev/null
+++ b/components/camel-aws-xray/src/test/java/org/apache/camel/component/aws/xray/bean/SomeBean.java
@@ -0,0 +1,33 @@
+package org.apache.camel.component.aws.xray.bean;
+
+import com.amazonaws.xray.AWSXRay;
+import org.apache.camel.*;
+import org.apache.camel.component.aws.xray.XRayTrace;
+import org.apache.camel.component.aws.xray.XRayTracer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@XRayTrace
+public class SomeBean {
+
+    @Handler
+    public void doSomething(@Headers Map<String, Object> headers, CamelContext context) {
+
+        ProducerTemplate template = context.createProducerTemplate();
+        String body = "New exchange test";
+
+        Endpoint testEndpoint = template.getCamelContext().getEndpoint("seda:test");
+        Exchange exchange = testEndpoint.createExchange(ExchangePattern.InOnly);
+        exchange.getIn().setBody(body);
+
+        Map<String, Object> newHeaders = new HashMap<>();
+        // as we create a completely new exchange, this exchange has no trace ID yet specified and would result in a new
+        // trace ID being generated which would present a flawed view if viewed in the AWS XRay console
+        newHeaders.put(XRayTracer.XRAY_TRACE_ID, headers.get(XRayTracer.XRAY_TRACE_ID));
+        // store the current AWS XRay trace entity (= segment or subsegment) into the headers
+        newHeaders.put(XRayTracer.XRAY_TRACE_ENTITY, AWSXRay.getGlobalRecorder().getTraceEntity());
+        exchange.getIn().setHeaders(newHeaders);
+        template.asyncSend(testEndpoint, exchange);
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
acosentino@apache.org.