For the complete documentation index, see llms.txt. Markdown versions are available by appending .md to documentation URLs.

What Is Jaeger Tracing? Learn by Implementing

Last Updated: August 28, 202615 min read

Distributed applications make it difficult to follow requests across services and identify the source of latency or errors. Jaeger tracing addresses this problem by collecting and visualizing trace data generated by instrumented applications.

This article explains what Jaeger is, how distributed tracing works, its core components, how to set it up, and how to use traces to troubleshoot performance issues in distributed systems.

Jaeger is an open-source, cloud-native distributed tracing platform for monitoring and troubleshooting microservices-based applications. It reconstructs the path of a request from spans created as the request travels through multiple services.

Created at Uber in 2015, Jaeger became a graduated CNCF project in 2019. Jaeger v2 is re-architected around the OpenTelemetry Protocol (OTLP) and can receive trace data through OTLP. Applications are typically instrumented using OpenTelemetry, while Jaeger receives, stores, queries, and visualizes the resulting traces.

How Does Jaeger Tracing Work?

Jaeger tracing works by receiving spans generated by instrumented applications and reconstructing them into an end-to-end view of a request. Before Jaeger can display a trace, the instrumentation must start or continue a trace, record operations as spans, propagate trace context between services, and export completed spans.

1. Starting a Trace

When an instrumented service receives a request without existing trace context, its OpenTelemetry instrumentation creates a root span with a new trace ID and span ID.

The trace ID identifies the complete request journey, while the span ID identifies this particular operation. If valid trace context is present in the incoming request, the service continues that trace instead of starting a new one.

2. Recording Operations as Spans

Each instrumented operation creates a span. A span records details such as the operation name, start and end times, duration, status, service information, attributes, and events.

Each span has zero or one parent. A non-root span records its parent span’s ID, linking the operations into a trace tree. A root span has no parent span ID because it is the first span created for the trace. New spans generally use the currently active span as their parent, forming a hierarchy of operations. For example, an HTTP span for GET /checkout might record the route, response status code, and total duration. If the operation fails, the span can include an error status and an exception event. Application-specific information can also be recorded as custom span attributes.

When the service calls another service, HTTP instrumentation commonly creates a client span for the outgoing request. This client span becomes the current span whose context is sent to the downstream service.

3. Propagating Trace Context

Before sending the request, the instrumentation injects the current span’s context into the outgoing request headers. This process is called trace context propagation.

HTTP applications commonly use the W3C Trace Context  format:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             │                 │                   │              │
             │                 │                   │              Trace flags
             │                 │                   Parent ID
             │                 Trace ID
             Version

The parent-id field contains the span ID of the caller’s current span. The downstream service extracts this context and creates its own span with the same trace ID and a new span ID. The extracted span context becomes the parent of the new span, connecting operations performed by both services into one trace.

4. Exporting Spans

When a span ends, the OpenTelemetry SDK passes it to a span processor. Applications commonly use a Batch Span Processor, which temporarily queues completed spans and exports them in batches. The spans are sent through OTLP over gRPC or HTTP, either directly to Jaeger or through an optional OpenTelemetry Collector.

Batching moves most export work away from the request path and reduces the number of network calls. However, tracing still introduces some processing and memory overhead, and spans can be dropped if the export queue becomes full. After export, the spans enter the Jaeger backend, where they are stored and retrieved by trace ID. Jaeger uses their trace IDs and parent-child relationships to reconstruct and visualize the complete request.

Jaeger Architecture and Core Components

Jaeger v2 is built on the OpenTelemetry Collector framework. A single Jaeger binary can perform different roles, allowing the components to run together during local development or separately in production.

Jaeger architecture showing instrumented applications, an optional OpenTelemetry Collector, Jaeger Collector, trace storage, Jaeger Query, and Jaeger UI
Jaeger receives spans from instrumented applications, stores them, and makes them available through its query service and UI.

OpenTelemetry Collector

The OpenTelemetry Collector is an optional component that sits outside the Jaeger backend. It can receive spans from applications, process or enrich them, and forward them to Jaeger.

Applications can also export OTLP traces directly to Jaeger when they do not need this additional processing layer.

Older Jaeger architectures commonly placed a Jaeger Agent beside each application. Jaeger v2 still supports an agent role, but the project recommends using a standard OpenTelemetry Collector for new agent or sidecar deployments.

Jaeger Collector

The Jaeger Collector receives spans from instrumented applications or an OpenTelemetry Collector. Modern applications instrumented via OpenTelemetry SDKs commonly send spans through OTLP over gRPC on port 4317 or OTLP over HTTP on port 4318.

After receiving the spans, the collector passes them through its configured processing pipeline and writes them to the storage backend. It does not create application spans because that work happens inside the instrumented application.

Trace Storage

The storage backend persists spans so Jaeger can search and reconstruct traces later. Jaeger supports distributed storage backends such as Cassandra, Elasticsearch, and OpenSearch.

In-memory storage is useful for local development because it does not require an additional database. However, its data disappears when Jaeger restarts, making it unsuitable for production use.

Jaeger Query

Jaeger Query reads trace data from storage and provides the APIs used to search and retrieve it. Searches can use properties such as service name, operation, duration, tags, and trace ID.

Separating the query role from collection allows production deployments to scale trace ingestion and trace searches independently.

Jaeger UI

The Jaeger UI communicates with Jaeger Query and presents trace data in a browser. It displays spans on a shared timeline, allowing engineers to inspect service relationships, operation durations, attributes, events, and errors.

The UI does not retrieve spans directly from the storage backend. Jaeger Query retrieves the requested data and returns it to the interface.

Key Features of Jaeger

  • Native OTLP Support: Jaeger accepts OTLP trace data over gRPC and HTTP, allowing OpenTelemetry-instrumented applications to export spans directly or through an OpenTelemetry Collector.

  • Trace Search and Visualization: Engineers can search traces by service, operation, duration, tags, or trace ID and inspect spans in a timeline. The UI also supports structural trace comparison.

  • Service Dependency Graphs: Jaeger generates System Architecture and Deep Dependency Graph views that show observed relationships and request paths between services.

  • Service Performance Monitoring: The Monitor view presents request rate, error rate, and duration metrics derived from spans, helping identify slow or failing operations.

  • Scalable Deployment and Storage: Jaeger’s Collector and Query roles can scale independently and work with storage backends such as Cassandra, Elasticsearch, and OpenSearch.

  • Remote and Adaptive Sampling: Jaeger centralizes sampling configuration and calculates probabilities for services and endpoints to meet configured trace-throughput targets.

Set Up Jaeger Locally with Docker

This example runs Jaeger alongside two instrumented Node.js services:

Client → frontend-service → inventory-service → Jaeger

The demo generates normal, slow, and failed requests so you can inspect meaningful traces in the Jaeger UI.

Prerequisites

  • Before starting, install Git, Docker Desktop with Docker Compose, and Node.js with npm.
  • Download the demo accompanying this article, then open its directory:
    git clone --depth 1 --filter=blob:none --sparse https://github.com/SigNoz/examples.git signoz-examples && cd signoz-examples && git sparse-checkout set nodejs/jaeger-tracing-demo && cd nodejs/jaeger-tracing-demo

Step 1: Start Jaeger and the applications

Install the traffic-generator dependencies:

npm install

Start Jaeger, frontend-service, and inventory-service:

docker compose up --build -d

Confirm that all three containers are running:

docker compose ps

The Compose configuration runs Jaeger 2.20 using its built-in all-in-one configuration:

services:
  jaeger:
    image: cr.jaegertracing.io/jaegertracing/jaeger:2.20.0
    ports:
      - "16686:16686"
      - "4317:4317"
      - "4318:4318"

Port 16686 exposes the Jaeger UI, while ports 4317 and 4318 accept OTLP data over gRPC and HTTP, respectively.

The application containers export their traces to Jaeger over OTLP/HTTP:

environment:
  OTEL_SERVICE_NAME: frontend-service
  OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://jaeger:4318/v1/traces

Here, jaeger is the Compose service name. Docker resolves it to the Jaeger container inside the Compose network.

Step 2: Generate traces

Run the included traffic generator:

npm run traffic

It sends normal, deliberately slow, and deliberately failing requests through both services.

Step 3: View and Analyze Traces in Jaeger

After generating traffic, open the Jaeger UI at http://localhost:16686. Select frontend-service from the Service menu and click Find Traces.

The search results contain normal requests, slow requests lasting approximately 700 ms, and failed requests marked with an error. Start with a normal trace to understand the expected request path before investigating an outlier.

Jaeger search results containing normal, slow, and failed frontend requests
The generated traffic produces short successful traces, slow outliers, and failed traces marked with errors.

Understand the trace timeline

Open a trace to view its spans. Each row represents a timed operation performed while processing the request. Indentation shows parent-child relationships, while the horizontal bars show when each operation started and how long it ran relative to the complete request.

For this demo, a checkout trace follows this path:

frontend HTTP request
└── frontend call to inventory
    └── inventory HTTP request
        └── inventory.lookup

The frontend and inventory spans share the same trace ID because OpenTelemetry propagates trace context between the services.

Span durations can overlap because a parent span remains active while its child operation runs. Therefore, adding every span duration together does not give the total request duration.

Find the source of latency

Return to the search results and open a trace lasting approximately 700 ms. The exact duration will vary between runs.

In the captured example, the complete trace took 709.122 ms, while the inventory.lookup span took 703.498 ms. The width of this span shows that the inventory operation accounts for nearly all of the request latency.

Jaeger trace with a slow inventory.lookup span expanded
The inventory.lookup span accounts for almost the entire duration of the slow checkout request.

Select the inventory.lookup span and inspect its attributes and events. The span contains:

inventory.lookup.mode = slow
inventory.sku = camera-013

It also records the following event:

simulated slow database query

These details identify both the operation that caused the delay and the condition under which it occurred. The frontend itself was not performing 700 ms of work; it was waiting for the downstream inventory operation to complete.

Investigate a failed request

Return to the search results and open a trace marked with an error. Expand the inventory spans and select inventory.lookup.

The span has an ERROR status and records an exception similar to:

Inventory database timed out for camera-020
Jaeger trace showing an inventory timeout and propagated HTTP errors
The trace connects the simulated inventory timeout and HTTP 503 response to the HTTP 502 returned by the frontend.

The inventory service records the timeout and returns HTTP 503. The frontend cannot complete the checkout, so it returns HTTP 502 to the client. Following the spans upward shows how one downstream failure affected the complete request.

In this example, the trace contains six spans across two services, with four spans marked as errors. OpenTelemetry context propagation connects these operations into one trace, while the recorded span status and exception provide the information needed to diagnose the failure.

Step 4: Stop the demo

When finished, stop and remove the containers:

docker compose down

Jaeger Best Practices

  • Use consistent service names: Set a stable OTEL_SERVICE_NAME environment variable for each application so traces remain easy to search across environments.
  • Preserve trace context: Verify propagation across HTTP, gRPC, messaging, and asynchronous boundaries to prevent disconnected traces.
  • Add useful span attributes: Record low-cardinality diagnostic details, but exclude credentials, personal data, and unbounded values.
  • Configure sampling carefully: Control trace volume while retaining errors, slow requests, and other diagnostically valuable traces.
  • Use an OpenTelemetry Collector: Add one when traces require batching, retries, enrichment, filtering, or routing.
  • Monitor the tracing pipeline: Track rejected or dropped spans, exporter failures, storage health, and ingestion latency.

Limitations of Jaeger

  • Tracing-focused: Jaeger primarily stores and analyzes traces. It does not provide native storage and analysis for application logs or general-purpose metrics.
  • Additional infrastructure: Production deployments require an external storage backend and operational planning for scaling, retention, backups, and upgrades.
  • Limited cross-signal correlation: Investigating a trace alongside related logs and infrastructure metrics generally requires integration with other observability tools.
  • No native user management: Jaeger UI does not provide user accounts or roles, so restricted access typically requires an authenticated reverse proxy.
  • Dependent on instrumentation quality: Missing spans, broken context propagation, or aggressive sampling can produce incomplete traces and hide the source of an incident.
  • Extra setup for SPM: Service Performance Monitoring can derive metrics from spans, but it requires a separate Prometheus-compatible metrics store.

SigNoz: Open Source Alternative to Jaeger

SigNoz is an all-in-one open-source observability platform built around OpenTelemetry. You can use it to inspect traces, query logs and metrics, monitor application and infrastructure health, and create dashboards and alerts. SigNoz also groups exceptions captured through trace data, making application errors easier to investigate.

For trace analysis, SigNoz provides individual request views using flame graphs and Gantt charts. You can filter spans using attributes such as service name, operation, HTTP status code, deployment environment, or application-specific fields. Trace data can also be aggregated to examine service-level latency, request rates, and errors rather than inspecting requests individually.

Distributed trace with span details displayed in SigNoz
SigNoz displays the request path and duration of each span while retaining the attributes attached by OpenTelemetry.

The Jaeger demo in this article identified inventory.lookup as the source of a 700 ms delay. Jaeger provides the trace evidence needed to locate that operation. In a production incident, the investigation may continue into inventory-service logs, database metrics, container memory, or a recent deployment. SigNoz keeps those signals available in the same product, using their timestamps and OpenTelemetry resource attributes to narrow the search.

SigNoz also provides a RED metrics dashboard out of the box. These views summarize request rate, error rate, and duration for services and endpoints, including latency percentiles such as p50, p90, and p99. This provides a service-level view before you open an individual trace.

The OpenTelemetry instrumentation used in the Jaeger demo is not tied to Jaeger. To send its traces to SigNoz, change the OTLP exporter endpoint and supply the required authentication headers when using SigNoz Cloud. Metrics and logs require their corresponding OpenTelemetry collection to be configured as well.

Jaeger and SigNoz Compared

AreaJaegerSigNoz
Main useCollecting, searching, and visualizing distributed tracesMonitoring applications and infrastructure using traces, metrics, and logs
Trace analysisTrace search, timelines, span details, comparison, and service dependenciesTrace search, flame graphs, attribute filtering, and trace aggregation
Application metricsSpan-derived metrics require additional configuration and a Prometheus-compatible storeService RED metrics and latency percentiles are available in APM views
LogsRequires a separate logging systemLog ingestion, search, filtering, and dashboards
Dashboards and alertsGeneral monitoring requires additional toolsDashboards and alerts use trace, metric, and log data
StorageSupports backends such as OpenSearch, Elasticsearch, and CassandraUses ClickHouse
DeploymentSelf-hostedSigNoz Cloud or self-hosted
InstrumentationAccepts OpenTelemetry traces over OTLPAccepts OpenTelemetry traces, metrics, and logs over OTLP

Jaeger remains a practical choice for teams that need a dedicated distributed tracing backend. Choose SigNoz when traces form one part of the incident workflow and your team also needs application metrics, logs, dashboards, and alerts without operating separate tools for each signal. See the Jaeger alternatives guide for a broader comparison of available tracing backends.

Get Started with SigNoz

SigNoz provides application metrics, distributed tracing, logs, dashboards, and alerts in an OpenTelemetry-native platform. You can use SigNoz Cloud or self-host SigNoz in your own environment.

If your applications already produce OpenTelemetry data, configure their OTLP exporters to send it to SigNoz. Otherwise, follow the instrumentation guides for your language or framework. Start with one service and verify that you can move from an application latency or error signal to its trace and related logs.

Start monitoring with SigNoz Cloud in minutes. Get metrics, traces, logs, dashboards, and alerts without managing the backend.

Get Started - Free

Is this page helpful

Tags
Jaegerdistributed-tracing