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 <[email protected]>
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
[email protected].

Reply via email to