Dec 4th, ‘23/7 min read

Instrumenting Java Apps with OpenTelemetry: Tutorial & Best Practices

A comprehensive guide to instrument Java applications using OpenTelemetry libraries

Share:
Instrumenting Java Apps with OpenTelemetry: Tutorial & Best Practices

Regardless of the framework and language, Observability is king, and monitoring is the frontline of your Observability. OpenTelemetry is an observability framework that is usable in many circumstances. This article explores how OpenTelemetry empowers you to instrument your Java applications for Observability and provides a step-by-step guide.

New to the OpenTelemetry world, read here on What is OpenTelemetry?

Background

OpenTelemetry provides instrumentation SDK, API, libraries, and exporters. There are three OpenTelemetry Java-instrumentation repositories:

You can instrument your Java Application through the OpenTelemetry Java Agent. The OpenTelemetry Java agent is a component of the opentelemetry-java-instrumentation repository. The library provides options for OpenTelemetry Java agent configuration.

Another framework similar to OpenTelemetry for Java is the Java Management Extensions (JMX). But as OpenTelemetry is platform-agnostic, JMX is Java-specific and is encrusted into the Java Virtual Machine (JVM) and Java applications.

The OpenTelemetry Java agent is a runtime instrumentation tool that enables you to instrument your Java applications automatically without modifying the application's source code. It injects bytecode transformations into the Java bytecode at runtime and provides all OpenTelemetry features like distributed tracing and metrics collection.

Getting Started

To instrument your Java application with OpenTelemetry in such a way that traces, metrics, and logs are emitted to a backend for monitoring, you must install or configure the following: 

Decide on the backend or monitoring system where you want to export your telemetry data. Although OpenTelemetry provides exporters for various systems, you must configure them accordingly.

Auto Instrumentation

Use the OpenTelemetry Java Agent JAR alongside the corresponding modules for your frameworks/libraries. These modules generally start with opentelemetry-javaagent-instrumentation which injects bytecode (in your codebase) to capture telemetry at various points in your application or service. 

To automatically instrument a Java application with OpenTelemetry using Docker, you can utilize the opentelemetry-javaagent.

1. Update your Dockerfile

Ensure that you have a Dockerfile for your Java application. Add the following lines to your Dockerfile; it will download the OpenTelemetry Java Agent JAR file and set it as the application's Java agent. This JAR file contains the necessary agent and instrumentation libraries.

ARG OTEL_VERSION=1.7.0

# Download and configure OpenTelemetry Java Agent
RUN wget "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_VERSION}/opentelemetry-javaagent-all.jar" -O /opentelemetry-javaagent.jar

# Set the OpenTelemetry Java Agent as the application's agent
ENV JAVA_TOOL_OPTIONS="-javaagent:/opentelemetry-javaagent.jar"

2. Build and Run your Container

Build your Docker image using the updated Dockerfile. Run the container, and the Java application will automatically be instrumented with OpenTelemetry. The telemetry data will then be sent to your configured exporters. To achieve that, add the following configuration to your machine startup arguments and launch your application:

docker run -e JAVA_TOOL_OPTIONS="-javaagent:/path/to/opentelemetry-javaagent.jar -Dotel.service.name=your-service-name" -v /path/to/myapp.jar:/myapp.jar openjdk:8 java -jar /myapp.jar

Replace /path/to/opentelemetry-javaagent.jar with the actual path to the Java agent JAR file. 

Replace your-service-name with the desired name for your service.

Replace /path/to/myapp.jar with the actual path to your application JAR file.

In this example, you can use an OpenJDK 8 Docker image, but you can replace it with the appropriate image for your Java version.

3. Configure the agent

The agent provides various flexible configuration options.

Option 1:

Pass configuration properties using the -D flag. For example, to configure a service name and a Zipkin exporter for traces:

docker run -e JAVA_TOOL_OPTIONS="-javaagent:/path/to/opentelemetry-javaagent.jar -Dotel.service.name=your-service-name -Dotel.traces.exporter=zipkin" -v /path/to/myapp.jar:/myapp.jar openjdk:8 java -jar /myapp.jar

Option 2:

Use environment variables to configure the agent:

docker run -e JAVA_TOOL_OPTIONS="-javaagent:/path/to/opentelemetry-javaagent.jar" -e OTEL_SERVICE_NAME="your-service-name" -v /path/to/myapp.jar:/myapp.jar openjdk:8 java -jar /myapp.jar

Option 3:

Supply a Java properties file to load configuration values:

docker run -e JAVA_TOOL_OPTIONS="-javaagent:/path/to/opentelemetry-javaagent.jar -Dotel.javaagent.configuration-file=/path/to/properties/file.properties" -v /path/to/myapp.jar:/myapp.jar openjdk:8 java -jar /myapp.jar

Replace /path/to/properties/file.properties with the actual path to your properties file.

Replace /path/to/opentelemetry-javaagent.jar with the actual path to the Java agent JAR file,

Replace your-service-name with your desired service name,

Replace /path/to/myapp.jar with the actual path to your application JAR file.

You will quickly gather telemetry data without modifying your code by automatically instrumenting your Java application with OpenTelemetry using the Java agent. This makes it convenient to add Observability to your Java applications.

You can also use OpenTelemetry Java with other web frameworks, such as Apache Wicket and Play.

The complete handbook of OpenTelemetry Metrics

The complete handbook of OpenTelemetry Metrics

Example Application

Let us assume that you want to auto-instrument a Java Spring application. First, we need to create the application. You can do this in two steps: 

Step 1: Upload your dependencies

Add the following dependencies to your project's pom.xml file:

<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-api</artifactId>
  <version>1.7.0</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk</artifactId>
  <version>1.7.0</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-javaagent-all</artifactId>
  <version>1.7.0</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-javaagent-instrumentation-spring</artifactId>
  <version>1.7.0</version>
</dependency>

Step 2: Create and launch an HTTP Server:

With the dependencies added, you can now create a simple Spring application with an HTTP server. Here's a basic example:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class MyApp {

    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

@RestController
class HelloController {

    @GetMapping("/")
    String hello() {
        return "Hello, World!";
    }
}

Having created your spring application, let us learn how to auto-instrument it with OTel. 

💡
Use Levitate as a backend for your Otel metrics. It offers high cardinality metrics support and long-term retention of up to a year. Get started today.

Auto-instrumenting with OTel Java Spring application

In the following steps, we assume you are using Docker.

1. Set up a Dockerized Java Spring Application:

FROM adoptopenjdk/openjdk11:alpine-jre
WORKDIR /app
COPY target/my-app.jar .
CMD ["java", "-javaagent:/opentelemetry-javaagent.jar", "-jar", "my-app.jar"]

Replace my-app.jar with the name of your Spring application JAR file, and Save the Dockerfile.

2. Add OpenTelemetry Agent to the Docker image:

Download the OpenTelemetry Java Agent JAR file by running the following command:

curl -LJO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent-all.jar

Rename the downloaded JAR file to opentelemetry-javaagent.jar: mv opentelemetry-javaagent-all.jar opentelemetry-javaagent.jar

Copy the opentelemetry-javaagent.jar into your project directory where the Dockerfile is located.

3. Update your Maven dependencies:

Add the OpenTelemetry Java instrumentation module for Spring to your pom.xml file by including the following dependency:

<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-javaagent-instrumentation-spring</artifactId>
  <version>1.7.0</version>
</dependency>

Save your pom.xml file and let Maven download the required dependencies.

4. Build and run your Dockerized application:

Build the Docker image using the command:

docker build -t my-app .

Once the image is built, run the Docker container using:

docker run -p 8080:8080 my-app

This will start your Spring application with OpenTelemetry instrumentation.

5. Verify Instrumentation:

Ensure your Spring application is up and running inside the Docker container. Open your web browser and visit http://localhost:8080 to trigger requests. Verify that your application is now automatically instrumented with OpenTelemetry.

6. Choose an Exporter:

You can set up exporters in your application's configuration to export telemetry data to a backend, such as Jaeger or Zipkin. Determine which backend or observability tool to export your telemetry data to.

💡
Use Levitate as a backend for your Otel metrics. It offers high cardinality metrics support and long-term retention of up to a year. Get started today.

1. Add the exporter dependency to your Maven pom.xml file. For example, if you choose the Jaeger exporter, add the following lines (make sure to update the version based on the latest release):

<dependency>
  <groupId>io.opentelemetry.exporter</groupId>
  <artifactId>opentelemetry-exporter-jaeger</artifactId>
  <version>1.7.0</version>
</dependency>

2. Update your Spring application configuration to initialize and configure the exporter. You can set the configuration for the Jaeger exporter through environment variables in your Dockerfile. Here's an example:

# Add environment variables to your Dockerfile
ENV OTEL_EXPORTER_JAEGER_SERVICE_NAME=my-app
ENV OTEL_EXPORTER_JAEGER_AGENT_HOST=jaeger-agent
ENV OTEL_EXPORTER_JAEGER_AGENT_PORT=6831

Ensure you set appropriate values for the service name, Jaeger agent host, and port.

3. Build your Docker image with your Java application and run the container. The exporter should send telemetry data to your chosen backend or observability tool.

7. Analyze and Visualize

 Use the chosen backend's user interface or APIs to inspect the data with which you will analyze and visualize the performance and behavior of your application. 

Conclusion

OpenTelemetry is a robust framework for the Observability of your Java applications. This guide has explained the minutiae of using OTel to observe Java applications. To read more deep dives like this, subscribe to the Last9 blog

💡
The Last9 promise — We will reduce your Observability TCO by about 50%. Our managed time series database data warehouse, Levitate, comes with streaming aggregation, data tiering, and the ability to manage high cardinality. If this sounds interesting, talk to us.

Newsletter

Stay updated on the latest from Last9.

Authors

Last9

Last9 helps businesses gain insights into the Rube Goldberg of micro-services. Levitate - our managed time series data warehouse is built for scale, high cardinality, and long-term retention.

Handcrafted Related Posts