Distributed tracing, a key aspect of observability, enables developers to track and analyze requests spanning multiple services.
OpenTelemetry and Jaeger are comprehensive solutions for distributed tracing. But how do they complement each other? This article is a deep dive into how you can use Jaeger with OTel.
What is Jaeger?
Jaeger is a Cloud Native Computing Foundation (CNCF) open-source, end-to-end distributed tracing system built for monitoring and troubleshooting microservices-based architectures.
Usually incorporated as an OpenTelemetry backend, it is used to track and visualize user request behavior across the distributed components of complex systems.
Jaeger’s functions
To complement its distributed tracing functions, Jaeger also has the following.
1. Rich Visualization
Jaeger provides a user-friendly web interface for analyzing distributed traces. It offers features like charts, waterfall-style dependency visualization graphs, flame graphs, and detailed service-level breakdowns.
2. Root Cause Analysis
Jaeger offers in-depth visualization of entire event chains, provides tracing and logging of request paths, and details when and how a series of related software issues began, as well as which problematic microservice triggered issues in others.
3. Scalability
Jaeger scales horizontally to handle large amounts of tracing data. It uses storage backends like Elasticsearch, Cassandra, or other compatible databases.
4. Flexibility
Jaeger supports multiple instrumentation methods for various programming languages (e.g., native OpenTracing instrumentation, Prometheus, and client libraries).
What is OpenTelemetry?
OpenTelemetry (OTel) is an open-source observability framework used to collect telemetry data from applications, and export to prespecified backends. Also, a CNCF project, that provides a unified set of APIs, libraries, agents, and protocol specifications.
OpenTelemetry can also be used for distributed tracing, context propagation (for integrating request metadata across distributed services), and semantic conventions (for ensuring consistency in metric data representation and interpretation). Read more about OTel in our dedicated article.
Jaeger vs. OpenTelemetry
Both Jaeger and OpenTelemetry are complementary rather than competing tools. Nonetheless, let's compare the two.
Differentiators | Jaeger | OpenTelemetry |
Purpose | A distributed tracing system. | Provide a unified set of APIs, libraries and protocols for capturing and exporting multiple telemetry data types in distributed environments. |
Origin | Originally created by Uber Technologies as an open-source project. Now a Cloud Native Computing Foundation (CNCF) project. | A collaborative effort by multiple industry leaders and a Cloud Native Computing Foundation (CNCF) project. Combines the functionalities of OpenTracing and OpenCensus into a standardized observability framework. |
Integration | Compatible with OpenTelemetry. Has instrumentation libraries derived from the OpenTelemetry project. | Provides instrumentation libraries, which can be used to generate Jaeger-compatible traces. Integrates well with other observability tools. |
Ecosystem | Has a strong and growing ecosystem of plugins, extensions, and integrations. | Rapidly gaining industry support and has a growing ecosystem of expert contributors.
|
Why integrate Jaeger with OTel?
Consider these three advantages:
- End-to-end distributed tracing: Integrating both tools into one observability framework provides you with a complete solution: OpenTelemetry allows you to track and capture user requests as they traverse various microservices and application components, then Jaeger provides a web UI and a storage backend for analysis and storage of tracked user requests.
- Automatic Instrumentation: OTel’s automatic instrumentation reduces the manual effort required to add tracing to your application code. Since you do not need to manually modify the codebase, the risk of inaccurate or missing traces is reduced; meaning that more (accurate) traces can be collected and exported to Jaeger for visualization.
- Performance Optimization: The end goal of observability is to ensure optimal application function. With inputs from OpenTelemetry and Jaeger, you can use insights from traces and trace metadata collected and analyzed to pinpoint performance bottlenecks in specific microservices, optimize resource usage, and improve overall system efficiency.
Both Jaeger and OTel have a set of exporters that ease the process of using both for end-to-end distributed tracing of your application.
These exporters function as alternatives to one another, and the decision is yours to make on which one to use to sync both systems. However, it is best practice to choose the exporter that best fits your requirements in terms of compatibility, performance, and ease of use.
OpenTelemetry provides three specific Jaeger-compatible exporters. Let us examine each.
Jaeger Trace Exporter
The Jaeger Trace Exporter is a built-in OpenTelemetry exporter that allows you to export traces to a Jaeger backend server. It supports Thrift over UDP only, as it uses protocols such as gRPC or Thrift HTTP.
OTLP Exporter
The OpenTelemetry Protocol (OTLP) Exporter is generic in function. It exports telemetry data to various backends and endpoints—Jaeger inclusive. You can configure it to send traces to a Jaeger endpoint using the Jaeger reception format.
Batch Span Processor
OpenTelemetry also provides a Batch Span Processor that can be used to buffer and batch multiple spans before exporting them to a Jaeger backend. This processor can be configured to send the accumulated spans to Jaeger at regular intervals or when specific conditions are met.
Now, what are the set-in-stone steps to use Jaeger and OTel in your application?Below is a 10-step sequence explaining how.
Using Open Telemetry and Jaeger in your application
Follow these steps.
1. Determine your Use Case
Identify the specific use case or problem you want to address with distributed tracing. You may want to monitor the performance of your application, troubleshoot issues, or optimize resource usage.
2. Understand OpenTelemetry Concepts
Familiarize yourself with the core concepts of OpenTelemetry, such as spans, traces, and attributes. Understand how instrumentation works and how to propagate context between services.
3. Choose a Programming Language
OpenTelemetry supports multiple languages, including Java, Python, Go, JavaScript, and more. Choose the appropriate OpenTelemetry SDK and instrument your code accordingly.
4. Install Dependencies
Install the necessary OpenTelemetry and Jaeger dependencies for your chosen programming language. These dependencies include the OpenTelemetry SDK, Jaeger receiver, and any required language-specific libraries or plugins.
These configurations enable you to send traces to the Jaeger backend. Provide the necessary configuration options, such as the Jaeger endpoint, authentication credentials, and sampling strategies. This configuration can usually be done through environment variables or a configuration file.
6. Instrumentation
Identify the parts of your codebase that you want to instrument with distributed tracing. Use the OpenTelemetry APIs and libraries in your code to create spans and add attributes to capture relevant information. Instrumentation can be done manually by adding code snippets or by utilizing auto-instrumentation features provided by OpenTelemetry, if available for your programming language.
7. Export Traces to Jaeger
Configure OpenTelemetry to export captured traces to Jaeger. This involves setting the appropriate exporter options and endpoints in your OpenTelemetry configuration or code. Ensure that the Jaeger endpoint is correctly configured to receive and store exported trace data.
8. Verify Integration
Test and verify that your application is correctly capturing and sending traces to Jaeger. Generate some test requests that exercise the areas of your codebase you have instrumented. Use the Jaeger UI to visualize and validate that the traces are successfully reaching the backend.
9. Visualize and Analyze Traces
Once your application is instrumented and traces are exported to Jaeger, you can start visualizing and analyzing the distributed traces. Use the Jaeger user interface (UI) to filter traces, view latency distributions, identify bottlenecks, and understand microservices’ behavior.
10. Iterate and Optimize
Continuously monitor and analyze your distributed traces. Use insights gained from trace visualization to optimize your services for performance and efficiency. Fine-tune your sampling strategies, adjust instrumentation, and make improvements based on the data you collect.
Now, let us exemplify the steps above.
How to use OTel with Jaeger
Step 1: Choose a Programming Language
OpenTelemetry provides auto-instrumentation for various languages, including Java, Python, Go, .NET, and more. Ensure that both OpenTelemetry and Jaeger are available for your chosen language. For this guide, we assume you're using Python.
Install Jaeger through Docker:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
-p 14250:14250 \
-p 9411:9411 \
jaegertracing/all-in-one:latest
Install the necessary OpenTelemetry and Jaeger libraries for your programming language. The specific libraries required may vary depending on the language.
Here is an example snippet for Python to install the libraries in Dockerfile:
dockerfile
FROM python:3.8
RUN pip install opentelemetry-api opentelemetry-sdk opentelemetry-instrumentation-http requests jaeger-client
Set up the OTel exporter to send traces to your Jaeger backend. Provide the appropriate configuration options, such as the Jaeger endpoint URL and any authentication credentials needed. This configuration can be done through environment variables or a configuration file.
python
import os
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.resources import Resource
jaeger_host = os.getenv("JAEGER_HOST", "localhost")
jaeger_port = os.getenv("JAEGER_PORT", "6831")
trace.set_tracer_provider(
trace.TracerProvider(
resource=Resource.create({"service.name": "your-application-name"})
)
)
jaeger_exporter = JaegerExporter(agent_host_name=jaeger_host, agent_port=jaeger_port)
trace.get_tracer_provider().add_span_processor(
trace.BatchSpanProcessor(jaeger_exporter)
)
Step 4: Enable Auto-Instrumentation
Activate the OpenTelemetry auto-instrumentation module. Depending on the programming language, this may involve adding an initialization snippet or modifying your application's configuration. Here's an example for a Flask web application:
python
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from flask import Flask
app = Flask(__name__)
# Instrument the Flask application
FlaskInstrumentor().instrument_app(app)
Step 5: Verify Tracing
Test your application to ensure that traces are being captured and exported to Jaeger. Generate some test requests that exercise different parts of your application. Use the Jaeger UI to verify that the traces are arriving at the backend and can be visualized.
bash
docker run -e JAEGER_HOST=your-jaeger-host -e JAEGER_PORT=your-jaeger-port your-docker-image
Step 6: Explore and Analyze Traces
Once traces are successfully captured and sent to Jaeger, use Jaeger's rich visualization UI to analyze them and gain insights into application performance.
📝
The Jaeger backend should be available at `http://your-jaeger-host:your-jaeger-port`.
Step 7: Fine-Tune Configuration
Adjust auto-instrumentation configuration options to meet your specific requirements. This may include tweaking sampling strategies, adding custom attributes to traces, or excluding certain parts of your codebase from instrumentation.
Step 8: Monitor and Optimize
Continuously monitor your application's traces to identify bottlenecks or areas for optimization.
Jaeger accepts tracing information formats such as OpenTelemetry Protocol (OTLP), Thrift Batch, and Protobuf Batch.
A range of attributes, including trace, parent and span IDs, start and end times, attributes, events, links, and status can be mapped from OpenTelemetry to Jaeger formats to enable Jaeger to understand and process the data for visualization.
Convert OpenTelemetry spans into Jaeger formats by following the steps outlined below.
Step 1: Ingesting Spans from Jaeger Using OpenTelemetry
To ingest spans from Jaeger, set up the OpenTelemetry Collector with a Jaeger receiver. The Jaeger receiver acts as a bridge between Jaeger and OpenTelemetry.
Here's an example configuration file for the OpenTelemetry Collector with Jaeger receiver.
receivers:
jaeger:
protocols:
grpc:
thrift_http:
exporters:
logging:
processors:
batch:
service:
pipelines:
traces:
receivers: [jaeger]
processors: [batch]
exporters: [logging]
Step 2: OpenTelemetry Collector
Set up and run the OpenTelemetry Collector using the configuration file from the previous step. Here's an example of how to run the OpenTelemetry Collector.
bash
docker run --rm -v /path/to/config.yaml:/etc/otel-collector-config.yaml otel/opentelemetry-collector-contrib:latest --config=/etc/otel-collector-config.yaml
📝
Make sure to replace `/path/to/config.yaml` with the path to your actual configuration file.
Step 3: Jaeger Receiver
With the OpenTelemetry Collector running, you can now configure Jaeger Agent and HotRod to send spans to it. Here's an example of configuring the Jaeger Agent.
bash
docker run --rm -p 6831:6831/udp -p 6832:6832/udp jaegertracing/jaeger-agent:latest --reporter.grpc.host-port=<otel_collector_host>:14250
Replace `<otel_collector_host>` with the hostname or IP address of the machine running the OpenTelemetry Collector.
Step 4: Jaeger Agent and HotRod
To simulate sending spans to Jaeger, you can use the Jaeger Agent and HotRod.
Start the Jaeger Agent Container:
docker run --name jaeger-agent \
-p 5778:5778/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5775:5775/udp \
jaegertracing/jaeger-agent:latest
Start the HotRod:
docker run --name hotrod \
-e JAEGER_AGENT_HOST=your_jaeger_agent_host \
-e JAEGER_AGENT_PORT=6831 \
-p 8080-8083:8080-8083 \
jaegertracing/example-hotrod:latest all
Replace `your_jaeger_agent_host` with the address of the Jaeger Agent container.
Step 5: Examples
Now that everything is set up, you can instrument your applications with OpenTelemetry and start generating and exporting traces.
Here's an example of using OpenTelemetry in Python:
from opentelemetry import trace
from opentelemetry.trace import SpanKind
# Create a tracer
tracer = trace.get_tracer(__name__)
# Create a span
with tracer.start_as_current_span("example_span", kind=SpanKind.SERVER) as span:
# Add custom attributes or events if needed
span.set_attribute("example_attribute", "value")
span.add_event("example_event")
# Perform your application logic
# End the span
span.end()
Once the spans are generated, the OpenTelemetry Collector configured with the Jaeger receiver will pick them up and forward them to downstream exporters.
You should now see spans flowing from the Hot Rod to the Jaeger Agent, and from there to the OpenTelemetry Collector. Afterward, the OpenTelemetry Collector will transform and send the spans to Jaeger.
📝
You can then use Jaeger's user interface accessible at
http://localhost:16686 to visualize and analyze the collected spans.
Conclusion
Integrating Jaeger with OpenTelemetry provides you with a comprehensive distributed tracing solution that allows you to capture, export, and analyze traces, enabling you to gain valuable insights into your microservices ecosystem.
Understanding the basics of OpenTelemetry and Jaeger, and following the steps provided for installing, instrumenting, exporting, and visualizing traces will help you leverage the benefits of and effectively utilize the powerful combination to ensure optimal application performance and improve user experience.
🤝
If you're passionate about reliability, observability, or monitoring, be sure to join our
SRE Discord community to connect and share ideas!