Amazon X-Ray SDK for Java - Amazon X-Ray
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Amazon X-Ray SDK for Java

The X-Ray SDK for Java is a set of libraries for Java web applications that provide classes and methods for generating and sending trace data to the X-Ray daemon. Trace data includes information about incoming HTTP requests served by the application, and calls that the application makes to downstream services using the Amazon SDK, HTTP clients, or an SQL database connector. You can also create segments manually and add debug information in annotations and metadata.

The X-Ray SDK for Java is an open source project. You can follow the project and submit issues and pull requests on GitHub: github.com/aws/aws-xray-sdk-java

Start by adding AWSXRayServletFilter as a servlet filter to trace incoming requests. A servlet filter creates a segment. While the segment is open, you can use the SDK client's methods to add information to the segment and create subsegments to trace downstream calls. The SDK also automatically records exceptions that your application throws while the segment is open.

Starting in release 1.3, you can instrument your application using aspect-oriented programming (AOP) in Spring. What this means is that you can instrument your application, while it is running on Amazon, without adding any code to your application's runtime.

Next, use the X-Ray SDK for Java to instrument your Amazon SDK for Java clients by including the SDK Instrumentor submodule in your build configuration. Whenever you make a call to a downstream Amazon Web Service or resource with an instrumented client, the SDK records information about the call in a subsegment. Amazon Web Services and the resources that you access within the services appear as downstream nodes on the trace map to help you identify errors and throttling issues on individual connections.

If you don't want to instrument all downstream calls to Amazon Web Services, you can leave out the Instrumentor submodule and choose which clients to instrument. Instrument individual clients by adding a TracingHandler to an Amazon SDK service client.

Other X-Ray SDK for Java submodules provide instrumentation for downstream calls to HTTP web APIs and SQL databases. You can use the X-Ray SDK for Java versions of HTTPClient and HTTPClientBuilder in the Apache HTTP submodule to instrument Apache HTTP clients. To instrument SQL queries, add the SDK's interceptor to your data source.

After you start using the SDK, customize its behavior by configuring the recorder and servlet filter. You can add plugins to record data about the compute resources running your application, customize sampling behavior by defining sampling rules, and set the log level to see more or less information from the SDK in your application logs.

Record additional information about requests and the work that your application does in annotations and metadata. Annotations are simple key-value pairs that are indexed for use with filter expressions, so that you can search for traces that contain specific data. Metadata entries are less restrictive and can record entire objects and arrays — anything that can be serialized into JSON.

Annotations and Metadata

Annotations and metadata are arbitrary text that you add to segments with the X-Ray SDK. Annotations are indexed for use with filter expressions. Metadata are not indexed, but can be viewed in the raw segment with the X-Ray console or API. Anyone that you grant read access to X-Ray can view this data.

When you have a lot of instrumented clients in your code, a single request segment can contain many subsegments, one for each call made with an instrumented client. You can organize and group subsegments by wrapping client calls in custom subsegments. You can create a custom subsegment for an entire function or any section of code, and record metadata and annotations on the subsegment instead of writing everything on the parent segment.

Submodules

You can download the X-Ray SDK for Java from Maven. The X-Ray SDK for Java is split into submodules by use case, with a bill of materials for version management:

If you use Maven or Gradle to build your application, add the X-Ray SDK for Java to your build configuration.

For reference documentation of the SDK's classes and methods, see Amazon X-Ray SDK for Java API Reference.

Requirements

The X-Ray SDK for Java requires Java 8 or later, Servlet API 3, the Amazon SDK, and Jackson.

The SDK depends on the following libraries at compile and runtime:

  • Amazon SDK for Java version 1.11.398 or later

  • Servlet API 3.1.0

These dependencies are declared in the SDK's pom.xml file and are included automatically if you build using Maven or Gradle.

If you use a library that is included in the X-Ray SDK for Java, you must use the included version. For example, if you already depend on Jackson at runtime and include JAR files in your deployment for that dependency, you must remove those JAR files because the SDK JAR includes its own versions of Jackson libraries.

Dependency management

The X-Ray SDK for Java is available from Maven:

  • Groupcom.amazonaws

  • Artifactaws-xray-recorder-sdk-bom

  • Version2.11.0

If you use Maven to build your application, add the SDK as a dependency in your pom.xml file.

Example pom.xml - dependencies
<dependencyManagement> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-bom</artifactId> <version>2.11.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-core</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-apache-http</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-aws-sdk</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-aws-sdk-instrumentor</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-sql-postgres</artifactId> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-sql-mysql</artifactId> </dependency> </dependencies>

For Gradle, add the SDK as a compile-time dependency in your build.gradle file.

Example build.gradle - dependencies
dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile("org.springframework.boot:spring-boot-starter-test") compile("com.amazonaws:aws-java-sdk-dynamodb") compile("com.amazonaws:aws-xray-recorder-sdk-core") compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk") compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk-instrumentor") compile("com.amazonaws:aws-xray-recorder-sdk-apache-http") compile("com.amazonaws:aws-xray-recorder-sdk-sql-postgres") compile("com.amazonaws:aws-xray-recorder-sdk-sql-mysql") testCompile("junit:junit:4.11") } dependencyManagement { imports { mavenBom('com.amazonaws:aws-java-sdk-bom:1.11.39') mavenBom('com.amazonaws:aws-xray-recorder-sdk-bom:2.11.0') } }

If you use Elastic Beanstalk to deploy your application, you can use Maven or Gradle to build on-instance each time you deploy, instead of building and uploading a large archive that includes all of your dependencies. See the sample application for an example that uses Gradle.

Configuring the X-Ray SDK for Java

The X-Ray SDK for Java includes a class named AWSXRay that provides the global recorder. This is a TracingHandler that you can use to instrument your code. You can configure the global recorder to customize the AWSXRayServletFilter that creates segments for incoming HTTP calls.

Service plugins

Use plugins to record information about the service hosting your application.

Plugins
  • Amazon EC2 – EC2Plugin adds the instance ID, Availability Zone, and the CloudWatch Logs Group.

  • Elastic Beanstalk – ElasticBeanstalkPlugin adds the environment name, version label, and deployment ID.

  • Amazon ECS – ECSPlugin adds the container ID.

  • Amazon EKS – EKSPlugin adds the container ID, cluster name, pod ID, and the CloudWatch Logs Group.

Segment resource data with Amazon EC2 and Elastic Beanstalk plugins.

To use a plugin, call withPlugin on your AWSXRayRecorderBuilder.

Example src/main/java/scorekeep/WebConfig.java - recorder
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorderBuilder; import com.amazonaws.xray.plugins.EC2Plugin; import com.amazonaws.xray.plugins.ElasticBeanstalkPlugin; import com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy; @Configuration public class WebConfig { ... static { AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin()).withPlugin(new ElasticBeanstalkPlugin()); URL ruleFile = WebConfig.class.getResource("/sampling-rules.json"); builder.withSamplingStrategy(new LocalizedSamplingStrategy(ruleFile)); AWSXRay.setGlobalRecorder(builder.build()); } }

The SDK also uses plugin settings to set the origin field on the segment. This indicates the type of Amazon resource that runs your application. When you use multiple plugins, the SDK uses the following resolution order to determine the origin: ElasticBeanstalk > EKS > ECS > EC2.

Sampling rules

The SDK uses the sampling rules you define in the X-Ray console to determine which requests to record. The default rule traces the first request each second, and five percent of any additional requests across all services sending traces to X-Ray. Create additional rules in the X-Ray console to customize the amount of data recorded for each of your applications.

The SDK applies custom rules in the order in which they are defined. If a request matches multiple custom rules, the SDK applies only the first rule.

Note

If the SDK can't reach X-Ray to get sampling rules, it reverts to a default local rule of the first request received at the beginning of each second, and five percent of any additional requests per host. This can occur if the host doesn't have permission to call sampling APIs, or can't connect to the X-Ray daemon, which acts as a TCP proxy for API calls made by the SDK.

You can also configure the SDK to load sampling rules from a JSON document. The SDK can use local rules as a backup for cases where X-Ray sampling is unavailable, or use local rules exclusively.

Example sampling-rules.json
{ "version": 2, "rules": [ { "description": "Player moves.", "host": "*", "http_method": "*", "url_path": "/api/move/*", "fixed_target": 0, "rate": 0.05 } ], "default": { "fixed_target": 1, "rate": 0.1 } }

This example defines one custom rule and a default rule. The custom rule applies a five-percent sampling rate with no minimum number of requests to trace for paths under /api/move/. The default rule traces the first request each second and 10 percent of additional requests.

The disadvantage of defining rules locally is that the fixed target is applied by each instance of the recorder independently, instead of being managed by the X-Ray service. As you deploy more hosts, the fixed rate is multiplied, making it harder to control the amount of data recorded.

On Amazon Lambda, you cannot modify the sampling rate. If your function is called by an instrumented service, calls that generated requests that were sampled by that service will be recorded by Lambda. If active tracing is enabled and no tracing header is present, Lambda makes the sampling decision.

To provide backup rules in Spring, configure the global recorder with a CentralizedSamplingStrategy in a configuration class.

Example src/main/java/myapp/WebConfig.java - recorder configuration
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorderBuilder; import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter; import com.amazonaws.xray.plugins.EC2Plugin; import com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy; @Configuration public class WebConfig { static { AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin()); URL ruleFile = WebConfig.class.getResource("/sampling-rules.json"); builder.withSamplingStrategy(new CentralizedSamplingStrategy(ruleFile)); AWSXRay.setGlobalRecorder(builder.build()); }

For Tomcat, add a listener that extends ServletContextListener and register the listener in the deployment descriptor.

Example src/com/myapp/web/Startup.java
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorderBuilder; import com.amazonaws.xray.plugins.EC2Plugin; import com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy; import java.net.URL; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class Startup implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) { AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin()); URL ruleFile = Startup.class.getResource("/sampling-rules.json"); builder.withSamplingStrategy(new CentralizedSamplingStrategy(ruleFile)); AWSXRay.setGlobalRecorder(builder.build()); } @Override public void contextDestroyed(ServletContextEvent event) { } }
Example WEB-INF/web.xml
... <listener> <listener-class>com.myapp.web.Startup</listener-class> </listener>

To use local rules only, replace the CentralizedSamplingStrategy with a LocalizedSamplingStrategy.

builder.withSamplingStrategy(new LocalizedSamplingStrategy(ruleFile));

Logging

By default, the SDK outputs ERROR-level messages to your application logs. You can enable debug-level logging on the SDK to output more detailed logs to your application log file. Valid log levels are DEBUG, INFO, WARN, ERROR, and FATAL. FATAL log level silences all log messages because the SDK does not log at fatal level.

Example application.properties

Set the logging level with the logging.level.com.amazonaws.xray property.

logging.level.com.amazonaws.xray = DEBUG

Use debug logs to identify issues, such as unclosed subsegments, when you generate subsegments manually.

Trace ID injection into logs

To expose the current fully qualified trace ID to your log statements, you can inject the ID into the mapped diagnostic context (MDC). Using the SegmentListener interface, methods are called from the X-Ray recorder during segment lifecycle events. When a segment or subsegment begins, the qualified trace ID is injected into the MDC with the key AWS-XRAY-TRACE-ID. When that segment ends, the key is removed from the MDC. This exposes the trace ID to the logging library in use. When a subsegment ends, its parent ID is injected into the MDC.

Example fully qualified trace ID

The fully qualified ID is represented as TraceID@EntityID

1-5df42873-011e96598b447dfca814c156@541b3365be3dafc3

This feature works with Java applications instrumented with the Amazon X-Ray SDK for Java, and supports the following logging configurations:

  • SLF4J front-end API with Logback backend

  • SLF4J front-end API with Log4J2 backend

  • Log4J2 front-end API with Log4J2 backend

See the following tabs for the needs of each front end and each backend.

SLF4J Frontend
  1. Add the following Maven dependency to your project.

    <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-slf4j</artifactId> <version>2.11.0</version> </dependency>
  2. Include the withSegmentListener method when building the AWSXRayRecorder. This adds a SegmentListener class, which automatically injects new trace IDs into the SLF4J MDC.

    The SegmentListener takes an optional string as a parameter to configure the log statement prefix. The prefix can be configured in the following ways:

    • None – Uses the default AWS-XRAY-TRACE-ID prefix.

    • Empty – Uses an empty string (e.g. "").

    • Custom – Uses a custom prefix as defined in the string.

    Example AWSXRayRecorderBuilder statement
    AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder .standard().withSegmentListener(new SLF4JSegmentListener("CUSTOM-PREFIX"));
Log4J2 front end
  1. Add the following Maven dependency to your project.

    <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-log4j</artifactId> <version>2.11.0</version> </dependency>
  2. Include the withSegmentListener method when building the AWSXRayRecorder. This will add a SegmentListener class, which automatically injects new fully qualified trace IDs into the SLF4J MDC.

    The SegmentListener takes an optional string as a parameter to configure the log statement prefix. The prefix can be configured in the following ways:

    • None – Uses the default AWS-XRAY-TRACE-ID prefix.

    • Empty – Uses an empty string (e.g. "") and removes the prefix.

    • Custom – Uses the custom prefix defined in the string.

    Example AWSXRayRecorderBuilder statement
    AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder .standard().withSegmentListener(new Log4JSegmentListener("CUSTOM-PREFIX"));
Logback backend

To insert the trace ID into your log events, you must modify the logger's PatternLayout, which formats each logging statement.

  1. Find where the patternLayout is configured. You can do this programmatically, or through an XML configuration file. To learn more, see Logback configuration.

  2. Insert %X{AWS-XRAY-TRACE-ID} anywhere in the patternLayout to insert the trace ID in future logging statements. %X{} indicates that you are retrieving a value with the provided key from the MDC. To learn more about PatternLayouts in Logback, see PatternLayout.

Log4J2 backend
  1. Find where the patternLayout is configured. You can do this programmatically, or through a configuration file written in XML, JSON, YAML, or properties format.

    To learn more about configuring Log4J2 through a configuration file, see Configuration.

    To learn more about configuring Log4J2 programmatically, see Programmatic Configuration.

  2. Insert %X{AWS-XRAY-TRACE-ID} anywhere in the PatternLayout to insert the trace ID in future logging statements. %X{} indicates that you are retrieving a value with the provided key from the MDC. To learn more about PatternLayouts in Log4J2, see Pattern Layout.

Trace ID Injection Example

The following shows a PatternLayout string modified to include the trace ID. The trace ID is printed after the thread name (%t) and before the log level (%-5p).

Example PatternLayout With ID injection
%d{HH:mm:ss.SSS} [%t] %X{AWS-XRAY-TRACE-ID} %-5p %m%n

Amazon X-Ray automatically prints the key and the trace ID in the log statement for easy parsing. The following shows a log statement using the modified PatternLayout.

Example Log statement with ID injection
2019-09-10 18:58:30.844 [nio-5000-exec-4] AWS-XRAY-TRACE-ID: 1-5d77f256-19f12e4eaa02e3f76c78f46a@1ce7df03252d99e1 WARN 1 - Your logging message here

The logging message itself is housed in the pattern %m and is set when calling the logger.

Segment listeners

Segement listeners are an interface to intercept lifecycle events such as the beginning and ending of segments produced by the AWSXRayRecorder. Implementation of a segment listener event function might be to add the same annotation to all subsegments when they are created with onBeginSubsegment, log a message after each segment is sent to the daemon using afterEndSegment, or to record queries sent by the SQL interceptors using beforeEndSubsegment to verify if the subsegment represents an SQL query, adding additional metadata if so.

To see the full list of SegmentListener functions, visit the documentation for the Amazon X-Ray Recorder SDK for Java API.

The following example shows how to add a consistent annotation to all subsegments on creation with onBeginSubsegment and to print a log message at the end of each segment with afterEndSegment.

Example MySegmentListener.java
import com.amazonaws.xray.entities.Segment; import com.amazonaws.xray.entities.Subsegment; import com.amazonaws.xray.listeners.SegmentListener; public class MySegmentListener implements SegmentListener { ..... @Override public void onBeginSubsegment(Subsegment subsegment) { subsegment.putAnnotation("annotationKey", "annotationValue"); } @Override public void afterEndSegment(Segment segment) { // Be mindful not to mutate the segment logger.info("Segment with ID " + segment.getId()); } }

This custom segment listener is then referenced when building the AWSXRayRecorder.

Example AWSXRayRecorderBuilder statement
AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder .standard().withSegmentListener(new MySegmentListener());

Environment variables

You can use environment variables to configure the X-Ray SDK for Java. The SDK supports the following variables.

  • AWS_XRAY_TRACING_NAME – Set a service name that the SDK uses for segments. Overrides the service name that you set on the servlet filter's segment naming strategy.

  • AWS_XRAY_DAEMON_ADDRESS – Set the host and port of the X-Ray daemon listener. By default, the SDK uses 127.0.0.1:2000 for both trace data (UDP) and sampling (TCP). Use this variable if you have configured the daemon to listen on a different port or if it is running on a different host.

    Format
    • Same portaddress:port

    • Different portstcp:address:port udp:address:port

  • AWS_XRAY_CONTEXT_MISSING – Set to RUNTIME_ERROR to throw exceptions when your instrumented code attempts to record data when no segment is open.

    Valid Values
    • RUNTIME_ERROR – Throw a runtime exception.

    • LOG_ERROR – Log an error and continue (default).

    • IGNORE_ERROR – Ignore error and continue.

    Errors related to missing segments or subsegments can occur when you attempt to use an instrumented client in startup code that runs when no request is open, or in code that spawns a new thread.

Environment variables override equivalent system properties and values set in code.

System properties

You can use system properties as a JVM-specific alternative to environment variables. The SDK supports the following properties:

  • com.amazonaws.xray.strategy.tracingName – Equivalent to AWS_XRAY_TRACING_NAME.

  • com.amazonaws.xray.emitters.daemonAddress – Equivalent to AWS_XRAY_DAEMON_ADDRESS.

  • com.amazonaws.xray.strategy.contextMissingStrategy – Equivalent to AWS_XRAY_CONTEXT_MISSING.

If both a system property and the equivalent environment variable are set, the environment variable value is used. Either method overrides values set in code.

Tracing incoming requests with the X-Ray SDK for Java

You can use the X-Ray SDK to trace incoming HTTP requests that your application serves on an EC2 instance in Amazon EC2, Amazon Elastic Beanstalk, or Amazon ECS.

Use a Filter to instrument incoming HTTP requests. When you add the X-Ray servlet filter to your application, the X-Ray SDK for Java creates a segment for each sampled request. This segment includes timing, method, and disposition of the HTTP request. Additional instrumentation creates subsegments on this segment.

Note

For Amazon Lambda functions, Lambda creates a segment for each sampled request. See Amazon Lambda and Amazon X-Ray for more information.

Each segment has a name that identifies your application in the service map. The segment can be named statically, or you can configure the SDK to name it dynamically based on the host header in the incoming request. Dynamic naming lets you group traces based on the domain name in the request, and apply a default name if the name doesn't match an expected pattern (for example, if the host header is forged).

Forwarded Requests

If a load balancer or other intermediary forwards a request to your application, X-Ray takes the client IP from the X-Forwarded-For header in the request instead of from the source IP in the IP packet. The client IP that is recorded for a forwarded request can be forged, so it should not be trusted.

When a request is forwarded, the SDK sets an additional field in the segment to indicate this. If the segment contains the field x_forwarded_for set to true, the client IP was taken from the X-Forwarded-For header in the HTTP request.

The message handler creates a segment for each incoming request with an http block that contains the following information:

  • HTTP method – GET, POST, PUT, DELETE, etc.

  • Client address – The IP address of the client that sent the request.

  • Response code – The HTTP response code for the completed request.

  • Timing – The start time (when the request was received) and end time (when the response was sent).

  • User agent — The user-agent from the request.

  • Content length — The content-length from the response.

Adding a tracing filter to your application (Tomcat)

For Tomcat, add a <filter> to your project's web.xml file. Use the fixedName parameter to specify a service name to apply to segments created for incoming requests.

Example WEB-INF/web.xml - Tomcat
<filter> <filter-name>AWSXRayServletFilter</filter-name> <filter-class>com.amazonaws.xray.javax.servlet.AWSXRayServletFilter</filter-class> <init-param> <param-name>fixedName</param-name> <param-value>MyApp</param-value> </init-param> </filter> <filter-mapping> <filter-name>AWSXRayServletFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping>

Adding a tracing filter to your application (spring)

For Spring, add a Filter to your WebConfig class. Pass the segment name to the AWSXRayServletFilter constructor as a string.

Example src/main/java/myapp/WebConfig.java - spring
package myapp; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; import javax.servlet.Filter; import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter; @Configuration public class WebConfig { @Bean public Filter TracingFilter() { return new AWSXRayServletFilter("Scorekeep"); } }

Configuring a segment naming strategy

Amazon X-Ray uses a service name to identify your application and distinguish it from the other applications, databases, external APIs, and Amazon resources that your application uses. When the X-Ray SDK generates segments for incoming requests, it records your application's service name in the segment's name field.

The X-Ray SDK can name segments after the hostname in the HTTP request header. However, this header can be forged, which could result in unexpected nodes in your service map. To prevent the SDK from naming segments incorrectly due to requests with forged host headers, you must specify a default name for incoming requests.

If your application serves requests for multiple domains, you can configure the SDK to use a dynamic naming strategy to reflect this in segment names. A dynamic naming strategy allows the SDK to use the hostname for requests that match an expected pattern, and apply the default name to requests that don't.

For example, you might have a single application serving requests to three subdomains– www.example.com, api.example.com, and static.example.com. You can use a dynamic naming strategy with the pattern *.example.com to identify segments for each subdomain with a different name, resulting in three service nodes on the service map. If your application receives requests with a hostname that doesn't match the pattern, you will see a fourth node on the service map with a fallback name that you specify.

To use the same name for all request segments, specify the name of your application when you initialize the servlet filter, as shown in the previous section. This has the same effect as creating a fixed SegmentNamingStrategy by calling SegmentNamingStrategy.fixed() and passing it to the AWSXRayServletFilter constructor.

Note

You can override the default service name that you define in code with the AWS_XRAY_TRACING_NAME environment variable.

A dynamic naming strategy defines a pattern that hostnames should match, and a default name to use if the hostname in the HTTP request does not match the pattern. To name segments dynamically in Tomcat, use the dynamicNamingRecognizedHosts and dynamicNamingFallbackName to define the pattern and default name, respectively.

Example WEB-INF/web.xml - servlet filter with dynamic naming
<filter> <filter-name>AWSXRayServletFilter</filter-name> <filter-class>com.amazonaws.xray.javax.servlet.AWSXRayServletFilter</filter-class> <init-param> <param-name>dynamicNamingRecognizedHosts</param-name> <param-value>*.example.com</param-value> </init-param> <init-param> <param-name>dynamicNamingFallbackName</param-name> <param-value>MyApp</param-value> </init-param> </filter> <filter-mapping> <filter-name>AWSXRayServletFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping>

For Spring, create a dynamic SegmentNamingStrategy by calling SegmentNamingStrategy.dynamic(), and pass it to the AWSXRayServletFilter constructor.

Example src/main/java/myapp/WebConfig.java - servlet filter with dynamic naming
package myapp; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; import javax.servlet.Filter; import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter; import com.amazonaws.xray.strategy.SegmentNamingStrategy; @Configuration public class WebConfig { @Bean public Filter TracingFilter() { return new AWSXRayServletFilter(SegmentNamingStrategy.dynamic("MyApp", "*.example.com")); } }

Tracing Amazon SDK calls with the X-Ray SDK for Java

When your application makes calls to Amazon Web Services to store data, write to a queue, or send notifications, the X-Ray SDK for Java tracks the calls downstream in subsegments. Traced Amazon Web Services and resources that you access within those services (for example, an Amazon S3 bucket or Amazon SQS queue), appear as downstream nodes on the trace map in the X-Ray console.

The X-Ray SDK for Java automatically instruments all AWS SDK clients when you include the aws-sdk and an aws-sdk-instrumentor submodules in your build. If you don't include the Instrumentor submodule, you can choose to instrument some clients while excluding others.

To instrument individual clients, remove the aws-sdk-instrumentor submodule from your build and add an XRayClient as a TracingHandler on your Amazon SDK client using the service's client builder.

For example, to instrument an AmazonDynamoDB client, pass a tracing handler to AmazonDynamoDBClientBuilder.

Example MyModel.java - DynamoDB client
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.handlers.TracingHandler; ... public class MyModel { private AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() .withRegion(Regions.fromName(System.getenv("AWS_REGION"))) .withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder())) .build(); ...

For all services, you can see the name of the API called in the X-Ray console. For a subset of services, the X-Ray SDK adds information to the segment to provide more granularity in the service map.

For example, when you make a call with an instrumented DynamoDB client, the SDK adds the table name to the segment for calls that target a table. In the console, each table appears as a separate node in the service map, with a generic DynamoDB node for calls that don't target a table.

Example Subsegment for a call to DynamoDB to save an item
{ "id": "24756640c0d0978a", "start_time": 1.480305974194E9, "end_time": 1.4803059742E9, "name": "DynamoDB", "namespace": "aws", "http": { "response": { "content_length": 60, "status": 200 } }, "aws": { "table_name": "scorekeep-user", "operation": "UpdateItem", "request_id": "UBQNSO5AEM8T4FDA4RQDEB94OVTDRVV4K4HIRGVJF66Q9ASUAAJG", } }

When you access named resources, calls to the following services create additional nodes in the service map. Calls that don't target specific resources create a generic node for the service.

  • Amazon DynamoDB – Table name

  • Amazon Simple Storage Service – Bucket and key name

  • Amazon Simple Queue Service – Queue name

To instrument downstream calls to Amazon Web Services with Amazon SDK for Java 2.2 and later, you can omit the aws-xray-recorder-sdk-aws-sdk-v2-instrumentor module from your build configuration. Include the aws-xray-recorder-sdk-aws-sdk-v2 module instead, then instrument individual clients by configuring them with a TracingInterceptor.

Example Amazon SDK for Java 2.2 and later - tracing interceptor
import com.amazonaws.xray.interceptors.TracingInterceptor; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration import software.amazon.awssdk.services.dynamodb.DynamoDbClient; //... public class MyModel { private DynamoDbClient client = DynamoDbClient.builder() .region(Region.US_WEST_2) .overrideConfiguration(ClientOverrideConfiguration.builder() .addExecutionInterceptor(new TracingInterceptor()) .build() ) .build(); //...

Tracing calls to downstream HTTP web services with the X-Ray SDK for Java

When your application makes calls to microservices or public HTTP APIs, you can use the X-Ray SDK for Java's version of HttpClient to instrument those calls and add the API to the service graph as a downstream service.

The X-Ray SDK for Java includes DefaultHttpClient and HttpClientBuilder classes that can be used in place of the Apache HttpComponents equivalents to instrument outgoing HTTP calls.

  • com.amazonaws.xray.proxies.apache.http.DefaultHttpClient - org.apache.http.impl.client.DefaultHttpClient

  • com.amazonaws.xray.proxies.apache.http.HttpClientBuilder - org.apache.http.impl.client.HttpClientBuilder

These libraries are in the aws-xray-recorder-sdk-apache-http submodule.

You can replace your existing import statements with the X-Ray equivalent to instrument all clients, or use the fully qualified name when you initialize a client to instrument specific clients.

Example HttpClientBuilder
import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import com.amazonaws.xray.proxies.apache.http.HttpClientBuilder; ... public String randomName() throws IOException { CloseableHttpClient httpclient = HttpClientBuilder.create().build(); HttpGet httpGet = new HttpGet("http://names.example.com/api/"); CloseableHttpResponse response = httpclient.execute(httpGet); try { HttpEntity entity = response.getEntity(); InputStream inputStream = entity.getContent(); ObjectMapper mapper = new ObjectMapper(); Map<String, String> jsonMap = mapper.readValue(inputStream, Map.class); String name = jsonMap.get("name"); EntityUtils.consume(entity); return name; } finally { response.close(); } }

When you instrument a call to a downstream web api, the X-Ray SDK for Java records a subsegment with information about the HTTP request and response. X-Ray uses the subsegment to generate an inferred segment for the remote API.

Example Subsegment for a downstream HTTP call
{ "id": "004f72be19cddc2a", "start_time": 1484786387.131, "end_time": 1484786387.501, "name": "names.example.com", "namespace": "remote", "http": { "request": { "method": "GET", "url": "https://names.example.com/" }, "response": { "content_length": -1, "status": 200 } } }
Example Inferred segment for a downstream HTTP call
{ "id": "168416dc2ea97781", "name": "names.example.com", "trace_id": "1-62be1272-1b71c4274f39f122afa64eab", "start_time": 1484786387.131, "end_time": 1484786387.501, "parent_id": "004f72be19cddc2a", "http": { "request": { "method": "GET", "url": "https://names.example.com/" }, "response": { "content_length": -1, "status": 200 } }, "inferred": true }

Tracing SQL queries with the X-Ray SDK for Java

SQL Interceptors

Instrument SQL database queries by adding the X-Ray SDK for Java JDBC interceptor to your data source configuration.

  • PostgreSQLcom.amazonaws.xray.sql.postgres.TracingInterceptor

  • MySQLcom.amazonaws.xray.sql.mysql.TracingInterceptor

These interceptors are in the aws-xray-recorder-sql-postgres and aws-xray-recorder-sql-mysql submodules, respectively. They implement org.apache.tomcat.jdbc.pool.JdbcInterceptor and are compatible with Tomcat connection pools.

Note

SQL interceptors do not record the SQL query itself within subsegments for security purposes.

For Spring, add the interceptor in a properties file and build the data source with Spring Boot's DataSourceBuilder.

Example src/main/java/resources/application.properties - PostgreSQL JDBC interceptor
spring.datasource.continue-on-error=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=create-drop spring.datasource.jdbc-interceptors=com.amazonaws.xray.sql.postgres.TracingInterceptor spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL94Dialect
Example src/main/java/myapp/WebConfig.java - Data source
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import javax.servlet.Filter; import javax.sql.DataSource; import java.net.URL; @Configuration @EnableAutoConfiguration @EnableJpaRepositories("myapp") public class RdsWebConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { logger.info("Initializing PostgreSQL datasource"); return DataSourceBuilder.create() .driverClassName("org.postgresql.Driver") .url("jdbc:postgresql://" + System.getenv("RDS_HOSTNAME") + ":" + System.getenv("RDS_PORT") + "/ebdb") .username(System.getenv("RDS_USERNAME")) .password(System.getenv("RDS_PASSWORD")) .build(); } ... }

For Tomcat, call setJdbcInterceptors on the JDBC data source with a reference to the X-Ray SDK for Java class.

Example src/main/myapp/model.java - Data source
import org.apache.tomcat.jdbc.pool.DataSource; ... DataSource source = new DataSource(); source.setUrl(url); source.setUsername(user); source.setPassword(password); source.setDriverClassName("com.mysql.jdbc.Driver"); source.setJdbcInterceptors("com.amazonaws.xray.sql.mysql.TracingInterceptor;");

The Tomcat JDBC Data Source library is included in the X-Ray SDK for Java, but you can declare it as a provided dependency to document that you use it.

Example pom.xml - JDBC data source
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> <version>8.0.36</version> <scope>provided</scope> </dependency>

Native SQL Tracing Decorator

  • Add aws-xray-recorder-sdk-sql to your dependencies.

  • Decorate your database datasource, connection, or statement.

    dataSource = TracingDataSource.decorate(dataSource) connection = TracingConnection.decorate(connection) statement = TracingStatement.decorateStatement(statement) preparedStatement = TracingStatement.decoratePreparedStatement(preparedStatement, sql) callableStatement = TracingStatement.decorateCallableStatement(callableStatement, sql)

Generating custom subsegments with the X-Ray SDK for Java

Subsegments extend a trace's segment with details about work done in order to serve a request. Each time you make a call with an instrumented client, the X-Ray SDK records the information generated in a subsegment. You can create additional subsegments to group other subsegments, to measure the performance of a section of code, or to record annotations and metadata.

To manage subsegments, use the beginSubsegment and endSubsegment methods.

Example GameModel.java - custom subsegment
import com.amazonaws.xray.AWSXRay; ... public void saveGame(Game game) throws SessionNotFoundException { // wrap in subsegment Subsegment subsegment = AWSXRay.beginSubsegment("Save Game"); try { // check session String sessionId = game.getSession(); if (sessionModel.loadSession(sessionId) == null ) { throw new SessionNotFoundException(sessionId); } mapper.save(game); } catch (Exception e) { subsegment.addException(e); throw e; } finally { AWSXRay.endSubsegment(); } }

In this example, the code within the subsegment loads the game's session from DynamoDB with a method on the session model, and uses the Amazon SDK for Java's DynamoDB mapper to save the game. Wrapping this code in a subsegment makes the calls DynamoDB children of the Save Game subsegment in the trace view in the console.

If the code in your subsegment throws checked exceptions, wrap it in a try block and call AWSXRay.endSubsegment() in a finally block to ensure that the subsegment is always closed. If a subsegment is not closed, the parent segment cannot be completed and won't be sent to X-Ray.

For code that doesn't throw checked exceptions, you can pass the code to AWSXRay.CreateSubsegment as a Lambda function.

Example Subsegment Lambda function
import com.amazonaws.xray.AWSXRay; AWSXRay.createSubsegment("getMovies", (subsegment) -> { // function code });

When you create a subsegment within a segment or another subsegment, the X-Ray SDK for Java generates an ID for it and records the start time and end time.

Example Subsegment with metadata
"subsegments": [{ "id": "6f1605cd8a07cb70", "start_time": 1.480305974194E9, "end_time": 1.4803059742E9, "name": "Custom subsegment for UserModel.saveUser function", "metadata": { "debug": { "test": "Metadata string from UserModel.saveUser" } },

For asynchronous and multi-threaded programming, you must manually pass the subsegment to the endSubsegment() method to ensure it is closed correctly because the X-Ray context may be modified during async execution. If an asynchronous subsegment is closed after its parent segment is closed, this method will automatically stream the entire segment to the X-Ray daemon.

Example Asynchronous Subsegment
@GetMapping("/api") public ResponseEntity<?> api() { CompletableFuture.runAsync(() -> { Subsegment subsegment = AWSXRay.beginSubsegment("Async Work"); try { Thread.sleep(3000); } catch (InterruptedException e) { subsegment.addException(e); throw e; } finally { AWSXRay.endSubsegment(subsegment); } }); return ResponseEntity.ok().build(); }

Add annotations and metadata to segments with the X-Ray SDK for Java

You can use annotations and metadata to record additional information about requests, the environment, or your application. You can add annotations and metadata to the segments that the X-Ray SDK creates, or to custom subsegments that you create.

Annotations are key-value pairs with string, number, or Boolean values. Annotations are indexed for use with filter expressions. Use annotations to record data that you want to use to group traces in the console, or when calling the GetTraceSummaries API.

Metadata are key-value pairs that can have values of any type, including objects and lists, but are not indexed for use with filter expressions. Use metadata to record additional data that you want stored in the trace but don't need to use with search.

In addition to annotations and metadata, you can also record user ID strings on segments. User IDs are recorded in a separate field on segments and are indexed for use with search.

Recording annotations with the X-Ray SDK for Java

Use annotations to record information on segments or subsegments that you want indexed for search.

Annotation Requirements
  • Keys – The key for an X-Ray annotation can have up to 500 alphanumeric characters. You cannot use spaces or symbols other than the underscore symbol (_).

  • Values – The value for an X-Ray annotation can have up to 1,000 Unicode characters.

  • The number of Annotations – You can use up to 50 annotations per trace.

To record annotations
  1. Get a reference to the current segment or subsegment from AWSXRay.

    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Segment; ... Segment document = AWSXRay.getCurrentSegment();

    or

    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Subsegment; ... Subsegment document = AWSXRay.getCurrentSubsegment();
  2. Call putAnnotation with a String key, and a Boolean, Number, or String value.

    document.putAnnotation("mykey", "my value");

The SDK records annotations as key-value pairs in an annotations object in the segment document. Calling putAnnotation twice with the same key overwrites previously recorded values on the same segment or subsegment.

To find traces that have annotations with specific values, use the annotations.key keyword in a filter expression.

Example src/main/java/scorekeep/GameModel.java – Annotations and metadata
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Segment; import com.amazonaws.xray.entities.Subsegment; ... public void saveGame(Game game) throws SessionNotFoundException { // wrap in subsegment Subsegment subsegment = AWSXRay.beginSubsegment("## GameModel.saveGame"); try { // check session String sessionId = game.getSession(); if (sessionModel.loadSession(sessionId) == null ) { throw new SessionNotFoundException(sessionId); } Segment segment = AWSXRay.getCurrentSegment(); subsegment.putMetadata("resources", "game", game); segment.putAnnotation("gameid", game.getId()); mapper.save(game); } catch (Exception e) { subsegment.addException(e); throw e; } finally { AWSXRay.endSubsegment(); } }

Recording metadata with the X-Ray SDK for Java

Use metadata to record information on segments or subsegments that you don't need indexed for search. Metadata values can be strings, numbers, Booleans, or any object that can be serialized into a JSON object or array.

To record metadata
  1. Get a reference to the current segment or subsegment from AWSXRay.

    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Segment; ... Segment document = AWSXRay.getCurrentSegment();

    or

    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Subsegment; ... Subsegment document = AWSXRay.getCurrentSubsegment();
  2. Call putMetadata with a String namespace, String key, and a Boolean, Number, String, or Object value.

    document.putMetadata("my namespace", "my key", "my value");

    or

    Call putMetadata with just a key and value.

    document.putMetadata("my key", "my value");

If you don't specify a namespace, the SDK uses default. Calling putMetadata twice with the same key overwrites previously recorded values on the same segment or subsegment.

Example src/main/java/scorekeep/GameModel.java – Annotations and metadata
import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Segment; import com.amazonaws.xray.entities.Subsegment; ... public void saveGame(Game game) throws SessionNotFoundException { // wrap in subsegment Subsegment subsegment = AWSXRay.beginSubsegment("## GameModel.saveGame"); try { // check session String sessionId = game.getSession(); if (sessionModel.loadSession(sessionId) == null ) { throw new SessionNotFoundException(sessionId); } Segment segment = AWSXRay.getCurrentSegment(); subsegment.putMetadata("resources", "game", game); segment.putAnnotation("gameid", game.getId()); mapper.save(game); } catch (Exception e) { subsegment.addException(e); throw e; } finally { AWSXRay.endSubsegment(); } }

Recording user IDs with the X-Ray SDK for Java

Record user IDs on request segments to identify the user who sent the request.

To record user IDs
  1. Get a reference to the current segment from AWSXRay.

    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Segment; ... Segment document = AWSXRay.getCurrentSegment();
  2. Call setUser with a string ID of the user who sent the request.

    document.setUser("U12345");

You can call setUser in your controllers to record the user ID as soon as your application starts processing a request. If you will only use the segment to set the user ID, you can chain the calls in a single line.

Example src/main/java/scorekeep/MoveController.java – User ID
import com.amazonaws.xray.AWSXRay; ... @RequestMapping(value="/{userId}", method=RequestMethod.POST) public Move newMove(@PathVariable String sessionId, @PathVariable String gameId, @PathVariable String userId, @RequestBody String move) throws SessionNotFoundException, GameNotFoundException, StateNotFoundException, RulesException { AWSXRay.getCurrentSegment().setUser(userId); return moveFactory.newMove(sessionId, gameId, userId, move); }

To find traces for a user ID, use the user keyword in a filter expression.

Amazon X-Ray metrics for the X-Ray SDK for Java

This topic describes the Amazon X-Ray namespace, metrics, and dimensions. You can use the X-Ray SDK for Java to publish unsampled Amazon CloudWatch metrics from your collected X-Ray segments. These metrics are derived from the segment’s start and end time, and the error, fault, and throttled status flags. Use these trace metrics to expose retries and dependency issues within subsegments.

CloudWatch is essentially a metrics repository. A metric is the fundamental concept in CloudWatch and represents a time-ordered set of data points. You (or Amazon Web Services) publish metrics data points into CloudWatch and you retrieve statistics about those data points as an ordered set of time-series data.

Metrics are uniquely defined by a name, a namespace, and one or more dimensions. Each data point has a timestamp and, optionally, a unit of measure. When you request statistics, the returned data stream is identified by namespace, metric name, and dimension.

For more information about CloudWatch, see the Amazon CloudWatch User Guide.

X-Ray CloudWatch metrics

The ServiceMetrics/SDK namespace includes the following metrics.

Metric Statistics available Description Units

Latency

Average, Minimum, Maximum, Count

The difference between the start and end time. Average, minimum, and maximum all describe operational latency. Count describes call count.

Milliseconds

ErrorRate

Average, Sum

The rate of requests that failed with a 4xx Client Error status code, resulting in an error.

Percent

FaultRate

Average, Sum

The rate of traces that failed with a 5xx Server Error status code, resulting in a fault.

Percent

ThrottleRate

Average, Sum

The rate of throttled traces that return a 429 status code. This is a subset of the ErrorRate metric.

Percent

OkRate

Average, Sum

The rate of traced requests resulting in an OK status code.

Percent

X-Ray CloudWatch dimensions

Use the dimensions in the following table to refine the metrics returned for your X-Ray instrumented Java applications.

Dimension Description

ServiceType

The type of the service, for example, AWS::EC2::Instance or NONE, if not known.

ServiceName

The canonical name for the service.

Enable X-Ray CloudWatch metrics

Use the following procedure to enable trace metrics in your instrumented Java application.

To configure trace metrics
  1. Add the aws-xray-recorder-sdk-metrics package as a Maven dependency. For more information, see X-Ray SDK for Java Submodules.

  2. Enable a new MetricsSegmentListener() as part of the global recorder build.

    Example src/com/myapp/web/Startup.java
    import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.AWSXRayRecorderBuilder; import com.amazonaws.xray.plugins.EC2Plugin; import com.amazonaws.xray.plugins.ElasticBeanstalkPlugin; import com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy; @Configuration public class WebConfig { ... static { AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder .standard() .withPlugin(new EC2Plugin()) .withPlugin(new ElasticBeanstalkPlugin()) .withSegmentListener(new MetricsSegmentListener()); URL ruleFile = WebConfig.class.getResource("/sampling-rules.json"); builder.withSamplingStrategy(new LocalizedSamplingStrategy(ruleFile)); AWSXRay.setGlobalRecorder(builder.build()); } }
  3. Deploy the CloudWatch agent to collect metrics using Amazon Elastic Compute Cloud (Amazon EC2), Amazon Elastic Container Service (Amazon ECS), or Amazon Elastic Kubernetes Service (Amazon EKS):

  4. Configure the SDK to communicate with the CloudWatch agent. By default, the SDK communicates with the CloudWatch agent on the address 127.0.0.1. You can configure alternate addresses by setting the environment variable or Java property to address:port.

    Example Environment variable
    AWS_XRAY_METRICS_DAEMON_ADDRESS=address:port
    Example Java property
    com.amazonaws.xray.metrics.daemonAddress=address:port
To validate configuration
  1. Sign in to the Amazon Web Services Management Console and open the CloudWatch console at https://console.amazonaws.cn/cloudwatch/.

  2. Open the Metrics tab to observe the influx of your metrics.

  3. (Optional) In the CloudWatch console, on the Logs tab, open the ServiceMetricsSDK log group. Look for a log stream that matches the host metrics, and confirm the log messages.

Passing segment context between threads in a multithreaded application

When you create a new thread in your application, the AWSXRayRecorder doesn't maintain a reference to the current segment or subsegment Entity. If you use an instrumented client in the new thread, the SDK tries to write to a segment that doesn't exist, causing a SegmentNotFoundException.

To avoid throwing exceptions during development, you can configure the recorder with a ContextMissingStrategy that tells it to log an error instead. You can configure the strategy in code with SetContextMissingStrategy, or configure equivalent options with an environment variable or system property.

One way to address the error is to use a new segment by calling beginSegment when you start the thread and endSegment when you close it. This works if you are instrumenting code that doesn't run in response to an HTTP request, like code that runs when your application starts.

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. Once the segment is available in the new thread, you can execute any runnable with access to that segment's context using the segment.run(() -> { ... }) method.

See Using instrumented clients in worker threads for an example.

Using X-Ray with Asynchronous Programming

The X-Ray SDK for Java can be used in asynchronous Java programs with SegmentContextExecutors. The SegmentContextExecutor implements the Executor interface, which means it can be passed into all asynchronous operations of a CompletableFuture. This ensures that any asynchronous operations will be executed with the correct segment in its context.

Example App.java: Passing SegmentContextExecutor to CompletableFuture
DynamoDbAsyncClient client = DynamoDbAsyncClient.create(); AWSXRay.beginSegment(); // ... client.getItem(request).thenComposeAsync(response -> { // If we did not provide the segment context executor, this request would not be traced correctly. return client.getItem(request2); }, SegmentContextExecutors.newSegmentContextExecutor());

AOP with Spring and the X-Ray SDK for Java

This topic describes how to use the X-Ray SDK and the Spring Framework to instrument your application without changing its core logic. This means that there is now a non-invasive way to instrument your applications running remotely in Amazon.

Configuring Spring

You can use Maven or Gradle to configure Spring to use AOP to instrument your application.

If you use Maven to build your application, add the following dependency in your pom.xml file.

<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-xray-recorder-sdk-spring</artifactId> <version>2.11.0</version> </dependency>

For Gradle, add the following dependency in your build.gradle file.

compile 'com.amazonaws:aws-xray-recorder-sdk-spring:2.11.0'

Configuring Spring Boot

In addition to the Spring dependency described in the previous section, if you’re using Spring Boot, add the following dependency if it’s not already on your classpath.

Maven:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.5.2</version> </dependency>

Gradle:

compile 'org.springframework.boot:spring-boot-starter-aop:2.5.2'

Adding a tracing filter to your application

Add a Filter to your WebConfig class. Pass the segment name to the AWSXRayServletFilter constructor as a string. For more information about tracing filters and instrumenting incoming requests, see Tracing incoming requests with the X-Ray SDK for Java.

Example src/main/java/myapp/WebConfig.java - spring
package myapp; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; import javax.servlet.Filter; import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter; @Configuration public class WebConfig { @Bean public Filter TracingFilter() { return new AWSXRayServletFilter("Scorekeep"); } }

Jakarta Support

Spring 6 uses Jakarta instead of Javax for its Enterprise Edition. To support this new namespace, X-Ray has created a parallel set of classes that live in their own Jakarta namespace.

For the filter classes, replace javax with jakarta. When configuring a segment naming strategy, add jakarta before the naming strategy class name, as in the following example:

package myapp; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; import jakarta.servlet.Filter; import com.amazonaws.xray.jakarta.servlet.AWSXRayServletFilter; import com.amazonaws.xray.strategy.jakarta.SegmentNamingStrategy; @Configuration public class WebConfig { @Bean public Filter TracingFilter() { return new AWSXRayServletFilter(SegmentNamingStrategy.dynamic("Scorekeep")); } }

Annotating your code or implementing an interface

Your classes must either be annotated with the @XRayEnabled annotation, or implement the XRayTraced interface. This tells the AOP system to wrap the functions of the affected class for X-Ray instrumentation.

Activating X-Ray in your application

To activate X-Ray tracing in your application, your code must extend the abstract class BaseAbstractXRayInterceptor by overriding the following methods.

  • generateMetadata—This function allows customization of the metadata attached to the current function’s trace. By default, the class name of the executing function is recorded in the metadata. You can add more data if you need additional information.

  • xrayEnabledClasses—This function is empty, and should remain so. It serves as the host for a pointcut instructing the interceptor about which methods to wrap. Define the pointcut by specifying which of the classes that are annotated with @XRayEnabled to trace. The following pointcut statement tells the interceptor to wrap all controller beans annotated with the @XRayEnabled annotation.

    @Pointcut(“@within(com.amazonaws.xray.spring.aop.XRayEnabled) && bean(*Controller)”)

If your project is using Spring Data JPA, consider extending from AbstractXRayInterceptor instead of BaseAbstractXRayInterceptor.

Example

The following code extends the abstract class BaseAbstractXRayInterceptor.

@Aspect @Component public class XRayInspector extends BaseAbstractXRayInterceptor { @Override protected Map<String, Map<String, Object>> generateMetadata(ProceedingJoinPoint proceedingJoinPoint, Subsegment subsegment) throws Exception { return super.generateMetadata(proceedingJoinPoint, subsegment); } @Override @Pointcut("@within(com.amazonaws.xray.spring.aop.XRayEnabled) && bean(*Controller)") public void xrayEnabledClasses() {} }

The following code is a class that will be instrumented by X-Ray.

@Service @XRayEnabled public class MyServiceImpl implements MyService { private final MyEntityRepository myEntityRepository; @Autowired public MyServiceImpl(MyEntityRepository myEntityRepository) { this.myEntityRepository = myEntityRepository; } @Transactional(readOnly = true) public List<MyEntity> getMyEntities(){ try(Stream<MyEntity> entityStream = this.myEntityRepository.streamAll()){ return entityStream.sorted().collect(Collectors.toList()); } } }

If you've configured your application correctly, you should see the complete call stack of the application, from the controller down through the service calls, as shown in the following screen shot of the console.

The complete call stack.