Why OpenTelemetry for Java
OpenTelemetry has the most mature Java ecosystem of any OTel implementation. The Java agent is a single JAR you attach with -javaagent: that instruments dozens of frameworks and libraries at class load: Spring Web, Spring WebFlux, Spring Data, JDBC, Hibernate, JDBI, Apache HttpClient, OkHttp, Jetty, Tomcat, gRPC, Kafka, RabbitMQ, Elasticsearch, AWS SDK, and more. You get traces, JVM metrics, and Log4j/Logback log correlation without changing application source.
Because OTLP is the same on every language, the Java services and Python workers and Go gateways all show up in one Service Map in Telemtra with shared trace context.
Use the Java agent (recommended)
Download the latest agent and attach it to the JVM:
curl -L -o opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
java \
-javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=checkout-api \
-Dotel.resource.attributes=deployment.environment=production \
-Dotel.exporter.otlp.endpoint=https://ingest.telemtra.io \
-Dotel.exporter.otlp.protocol=http/protobuf \
-Dotel.exporter.otlp.headers=x-api-key=$TELEMTRA_API_KEY \
-jar app.jar The agent instruments HTTP servers, HTTP clients, JDBC, Kafka, gRPC, and dozens of others automatically. JVM metrics (heap, GC, threads, class loading) are also exported as OTLP without extra config.
Where this goes in your project
With the Java agent path, you do not modify source code or build files. The two changes are: the agent JAR available at runtime, and a JVM startup flag pointing at it.
my-service/
├── src/main/java/... # your existing Spring Boot app (unchanged)
├── build.gradle / pom.xml # unchanged
├── opentelemetry-javaagent.jar # NEW: drop into image or sidecar volume
├── Dockerfile # ENTRYPOINT adds -javaagent: and otel.* flags
└── deploy/env # TELEMTRA_API_KEY 1. Bake the agent into your container image (or mount it from a volume):
# Dockerfile
FROM eclipse-temurin:21-jre
RUN curl -L -o /opt/opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
COPY build/libs/app.jar /opt/app.jar
ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/opentelemetry-javaagent.jar"
ENV OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.telemtra.io
ENV OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
CMD ["java", "-jar", "/opt/app.jar"] JAVA_TOOL_OPTIONS is picked up by the JVM automatically — you do not need to change application start scripts. The remaining OTel settings can be passed as env vars or -D flags:
OTEL_SERVICE_NAME=checkout-api
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
OTEL_EXPORTER_OTLP_HEADERS=x-api-key=${TELEMTRA_API_KEY}
TELEMTRA_API_KEY=tk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 2. Restart the JVM. On the next boot the agent banner appears in the startup log and instrumentation begins. Nothing in build.gradle or application source changes for the agent path.
If you cannot use a Java agent (sealed runtime, framework conflict), the SDK path below adds a dependency and an autoconfigure call in main.
Or use the SDK directly
If you cannot attach an agent (sealed runtime, framework conflict), the autoconfigure module wires the SDK from environment variables:
dependencies {
implementation 'io.opentelemetry:opentelemetry-api'
implementation 'io.opentelemetry:opentelemetry-sdk'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure'
} import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
OpenTelemetry openTelemetry = AutoConfiguredOpenTelemetrySdk
.initialize()
.getOpenTelemetrySdk(); Spring Boot
The agent instruments Spring MVC and WebFlux automatically. For Spring Boot 3, the opentelemetry-spring-boot-starter offers a Spring-idiomatic alternative:
implementation 'io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter' Controller methods become spans, RestTemplate and WebClient calls produce client spans, and JDBC queries appear under the request that triggered them.
Micrometer bridge
If your team already uses Micrometer, the OTel Micrometer bridge forwards meters to OTLP without rewriting metric definitions:
implementation 'io.opentelemetry.instrumentation:opentelemetry-micrometer-1.5' This is the easiest path for migrating Spring Boot Actuator metrics from Prometheus scrape to OTLP push.
Logs via Logback and Log4j2
Add the OpenTelemetry appender for your logging framework. Logback example:
<configuration>
<appender name="OpenTelemetry" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender" />
<root level="INFO">
<appender-ref ref="OpenTelemetry" />
</root>
</configuration> Log records flow through OTLP, are correlated with the active trace and span IDs, and become searchable in Telemtra alongside the trace itself.
Manual spans for business logic
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.Span;
private final Tracer tracer = openTelemetry.getTracer("checkout-api");
public void submitOrder(String orderId, long totalCents) {
Span span = tracer.spanBuilder("checkout.submit_order")
.setAttribute("order.id", orderId)
.setAttribute("order.total_cents", totalCents)
.startSpan();
try (var scope = span.makeCurrent()) {
chargeCard(orderId, totalCents);
writeLedger(orderId);
sendReceipt(orderId);
} catch (Exception e) {
span.recordException(e);
throw e;
} finally {
span.end();
}
} Metrics
Meter meter = openTelemetry.getMeter("checkout-api");
LongCounter ordersTotal = meter.counterBuilder("orders_total").build();
DoubleHistogram checkoutLatency = meter.histogramBuilder("checkout_latency_ms").build();
ordersTotal.add(1, Attributes.of(AttributeKey.stringKey("plan"), "team"));
checkoutLatency.record(348); JVM metrics out of the box
The Java agent exports heap, non-heap, GC pause time, class count, and thread count as standard OTLP metrics. In Telemtra these can power a "JVM health" dashboard without any extra setup.
Troubleshooting
- Agent silently does nothing. Confirm the
-javaagent:path is correct and appears before-jar. Check the JVM logs at startup for "OpenTelemetry Javaagent" banner. - 403 from Telemtra. The header is
x-api-key, lowercase. Pass it viaotel.exporter.otlp.headersasx-api-key=YOUR_KEY. - Spans missing from a specific library. Some libraries are instrumented only with experimental features enabled — set
OTEL_INSTRUMENTATION_<NAME>_ENABLED=true. - Application slow at startup. Class loading via the agent costs a few hundred ms but does not affect steady-state performance.
For OTLP transport details see What is OTLP?. To centralize batching with a Collector sidecar, follow the Collector quickstart. New to Telemtra? The quickstart covers the Java agent path in 60 seconds. Create a workspace to get an API key and see your first JVM span in under a minute.
OpenTelemetry Java FAQ
Does the OpenTelemetry Java agent require code changes?
No. The Java agent is a single JAR you attach to the JVM with -javaagent:. It instruments Spring, JDBC, Hibernate, JDBI, Apache HttpClient, OkHttp, gRPC, Kafka, RabbitMQ, Elasticsearch, AWS SDK, and dozens more at class-load time. No source code, build file, or dependency changes.
Does it work with Spring Boot, Spring WebFlux, and Quarkus?
Yes for Spring Boot and Spring WebFlux out of the box - the agent covers Spring Web, WebFlux, Spring Data, and the embedded Tomcat/Netty/Jetty servers. Quarkus has dedicated OpenTelemetry support via the quarkus-opentelemetry extension; use that instead of the agent on Quarkus.
Will the Java agent slow down my application?
Class loading via the agent costs a few hundred milliseconds at startup. Steady-state performance impact is negligible for HTTP, JDBC, and gRPC instrumentations because OpenTelemetry uses lock-free queues and batches exports. If you measure a problem, narrow instrumentation with OTEL_INSTRUMENTATION_<NAME>_ENABLED=false.
Do JVM metrics export automatically?
Yes. The Java agent exports heap, non-heap, GC pause time, class count, and thread count as standard OTLP metrics out of the box. In Telemtra these can power a JVM health dashboard without any extra configuration.
What if I cannot use a Java agent?
Use the SDK path: add io.opentelemetry:opentelemetry-sdk, opentelemetry-exporter-otlp, and opentelemetry-sdk-extension-autoconfigure to your build, then read OTEL_* environment variables in main. The SDK path requires explicit instrumentation per library.
Further reading
- Telemtra quickstart — minimum Java agent config and snippets for every other supported stack.
- opentelemetry.io — Java documentation
- opentelemetry.io — Java agent guide
- github.com/open-telemetry/opentelemetry-java — SDK source
- github.com/open-telemetry/opentelemetry-java-instrumentation — agent and instrumentations
- OpenTelemetry semantic conventions