AWS DevOps Blog

Aspect-Oriented Programming for AWS X-Ray Using Spring

This post was written by Andy Powell, Partner Solutions Architect.

For developers, tracing and instrumenting code is one of the most valuable tools when debugging code. When you are developing locally, you can use local debugging and profiling tools, but when you deploy an application to the cloud, the task is more challenging. In this blog post, we will look at a new way to instrument your application using AWS X-Ray without adding tracing code to your business logic.

AWS X-Ray

Released to the public earlier this year, AWS X-Ray provides a mechanism for developers to instrument and trace their code while running on AWS. AWS X-Ray enables developers to analyze and debug distributed applications through the entire AWS stack. Developers and operations personnel can also follow the flow of a request through the entire AWS infrastructure. X-Ray works well in a monolithic or microservices model. Either way, developers can get a complete view of their application’s performance and behavior.

X-Ray provides two key mechanisms for analyzing an application:

  • The service map.
  • The trace view.

The service map, shown here, provides a high-level view of the services consumed by an application and their relative health:

Application developers often look for a more detailed view of their application to help them answer questions like “Which functions are my bottlenecks?” and “Where is the most latency in the application?” The trace view allows you to see the flow of an application through service calls.  It shows you latency between services and the exact execution time of each X-Ray segment.

Similar to other logging and metrics frameworks, X-Ray requires the developer to insert specific code throughout the application. This might cause issues because the logging and metrics code has the potential to increase the complexity of the application code. Increased complexity leads, in turn, to increased maintenance and testing costs. Ideally, developers should add X-Ray tracing to an application in a non-invasive manner, one that does not affect the underlying business logic.

Aspect-oriented programming and the Spring Framework

Aspect-oriented programming (AOP) is a mechanism by which code runs either before, after, or around a target function. The code that runs outside the target code is called an aspect. An aspect provides the ability to perform actions like logging, transaction management, and method retries. The goal is for the aspect to provide these capabilities without affecting the target code. Pointcuts define where these aspects should act in the code. AOP allows developers to leverage powerful functionality without affecting their business logic.

An aspect-oriented approach is a perfect way to implement AWS X-Ray because it keeps the underlying code clean and provides non-invasive, reusable tracing logic.

Simply creating an aspect to invoke X-Ray is not enough. We also have to create pointcuts that tell the aspect where to act. These pointcuts will define which methods we wrap with tracing logic. After they are defined, the aspect sends the entire call stack to X-Ray for visualization.

The Spring Framework is a common application framework for the development of Java software. It provides an extensive programming and config method for modern Java applications.

Spring provides facilities for a range of application functions, including web applications, messaging applications, and streaming data applications. Spring applications are capable of being cloud-native from the onset.

AOP is one of the core components of the Spring Framework. Spring’s implementation of AOP “weaves” application code at runtime. Load-time weaving is also available, but it requires extra configuration at compile or application runtime to work properly. The Spring runtime weaving does not require any special compilation or agents.

AWS X-Ray Spring extensions

Starting with version 1.3.0, the AWS X-Ray SDK lets you use AOP in the Spring Framework to instrument code with no change to the application’s business logic. This means that there is now a non-invasive way to instrument your applications running remotely in AWS.

To include the extension in the code, first add the dependency to the application. If you are using Maven, you add the dependency this way:

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

If you are using Gradle, use the following syntax:

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

After you’ve included this in your application, there are a couple of steps that need configuration before tracing is enabled. First, 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.

Second, you need an interceptor to actually wrap the code. This involves extending an abstract class, AbstractXRayInterceptor, to activate X-Ray tracing in the application. The AbstractXRayInterceptor contains methods that must be overridden:

  • 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 insights.
  • 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.   The developer should define the pointcut.   You can specify which classes that are annotated with XRayEnabled you want traced.  A pointcut statement of  @Pointcut(“@within(com.amazonaws.xray.spring.aop.XRayEnabled) && bean(*Controller)”) tells the interceptor to wrap all controller beans annotated with the @XRayEnabled annotation.

Here is a sample implementation of the AbstractXRayInterceptor :

@Aspect
@Component
public class XRayInspector extends AbstractXRayInterceptor {    
    @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() {}
  
}

Here is an example of a Service 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());        
        }    
    }
}

By default, the AbstractXRayInterceptor instruments around all Spring data repository instances.

After it’s configured, the Spring application runs as normal. The X-Ray interceptor  picks up annotated classes automatically and builds a trace of the call stack. You can view this call stack in the Traces section of the X-Ray console.

Looking at the trace, you will see the complete call stack of the application, from the controller down through the service calls. Stack traces are arranged in a hierarchy in the same way as typical X-Ray traces. Any exceptions that occur in the call stack are added to the trace by the interceptor automatically. This gives you a complete view of the application’s functionality, performance, and error states.  X-Ray provides the convenience of a managed service of the tracing and reporting engine.  Without it, the developer would have to manage the tracing and reporting infrastructure.

Conclusion

On its own, X-Ray provides powerful functionality to trace your applications running on AWS. When you combine the service with the ease of use of AOP and the Spring Framework, it is a natural fit.

The demo app code is available for download. Download it today and start integrating deep tracing into your Spring applications.

Note

AWS X-Ray SDK for Java v1.3.0 will be available on maven central in the next few days. In the meantime, you can follow the below steps to use the latest version from the GitHub repo:

1. Clone the AWS X-Ray SDK for Java repo locally.
2. In the project root, run “mvn clean install -Dgpg.skip=true”.
3. Reference version 1.3.0 in your maven / gradle files as mentioned in the blog post.