Sep 19th, ‘24/9 min read

How to Use Jaeger with OpenTelemetry

This guide shows you how to easily use Jaeger with OpenTelemetry for improved tracing and application monitoring.

How to Use Jaeger with OpenTelemetry

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.

📖
Check out our Developer's Guide to Installing OpenTelemetry Collector for step-by-step instructions on setting up your observability stack.

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.

5. Configure the OpenTelemetry Collector and Jaeger Receiver

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.

📄
Read our Guide to Optimizing Prometheus Remote Write Performance for tips on improving efficiency and performance in your monitoring setup.

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.

Step 2: Install Required Tools & Libraries

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

Step 3: Configure the OTel Exporter

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. 

OpenTelemetry to Jaeger Transformation

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!

Newsletter

Stay updated on the latest from Last9.

Authors

Anjali Udasi

Helping to make the tech a little less intimidating. I love breaking down complex concepts into easy-to-understand terms.

Handcrafted Related Posts