Java
This guide provides detailed instructions on how to instrument a Java or Spring Boot application using the OpenTelemetry Java Agent. By the end of this guide, you will be able to automatically capture logs from your application and send them to CtrlB, correlated with the request flows that produced them.
OpenTelemetry SDK status (mid-2026): Java log instrumentation is Stable. See the OpenTelemetry language status table.
Prerequisites
- Java 8+
- CtrlB OTLP (ingestion host, auth, stream, service name): See Prerequisites
Why Instrument with the OpenTelemetry Java Agent?
The OpenTelemetry Java agent enables zero-code instrumentation, meaning:
- You don’t need to modify your application code.
- The agent automatically detects logging frameworks such as Logback and Log4j, and application frameworks such as Spring Boot, JDBC, HTTP clients, gRPC, Kafka, Redis, etc.
- It captures the logs your application already writes and enriches each record with
trace_id/span_idcorrelation. - The data is exported using OTLP to CtrlB.
This makes it the easiest way to start collecting logs for any Java application.
When the agent detects Logback or Log4j, it auto-installs the OpenTelemetry log-appender bridge. Your existing logger.info(...) / logger.error(...) statements flow into the OTLP logs pipeline unchanged — no new logging API to adopt — and each record is stamped with the trace_id and span_id active when it was written, so you can pivot from a log line to the request that produced it in CtrlB.
Step 1. Download the OpenTelemetry Java Agent
Download the latest Java agent JAR file from GitHub:
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
This file acts as a small "layer" around your application. When your app starts, the JVM loads this agent and instruments major libraries automatically.
Step 2. Configure OpenTelemetry Instrumentation
Instrumentation works only when the JVM is started with the agent:
OTEL_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_SERVICE_NAME=<service_name> \
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<INGESTION_HOST>/api/default/<STREAM_NAME>/_otel/v1/logs \
OTEL_TRACES_EXPORTER=none \
OTEL_METRICS_EXPORTER=none \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <API_TOKEN>,stream-name=<STREAM_NAME>" \
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <my-app>.jar
<service_name>is the name of your service as it should appear in CtrlB.
<my-app>.jaris the name of your application jar file.
In case you download opentelemetry-javaagent.jar file in different directory than that of the project, replace $PWD with the path of the otel jar file.
OTEL requires headers to be in comma-separated 'key=value' format as shown in the command above.
The block above sends logs only. To export traces, metrics, and logs from the same application, use the shared OTLP endpoint and headers instead of the signal-specific endpoint above:
OTEL_EXPORTER_OTLP_ENDPOINT=https://<INGESTION_HOST>/api/default \
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<INGESTION_HOST>/api/default/<STREAM_NAME>/_otel/v1/logs \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <API_TOKEN>,stream-name=<STREAM_NAME>"
How the Java Agent Works
When the JVM starts with:
-javaagent:opentelemetry-javaagent.jar
the agent:
-
Hooks into the JVM bytecode instrumentation layer (ByteBuddy) to automatically wrap supported libraries.
-
Bridges your logging framework (Logback / Log4j) into the OTLP logs pipeline, capturing the log records emitted during:
- incoming HTTP requests
- JDBC calls
- REST API calls (OkHttp, Apache HTTP client, WebClient)
- Messaging (Kafka, RabbitMQ)
- Cache calls (Redis)
- Exception and error handling
Each captured record carries the
trace_id/span_idof the request in progress, so logs stay correlated with their originating operation. -
Exports the collected telemetry to CtrlB via OTLP.
You don’t write a single line of logging-integration code.
Advanced Agent Configuration
The OpenTelemetry Java agent provides extensive configuration options to fine-tune instrumentation behavior, control data collection, and optimize performance for your specific use case.
Complete configuration reference: https://opentelemetry.io/docs/zero-code/java/agent/configuration/
How to Apply Configuration Options
All agent configurations can be passed as JVM system properties using the -D flag:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.javaagent.debug=true \
-Dotel.resource.attributes=deployment.environment=production \
-jar my-app.jar
Alternatively, use environment variables (replace . with _ and uppercase):
export OTEL_JAVAAGENT_DEBUG=true
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
java -javaagent:opentelemetry-javaagent.jar -jar my-app.jar
Common Configuration Options
| Configuration | Purpose | Example Value | Use Case |
|---|---|---|---|
otel.javaagent.debug | Enable detailed instrumentation logs during startup | true | Troubleshooting: verify which libraries are being instrumented |
otel.resource.attributes | Add custom metadata to all spans (environment, version, etc.) | deployment.env=prod,version=1.2.3 | Filter and group spans by deployment environment or app version |
otel.traces.sampler | Control sampling strategy to reduce trace volume | parentbased_traceidratio | High-traffic services: sample a percentage of traces to reduce costs |
otel.traces.sampler.arg | Sampling ratio (0.0 to 1.0) | 0.1 (10%) | Collect 10% of traces while maintaining statistical representativeness |
otel.instrumentation.[name].enabled | Enable/disable specific instrumentation libraries | false | Disable noisy instrumentations (e.g., excessive DB connection spans) |
Instrumentations Disabled by Default
Some instrumentations are intentionally disabled in the Java agent to prevent excessive span generation that provides little actionable value. You can enable them if needed for your specific use case.
| Instrumentation | Why Disabled | When to Enable | How to Enable |
|---|---|---|---|
jdbc-datasource | Creates a span for every DataSource#getConnection() call, which can happen hundreds of times per second in connection-pooled apps | Debugging connection pool issues or investigating connection leak patterns | -Dotel.instrumentation.jdbc-datasource.enabled=true |
dropwizard-metrics | Dropwizard's metrics API lacks label/tag support, resulting in low-cardinality, low-quality metrics that don't align with OpenTelemetry standards | Legacy apps heavily invested in Dropwizard metrics that need bridging to OTEL | -Dotel.instrumentation.dropwizard-metrics.enabled=true |
Example: Enabling Disabled Instrumentations
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.instrumentation.jdbc-datasource.enabled=true \
-Dotel.instrumentation.dropwizard-metrics.enabled=true \
-jar my-app.jar
⚠️ Warning: Enabling jdbc-datasource in high-throughput applications can significantly increase span volume and associated costs.
Step 3. Use OpenTelemetry Collector (Optional)
For production setups, it’s recommended to send logs through an OpenTelemetry Collector before CtrlB. This enables buffering, batching, retries, and multi-sink routing.
See OpenTelemetry Collector for Logs for configuration details.
Troubleshooting
Make sure to set OTEL_SERVICE_NAME before starting the JVM. The -javaagent:opentelemetry-javaagent.jar argument must come before -jar <your-app>.jar.