OpenTelemetry Java for Uptrace

This document explains how to configure the OpenTelemetry Java Agent to export spans, metrics, and logs to Uptrace using OTLP/gRPC.

OpenTelemetry Java Agent

The OpenTelemetry Java Agent provides automatic instrumentation and tracing capabilities for Java applications without requiring any code changes. It works by attaching to a Java application at runtime and intercepting method calls to collect telemetry data.

The agent simplifies the process of instrumenting your Java applications by eliminating the need to modify your application's source code.

Quickstart

Follow this guide to instrument your Java application and send telemetry data to Uptrace.

Step 0: Create Uptrace Project

Create an Uptrace project to obtain a DSN (connection string), for example: https://<secret>@api.uptrace.dev?grpc=4317.

Step 1: Download Java Agent

Download the latest pre-compiled Java agent JAR:

shell
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

Step 2: Configure Environment Variables

Configure the agent to export data to Uptrace using environment variables:

shell
export OTEL_RESOURCE_ATTRIBUTES=service.name=myservice,service.version=1.0.0
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<FIXME>"
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA
export OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM

Step 3: Run Your Application

Enable the agent by providing the -javaagent flag when starting your application:

shell
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar

That's it! The agent supports a huge number of libraries and frameworks and most popular application servers.

Configuration

The agent can be configured using environment variables, system properties, or a configuration file.

Environment Variables

Use environment variables to configure OpenTelemetry:

shell
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.uptrace.dev:4317
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<FIXME>"
export OTEL_RESOURCE_ATTRIBUTES=service.name=myservice,service.version=1.0.0
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA
export OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM

System Properties

Instead of environment variables, you can use system properties:

shell
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar \
     -Dotel.exporter.otlp.endpoint=https://api.uptrace.dev:4317 \
     -Dotel.exporter.otlp.headers=uptrace-dsn=<FIXME> \
     -Dotel.resource.attributes=service.name=myservice,service.version=1.0.0 \
     -Dotel.traces.exporter=otlp \
     -Dotel.metrics.exporter=otlp \
     -Dotel.logs.exporter=otlp \
     -Dotel.exporter.otlp.compression=gzip \
     -Dotel.exporter.otlp.metrics.temporality.preference=DELTA \
     -Dotel.exporter.otlp.metrics.default.histogram.aggregation=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM

Configuration File

You can save system properties to a file, for example uptrace.properties:

properties
otel.exporter.otlp.endpoint=https://api.uptrace.dev:4317
otel.exporter.otlp.headers=uptrace-dsn=<FIXME>
otel.resource.attributes=service.name=myservice,service.version=1.0.0
otel.traces.exporter=otlp
otel.metrics.exporter=otlp
otel.logs.exporter=otlp
otel.exporter.otlp.compression=gzip
otel.exporter.otlp.metrics.temporality.preference=DELTA
otel.exporter.otlp.metrics.default.histogram.aggregation=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM

Pass the configuration file to the agent using the otel.javaagent.configuration-file system property:

shell
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -Dotel.javaagent.configuration-file=path/to/uptrace.properties \
     -jar myapp.jar

Micrometer Metrics

Micrometer is a metrics collection library for Java applications that provides a simple way to instrument code with various metrics such as timers, gauges, counters, histograms, and distributions.

Popular frameworks like Spring Boot provide dependency management and auto-configuration for Micrometer out-of-the-box. For other frameworks, you need to enable Micrometer metrics first. For example, to monitor JVM metrics:

java
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.ProcessorMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;

new ClassLoaderMetrics().bindTo(Metrics.globalRegistry);
new JvmMemoryMetrics().bindTo(Metrics.globalRegistry);
new JvmGcMetrics().bindTo(Metrics.globalRegistry);
new ProcessorMetrics().bindTo(Metrics.globalRegistry);
new JvmThreadMetrics().bindTo(Metrics.globalRegistry);

OpenTelemetry Bridge for Micrometer

The OpenTelemetry Java agent automatically detects if the instrumented application is using Micrometer and injects a special MeterRegistry implementation to collect Micrometer meters.

To export metrics with the Java agent, add a dependency on the micrometer-core library:

Maven:

xml
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
    <version>1.10.5</version>
</dependency>

Gradle:

gradle
implementation("io.micrometer:micrometer-core:1.10.5")

Custom Metrics

To create custom metrics with Micrometer, use meter factory methods provided by the Metrics class, or use meter builders and register them in the Metrics.globalRegistry:

java
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;

class MyClass {
    private final Counter myCounter = Metrics.counter("my_custom_counter");
    private final Timer myTimer = Timer.builder("my_custom_timer").register(Metrics.globalRegistry);

    int foo() {
        myCounter.increment();
        return myTimer.recordCallable(this::fooImpl);
    }

    private int fooImpl() {
        // Your business logic here
        return 42;
    }
}

Sampling

You can reduce the number of created (sampled) spans by configuring head-based sampling.

For example, to sample 20% of traces:

shell Environment vars
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.2
shell System props
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar \
     -Dotel.traces.sampler=parentbased_traceidratio \
     -Dotel.traces.sampler.arg=0.2

See the documentation for more details.

Disabling the Java Agent

To disable the agent entirely, pass -Dotel.javaagent.enabled=false or use the OTEL_JAVAAGENT_ENABLED=false environment variable.

You can also disable specific instrumentations by passing -Dotel.instrumentation.[name].enabled=false or using the OTEL_INSTRUMENTATION_[NAME]_ENABLED=false environment variable. See the documentation for the list of instrumentation names.

Troubleshooting

Common Issues

Agent not starting:

  • Verify the path to the opentelemetry-javaagent.jar file is correct
  • Check that Java has read permissions for the JAR file
  • Ensure you're using a supported Java version

No data in Uptrace:

  • Verify your DSN is correctly configured in OTEL_EXPORTER_OTLP_HEADERS
  • Check that the endpoint URL is correct: https://api.uptrace.dev:4317
  • Ensure your application is generating spans (check application logs for errors)

Performance issues:

  • Adjust sampling rate to reduce overhead: OTEL_TRACES_SAMPLER_ARG=0.1 (10% sampling)
  • Disable unused instrumentations to reduce memory usage

What's next?

Check available OpenTelemetry Java agent configuration options