• Guide Content

OpenTelemetry Java Agent: Easy Auto-Instrumentation for Java Apps

What Is OpenTelemetry Java Agent? 

OpenTelemetry is an open-source project managed by the Cloud Native Computing Foundation (CNCF), which provides a unified set of APIs to collect telemetry data from software systems. The OpenTelemetry Java Agent is an automatic instrumentation library which allows trace data to be collected from a Java application and sent to an observability backend.

The agent works by injecting byte code into classes at load time, enabling it to intercept method calls and extract trace data. The main advantage of the OpenTelemetry Java Agent is that it requires no code changes to your application. This means you can monitor your application’s performance without altering its codebase.

The OpenTelemetry Java Agent is based on the OpenTelemetry Java SDK. Essentially, it’s a pre-packaged implementation of the SDK, which lets you add instrumentation to your applications easily. You can still use the Java SDK directly if you need a different implementation or want to instrument your applications for specific metrics manually.

How to Use the OpenTelemetry Java Agent? 

Download and Distribute the Latest JAR

The first step to using the OpenTelemetry Java Agent is to download the latest JAR file for the Java Agent. The agent is available for download from the official GitHub repository. After downloading the agent, distribute it across your application servers. This distribution can be achieved through various means, including file transfer, containerization, or using a configuration management tool.

Prepare Environment Variables

After distributing the Java JAR agent, you need to prepare the environment variables. These variables determine how the agent operates and how your observability platform will categorize the trace data.

You should be familiar with the following environment variables:

  • OTEL_TRACES_EXPORTER: Specifies the exporter to be used for sending traces. Common values include otlp (for the OTLP exporter), jaeger, zipkin, or logging for debugging purposes.
  • OTEL_EXPORTER_OTLP_ENDPOINT: Defines the endpoint for the OTLP exporter. It’s the destination where the trace data will be sent, typically the URL of your observability platform.
  • OTEL_RESOURCE_ATTRIBUTES: Used to specify resource attributes like service name, environment, and version. These attributes are important for filtering and querying trace data in your observability tool.

Here is an example of a possible setting for the environment variables:

export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_RESOURCE_ATTRIBUTES=service.name=MyService,service.version=1.2.3,deployment.environment=production

Attach the Agent to the JAR of Your Application

The final step in using the OpenTelemetry Java Agent is to attach it to your application’s jar while running it. This attachment uses the -javaagent command-line option when starting your Java application. By attaching the agent to your application, it can start collecting and sending trace data from your application to your observability platform.

Using Instrumentation Annotations with a Java Agent  

Dependencies

Before we proceed, we must set up our project with the right dependencies. To include the Java Agent in your project, you can add the following line to your build.gradle or pom.xml file:

dependencies {
    implementation 'io.opentelemetry:opentelemetry-api:1.0.0'
    implementation 'io.opentelemetry:opentelemetry-sdk:1.0.0'
    implementation 'io.opentelemetry:opentelemetry-exporter-logging:1.0.0'
}

Make sure you are using the latest version of the OpenTelemetry Java Agent. You can always check for the latest version on the OpenTelemetry GitHub repository. Once you’ve added the necessary dependencies, you can import and use the OpenTelemetry classes in your project.

Creating Spans Around Methods with @WithSpan

The @WithSpan annotation creates a new span around the annotated method. A span represents a specific operation within a trace, like a function call or operation on a database. By wrapping our methods with spans, we can trace the execution of our code and understand where bottlenecks or errors might be coming from.

@WithSpan
public void processOrder(Order order) {
    // Processing logic
}

when the method is entered and ends when the method is exited. This allows us to track how long the method is running and can help pinpoint performance issues.

Adding Attributes to the Span with @SpanAttribute

The @SpanAttribute annotation allows us to add attributes to the span. Attributes are key-value pairs that can add additional metadata to the span. This can be useful for adding contextual information to the span like the id of the order being processed or the user that initiated the request.

@WithSpan
public void processOrder(@SpanAttribute("order_id") String orderId, Order order) {
    // Processing logic
}

In the above code, we add an attribute order_id to the span. This attribute can then be used in our tracing backend to filter or group spans.

Suppressing @WithSpan Instrumentation

There may be situations where we want to suppress the @WithSpan instrumentation for specific methods. This can be done using the system property: 

otel.instrumentation.opentelemetry-instrumentation-annotations.exclude-methods

Or the environment variable: 

OTEL_INSTRUMENTATION_OPENTELEMETRY_INSTRUMENTATION_ANNOTATIONS_EXCLUDE_METHODS

This can be useful for methods that are irrelevant for tracing or when you want to manually manage the span.

Creating Spans Around Methods

Creating spans around methods can provide valuable insights into the behavior of our applications. It allows us to see how long methods are taking to execute, how often they are called, and whether they are causing any errors.

@WithSpan
public void processOrder(Order order) {
    // Processing logic
}

In the above code, a span is created around the processOrder method. This span will capture the time taken by the method and any exceptions that are thrown. By creating spans around our methods, we can build a detailed picture of how our application is performing and where any issues might be coming from.

Microservices Monitoring with Lumigo

Lumigo is cloud native observability tool that provides automated distributed tracing of microservice applications and supports OpenTelemetry for reporting of tracing data and resources. With Lumigo, users can:

  • See the end-to-end path of a transaction and full system map of applications
  • Monitor and debug open-source libraries like ExpressJS and Python Flask, and managed services like Amazon DynamoDB, Twilio, Stripe
  • Go from alert to root cause analysis in one click with no code changes
  • Understand system behavior and explore performance and cost issues 
  • Group services into business contexts

Get started with a free trial of Lumigo for your microservice applications