OpenTelemetry: from signals to Java traces.
A practical guide to portable telemetry, Jaeger, and adding tracing to a Java service without changing its source code.
A request arrives at your API, calls another service, and waits on a database. If the user reports a slow response, which step took the time? OpenTelemetry gives each part of that journey a shared context, so you can follow the request through the system instead of stitching together guesses.
What is OpenTelemetry?
OpenTelemetry, often shortened to OTel, is an open source, vendor-neutral framework for creating, collecting, and exporting telemetry. It provides specifications, APIs, SDKs, instrumentation libraries, and the OpenTelemetry Collector. It is not itself a monitoring backend or a dashboard.
Telemetry is commonly grouped into three signals:
- TracesShow the path of one request through a service or across several services. A trace is made up of timed operations called spans.
- MetricsNumerical measurements over time, such as request rate, error count, latency, or memory use.
- LogsTimestamped event records that provide details about what happened. Logs can be correlated with trace context.
OpenTelemetry also defines context propagation, which carries identifiers such as trace and span IDs across process boundaries. That shared context is what lets a trace connect work done by separate services.
Why use OpenTelemetry?
Without a common instrumentation layer, each application may need backend-specific libraries and configuration. That can make telemetry harder to standardize and a backend change more disruptive.
- Keep instrumentation portable. Send data over OTLP, the OpenTelemetry Protocol, to compatible backends. This makes it easier to change or add destinations.
- Get useful signals from existing libraries. Many common frameworks, HTTP clients, and database libraries have supported instrumentation.
- Follow requests across services. Context propagation links spans across service boundaries when participating services are instrumented and configured to propagate context.
- Choose where processing happens. Export directly to a backend to start, or route through a Collector to batch, filter, enrich, and send telemetry to one or more destinations.
OpenTelemetry does not automatically make every application observable. Instrumentation coverage varies, and teams still need to choose useful attributes, sampling, retention, and alerts. Avoid putting secrets or personal data into span attributes, and watch for high-cardinality values such as raw user IDs.
OpenTelemetry and Jaeger
OpenTelemetry and Jaeger solve related but different parts of the tracing workflow. OpenTelemetry creates and exports traces. Jaeger receives trace data, stores it, and provides a UI to search and inspect traces. Jaeger accepts OTLP, so an OTel SDK or Java agent can export to Jaeger directly for a local setup.
For a small learning environment, the Java agent can send OTLP directly to Jaeger. In a larger deployment, a Collector is commonly placed in the middle. It can receive telemetry from many services, apply shared processing and sampling, and export to Jaeger or other destinations. The Collector is optional for the local demo below.
The Jaeger all-in-one image combines receiving and querying in one process and uses in-memory storage by default. That makes it convenient for a laptop demo; traces disappear when the container stops, so this is not a durable production configuration.
Set up Java auto-instrumentation
The OpenTelemetry Java agent attaches to a Java 8+ process and instruments many supported frameworks and libraries at runtime. It can capture common boundaries such as incoming HTTP requests, outbound HTTP calls, and database operations without adding instrumentation code to the application. Support depends on the libraries and versions in use.
1. Start Jaeger v2
With Docker installed, start the official Jaeger v2 all-in-one image. Ports 4317 and 4318 accept OTLP over gRPC and HTTP respectively; 16686 serves the UI.
docker run --rm --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
cr.jaegertracing.io/jaegertracing/jaeger:2.22.0Keep this terminal open and visit http://localhost:16686 to open Jaeger.
2. Download the Java agent
Download the latest OpenTelemetry Java agent JAR from the project’s releases. For repeatable deployments, select and pin a specific agent release in your build or deployment process rather than resolving “latest” at every startup.
curl -L -o opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar3. Configure and launch your application
Set a meaningful service name, point the exporter at Jaeger’s OTLP/HTTP receiver, and attach the agent using -javaagent. OpenTelemetry Java agent 2.x uses HTTP/protobuf by default; the explicit protocol below makes the example clear.
export OTEL_SERVICE_NAME=orders-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
java -javaagent:./opentelemetry-javaagent.jar -jar app.jarReplace app.jar with your application’s JAR path. The agent runs in the same JVM as the app. If the process runs inside a container, localhost refers to that container itself; use a hostname reachable from it, such as the Jaeger service name on a shared Docker network.
4. Generate traffic and find a trace
Send requests to an endpoint in your application that exercises an instrumented server framework. Then open Jaeger, choose orders-api in the Service list, and search for traces. Open a result to inspect its spans, timing, status, and attributes.
If the service is missing, check the agent startup output, verify the application uses a supported library and version, and confirm the OTLP endpoint is reachable from the Java process. Ensure the app received a request after it started; a quiet service has no request trace to display.
What to know before production
Auto-instrumentation is a useful starting point, not a complete observability strategy. A production setup should make deliberate choices about:
- Collector placement. A Collector can centralize credentials, routing, batching, filtering, and retries. Use resilient deployment and configure the appropriate receivers and exporters.
- Sampling and volume. Trace every request only if the cost and volume are acceptable. Choose a sampling strategy that preserves the traces needed for investigation.
- Data handling. Review captured headers, SQL statements, and attributes. Remove or redact sensitive values before exporting.
- Service identity. Set stable service names and useful deployment attributes so traces can be grouped and compared.
- Manual spans where needed. The agent can show supported library boundaries, but business operations often need custom spans or attributes to explain what the application is doing.
OpenTelemetry gives your services a shared way to describe their work. Jaeger gives you a place to follow that work when a request takes an unexpected path.