OpenTelemetry Java for Uptrace

Quickstart

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 such as traces and metrics.

OpenTelemetry Java Agent simplifies the process of instrumenting your Java applications to collect telemetry data by eliminating the need to modify your application's source code.

  • Step 0. Create an Uptrace project to obtain a DSN](/get#dsn) (connection string), for example, https://<token>@api.uptrace.dev?grpc=4317.

Step 1. 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 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://otlp.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. Enable the agent by providing the -javaagent flag when starting your app:

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

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

Configuration

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

Environment variables

For example, you can use environment variables to configure OpenTelemetry:

shell
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.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 using environment variables, you can also use system properties:

shell
java -javaagent:path/to/opentelemetry-javaagent.jar \
     -jar myapp.jar \
     -Dotel.exporter.otlp.endpoint=https://otlp.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

Config file

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

shell
otel.exporter.otlp.endpoint=https://otlp.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

And pass that 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-based applications, which provides a simple way to instrument code with various metrics such as timers, gauges, counters, histograms, and distributions.

Some 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:

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);

Then you need to install the OpenTelemetry bridge for Micrometer.

OpenTelemetry bridge for Micrometer

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

To export metrics with the javaagent, you need to add a dependency on the micrometer-core library.

For Maven users:

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

For Gradle users:

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

Custom metrics

To create custom metrics with Micrometer, use one of 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;

class MyClass {

  Counter myCounter = Metrics.counter("my_custom_counter");
  Timer myTimer = Timer.builder("my_custom_timer").register(Metrics.globalRegistry);

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

  private int fooImpl() {
    // ...
  }
}

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 documentation for details.

Disabling java agent

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

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

What's next?

Next, check available OpenTelemetry Java agent configuration options.