Last9 Last9

Feb 19th, ‘25 / 4 min read

OpenTelemetry Java: A Detailed Guide with Examples and Troubleshooting

Learn how to set up OpenTelemetry in Java with examples, best practices, and troubleshooting tips to monitor and optimize your applications.

OpenTelemetry Java: A Detailed Guide with Examples and Troubleshooting

If you're a Java developer looking to get started with OpenTelemetry, you've come to the right place. This guide will take you through everything you need to know to instrument your Java applications with OpenTelemetry, from basic setup to exporting traces and metrics.

What is OpenTelemetry?

OpenTelemetry (OTel) is an open-source observability framework for collecting traces, metrics, and logs from applications. It helps developers gain insights into their application's performance and behavior without being tied to a specific vendor.

OpenTelemetry provides APIs, libraries, and agents to collect and export telemetry data, making it a critical tool for modern distributed systems.

Why Use OpenTelemetry for Java Applications?

If you're working with distributed systems, monitoring, and debugging can be a nightmare.

OpenTelemetry simplifies this by providing a standardized way to instrument your Java applications, making it easier to collect telemetry data and send it to backends like Prometheus, Last9, and Zipkin.

Example Use Cases

  • Microservices Monitoring: Track interactions between services and identify performance bottlenecks.
  • Performance Optimization: Measure latency and throughput of different components.
  • Error Detection: Identify failed transactions and error-prone areas in your application.
💡
If you're looking to scale your OpenTelemetry setup efficiently, check out this guide on scaling the OpenTelemetry Collector to handle high-volume telemetry data in production.

Installing and Configuring OpenTelemetry in Java

To integrate OpenTelemetry into your Java application, you need to include the necessary dependencies and set up the environment correctly.

Required Dependencies

For a Maven project, add the following dependencies to your pom.xml:

<dependencies>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.30.0</version>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
        <version>1.30.0</version>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
        <version>1.30.0</version>
    </dependency>
</dependencies>

For a Gradle project, add the following to build.gradle:

dependencies {
    implementation 'io.opentelemetry:opentelemetry-api:1.30.0'
    implementation 'io.opentelemetry:opentelemetry-sdk:1.30.0'
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp:1.30.0'
}

How to Configure the OpenTelemetry Environment

Ensure you have Java 8 or later installed and that your project is configured to support OpenTelemetry. Set up your application to export traces and metrics by configuring OpenTelemetry exporters correctly.

💡
To simplify OpenTelemetry configuration, you can leverage environment variables—learn more in this guide on OpenTelemetry environment variables.

Getting Started with OpenTelemetry in Java

Step 1: Initialize OpenTelemetry SDK

To start collecting traces, initialize the OpenTelemetry SDK in your application:

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;

public class OpenTelemetryExample {
    public static void main(String[] args) {
        OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder()
            .setEndpoint("http://localhost:4317")
            .build();
        
        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
            .addSpanProcessor(BatchSpanProcessor.builder(spanExporter).build())
            .build();
        
        OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
            .setTracerProvider(tracerProvider)
            .build();
        
        Tracer tracer = openTelemetry.getTracer("com.example.OpenTelemetryExample");
        
        Span span = tracer.spanBuilder("sample-span").startSpan();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            span.recordException(e);
        } finally {
            span.end();
        }
    }
}

Step 2: Run and Observe Traces

Start Jaeger for trace visualization:

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 16686:16686 \
  jaegertracing/all-in-one:latest

Visit http://localhost:16686 to see your traces.

How to Fix Common OpenTelemetry Java Issues

1. No Traces Appearing in Jaeger

Possible Causes:

  • OpenTelemetry SDK is not properly initialized.
  • Jaeger is not running or is listening on a different port.
  • Spans are not being created, started, or ended correctly.

Solutions:

  • Double-check that the OpenTelemetry SDK is correctly configured in your application. Ensure that the tracer provider and span processor are properly set up.
  • Confirm that Jaeger is running by accessing http://localhost:16686. If it's not running, restart the Jaeger container using:
docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 16686:16686 jaegertracing/all-in-one:latest
  • Ensure that spans are being correctly started and ended in your application code. For example:
Span span = tracer.spanBuilder("sample-span").startSpan();
try {
    // Simulate work
    Thread.sleep(1000);
} catch (InterruptedException e) {
    span.recordException(e);
} finally {
    span.end();
}
💡
If you're unsure whether to use OpenTelemetry or Jaeger for tracing, check out this comparison guide on OpenTelemetry vs. Jaeger to make the right choice for your application.

2. Application Crashes on Startup

Possible Causes:

  • Missing or incorrect OpenTelemetry dependencies.
  • Conflicting versions of OpenTelemetry libraries.
  • Incorrect configuration in your application code.

Solutions:

  • Verify that all required dependencies are present in pom.xml (Maven) or build.gradle (Gradle).
  • Ensure that all OpenTelemetry libraries use compatible versions. A mix of old and new versions can cause class-loading issues.
  • Check error logs for stack traces indicating missing classes or misconfigurations.

3. Metrics Not Showing Up in Prometheus

Possible Causes:

  • The Prometheus OpenTelemetry exporter is not configured correctly.
  • Prometheus is not scraping the correct endpoint.
  • The application is not exposing metrics properly.

Solutions:

  • Verify that your application is using the correct OpenTelemetry metrics exporter, such as:
OtlpGrpcMetricExporter metricExporter = OtlpGrpcMetricExporter.builder()
    .setEndpoint("http://localhost:4317")
    .build();
  • Check your prometheus.yml configuration to ensure it includes the correct scrape job:
scrape_configs:
  - job_name: 'otel-metrics'
    static_configs:
      - targets: ['localhost:9464']
  • Restart Prometheus and confirm that it is successfully scraping metrics using:
curl http://localhost:9090/api/v1/targets
💡
To better understand how Prometheus handles metrics, check out this deep dive on Prometheus metric types and learn how they integrate with OpenTelemetry.

Best Practices for OpenTelemetry in Java

To ensure efficient and effective observability, follow these best practices:

1. Use Context Propagation

Always propagate context across services to maintain a complete trace. Use TextMapPropagator to carry trace information between services.

2. Optimize Data Collection

Avoid excessive instrumentation that might lead to unnecessary performance overhead. Use sampling strategies to reduce the volume of collected data.

3. Choose the Right Exporter

Select the appropriate exporter based on your observability stack. OTLP is widely supported, but Jaeger and Zipkin may be more suitable for some use cases.

4. Use Semantic Conventions

Follow OpenTelemetry's semantic conventions for naming spans, attributes, and metrics to ensure consistency.

5. Regularly Update OpenTelemetry SDK

Stay up-to-date with the latest versions of OpenTelemetry to benefit from bug fixes, security patches, and performance improvements.

Conclusion

OpenTelemetry provides a robust and flexible solution for monitoring Java applications. Following best practices ensures you get the most out of OpenTelemetry while maintaining application performance.

Start integrating OpenTelemetry today for enhanced observability in your Java applications.

💡
And if you ever want to dive deeper, join our Discord community! We have a dedicated channel where you can discuss your specific use case with fellow developers.

Contents


Newsletter

Stay updated on the latest from Last9.

Authors
Anjali Udasi

Anjali Udasi

Helping to make the tech a little less intimidating. I love breaking down complex concepts into easy-to-understand terms.