OpenTelemetry Telemetry Setup Guide
Overview
Docling Pipelines supports OpenTelemetry (OTEL) for distributed tracing and observability. This guide covers installation, configuration, and usage of the telemetry features.
Features
- Automatic HTTP Request Tracing: All API requests are automatically traced with latency metrics
- Operator Execution Tracing: Track execution time, success rate, and error counts per operator
- Metrics Collection: HTTP and operator metrics exported via OTLP alongside traces
- Log-Trace Correlation: Every JSON log record includes
trace_idandspan_idfor direct navigation from logs to traces - Head-Based Sampling: Reduce trace volume in high-traffic environments via
OTEL_TRACES_SAMPLER_ARG - Zero Overhead When Disabled: No performance impact when telemetry is disabled
- Vendor Agnostic: Works with any OTLP-compatible monitoring backend
- Non-Breaking: Optional feature that doesn't affect existing functionality
Prerequisites
- Python 3.12+
- Docling Pipelines installed
- OTLP-compatible monitoring backend (Jaeger, Grafana Tempo, Datadog, etc.)
Installation
1. Install Telemetry Dependencies
# Install Docling Pipelines with telemetry support
uv pip install -e ".[telemetry]"
This installs the following packages:
- opentelemetry-api - Core OTEL API
- opentelemetry-sdk - OTEL SDK implementation
- opentelemetry-exporter-otlp-proto-grpc - OTLP gRPC exporter
- opentelemetry-instrumentation-fastapi - FastAPI auto-instrumentation
2. Choose a Monitoring Backend
Docling Pipelines works with any OTLP-compatible backend. Popular options:
- Jaeger (recommended for local development)
- Grafana Tempo
- Datadog
- New Relic
- AWS X-Ray (via OTEL Collector)
- Google Cloud Trace (via OTEL Collector)
Quick Start with Jaeger (Local Development)
Option 1: Docker Compose (Recommended)
Start Jaeger and Docling Pipelines API together:
# From project root
docker-compose -f docker/docker-compose.telemetry.yml up -d
# Verify services are running
docker-compose -f docker/docker-compose.telemetry.yml ps
Access services:
- Jaeger UI: http://localhost:16686
- Docling Pipelines API: http://localhost:8000
- API Docs: http://localhost:8000/docs
Test telemetry:
# Make a request
curl http://localhost:8000/api/v1/operators
# View traces in Jaeger UI
open http://localhost:16686
Stop services:
docker-compose -f docker/docker-compose.telemetry.yml down
Option 2: Jaeger Only (Docker)
If you want to run Jaeger separately:
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
2. Configure Environment Variables
Create or update your .env file:
# Enable telemetry
TELEMETRY_ENABLED=true
# Service identification
OTEL_SERVICE_NAME=docling-pipelines-dev
OTEL_SERVICE_VERSION=0.1.0
OTEL_DEPLOYMENT_ENVIRONMENT=development
# OTLP endpoint (Jaeger)
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
3. Run Docling Pipelines
Run any flow normally - telemetry will be automatically enabled:
# CLI
docling-pipelines --flow-file sample_flows/quickstart/basic_ingest_extract.json
# API
docling-pipelines-api
4. View Traces
Open Jaeger UI in your browser:
http://localhost:16686
Select "docling-pipelines-dev" from the service dropdown and click "Find Traces".
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
TELEMETRY_ENABLED |
Master switch — enables both traces and metrics | false |
OTEL_SERVICE_NAME |
Service name in traces/metrics | docling-pipelines |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP endpoint URL | http://localhost:4317 |
OTEL_SERVICE_VERSION |
Service version | 0.1.0 |
OTEL_DEPLOYMENT_ENVIRONMENT |
Environment name | development |
OTEL_EXPORTER_OTLP_HEADERS |
Authentication headers (e.g. Authorization=Basic ...) |
(none) |
OTEL_TRACES_SAMPLER_ARG |
Head-based sampling rate, 0.0–1.0 (1.0 = 100%) | 1.0 |
OTEL_METRIC_EXPORT_INTERVAL |
Metrics flush interval in milliseconds | 60000 |
Deployment-Level Configuration
Telemetry is configured once at deployment level, not per-flow:
Docker Compose:
services:
docling-pipelines:
environment:
- TELEMETRY_ENABLED=true
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
Kubernetes:
env:
- name: TELEMETRY_ENABLED
value: "true"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4317"
What Gets Collected
HTTP Requests (Automatic)
All API requests are automatically traced and measured:
Spans — attributes captured:
- http.method, http.url, http.scheme, http.target
- http.status_code, transaction.id, error
Metrics — instruments:
- http.server.request.count — counter, labelled by method / route / status code
- http.server.request.duration — histogram in milliseconds
Operator Execution (Automatic)
All operator executions are automatically traced and measured:
Spans — attributes captured:
- operator.name, operator.short_name, operator.category
- job.id, job_run.id
- operator.processed_docs, operator.failed_docs, operator.total_docs
Metrics — instruments:
- operator.execution.count — counter, labelled by operator name / category / success
- operator.execution.duration — histogram in milliseconds
- operator.error.count — counter, labelled by operator name / category
Log-Trace Correlation (Automatic)
When JSON logging is enabled (DS_LOG_JSON=True) and telemetry is active, every log record includes:
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7"
}
Use these fields in Grafana to jump directly from a log line to its trace.
Example Trace
Flow Execution
├── HTTP POST /api/v1/flows/execute
│ ├── operator.IngestSourceOperator
│ │ └── processed_docs: 10
│ ├── operator.ExtractOperator
│ │ └── processed_docs: 10
│ └── operator.EmbeddingsOperator
│ └── processed_docs: 10
Production Deployment
1. Use OTEL Collector
For production, route traces through an OTEL Collector:
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector:latest
ports:
- "4317:4317" # OTLP gRPC
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
command: ["--config=/etc/otel-collector-config.yaml"]
docling-pipelines:
environment:
- TELEMETRY_ENABLED=true
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
2. Configure Sampling
Docling Pipelines supports head-based sampling natively — no collector required:
# Sample 10% of traces (reduces both trace volume and exporter overhead)
OTEL_TRACES_SAMPLER_ARG=0.1
For tail-based sampling or more advanced strategies, route through an OTEL Collector:
# otel-collector-config.yaml
processors:
probabilistic_sampler:
sampling_percentage: 10
3. Security
For production deployments:
- Use TLS: Configure secure OTLP endpoints
- Authentication: Add authentication headers if required
- Network Policies: Restrict access to telemetry endpoints
Monitoring Backends
Jaeger
# Local
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# Production
OTEL_EXPORTER_OTLP_ENDPOINT=https://jaeger.example.com:4317
Grafana Cloud (Managed Tempo)
Grafana Cloud provides managed observability with Tempo for distributed tracing.
1. Get Credentials
- Log in to Grafana Cloud
- Navigate to Connections → Add new connection → OpenTelemetry
- Note your credentials:
- Endpoint:
https://otlp-gateway-{region}.grafana.net/otlp - Instance ID: Your numeric instance ID (e.g.,
123456) - API Token: Generate with "MetricsPublisher" role (starts with
glc_)
2. Encode Credentials
Grafana Cloud requires Basic authentication:
# Format: instance_id:api_token
echo -n "123456:glc_your_api_token_here" | base64
# Output: MTIzNDU2OmdsY195b3VyX2FwaV90b2tlbl9oZXJl
3. Configure Environment
# Enable telemetry
TELEMETRY_ENABLED=true
# Service identification
OTEL_SERVICE_NAME=docling-pipelines-prod
OTEL_DEPLOYMENT_ENVIRONMENT=production
# Grafana Cloud endpoint (replace region: prod-us-east-0, prod-eu-west-0, etc.)
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-0.grafana.net/otlp
# Authentication (use your base64-encoded credentials)
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic {base64_encoded_credentials}
4. Docker Compose Example
services:
docling-pipelines:
environment:
- TELEMETRY_ENABLED=true
- OTEL_SERVICE_NAME=docling-pipelines-prod
- OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-0.grafana.net/otlp
- OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic MTIzNDU2OmdsY195b3VyX2FwaV90b2tlbl9oZXJl
5. Kubernetes with Secret
apiVersion: v1
kind: Secret
metadata:
name: grafana-cloud-creds
type: Opaque
stringData:
auth-header: "Authorization=Basic MTIzNDU2OmdsY195b3VyX2FwaV90b2tlbl9oZXJl"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: docling-pipelines
spec:
template:
spec:
containers:
- name: app
env:
- name: TELEMETRY_ENABLED
value: "true"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "https://otlp-gateway-prod-us-east-0.grafana.net/otlp"
- name: OTEL_EXPORTER_OTLP_HEADERS
valueFrom:
secretKeyRef:
name: grafana-cloud-creds
key: auth-header
6. View Traces
- Go to your Grafana Cloud instance
- Navigate to Explore → Select Tempo data source
- Query with TraceQL:
{ service.name="docling-pipelines-prod" }
Self-Hosted Grafana Tempo
OTEL_EXPORTER_OTLP_ENDPOINT=https://tempo.example.com:4317
Datadog
# Via OTEL Collector with Datadog exporter
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
AWS X-Ray
# Via OTEL Collector with X-Ray exporter
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
Troubleshooting
Traces Not Appearing
-
Check telemetry is enabled:
bash echo $TELEMETRY_ENABLED # Should output: true -
Verify OTLP endpoint is reachable:
bash curl http://localhost:4317 -
Check logs for initialization:
bash # Look for: "Telemetry initialized successfully" docling-pipelines --flow-file flow.json 2>&1 | grep -i telemetry
Performance Issues
-
Disable telemetry:
bash TELEMETRY_ENABLED=false -
Reduce trace volume with head-based sampling:
bash OTEL_TRACES_SAMPLER_ARG=0.1 # sample 10% -
Increase metrics export interval to reduce exporter frequency:
bash OTEL_METRIC_EXPORT_INTERVAL=120000 # flush every 2 minutes -
Check OTLP endpoint latency
Missing Dependencies
If you see warnings about missing OpenTelemetry packages:
# Reinstall with telemetry dependencies
uv pip install -e ".[telemetry]"
Verification
1. Check Telemetry Status
from docpipe.utils.infrastructure import get_telemetry_service
telemetry = get_telemetry_service()
print(f"Telemetry enabled: {telemetry.is_enabled}")
print(f"Metrics enabled: {telemetry.metrics_enabled}")
2. Test with Sample Flow
# Run a simple flow
docling-pipelines --flow-file sample_flows/quickstart/complete_pipeline_ollama.json
# Check Jaeger UI for traces
open http://localhost:16686
3. Verify Span Attributes
In Jaeger UI, click on a trace and verify:
- HTTP spans have http.method, http.url, http.status_code
- Operator spans have operator.name, operator.category
- Transaction IDs are present
4. Verify Metrics
In Grafana, query the Prometheus/OTLP data source:
http_server_request_count_total
operator_execution_count_total
operator_error_count_total
Best Practices
- Use Descriptive Service Names: Set
OTEL_SERVICE_NAMEto identify your instance - Tag Environments: Use
OTEL_DEPLOYMENT_ENVIRONMENTto distinguish dev/staging/prod - Enable Head-Based Sampling in Production: Set
OTEL_TRACES_SAMPLER_ARG=0.1for 10% sampling - Correlate Logs and Traces: Enable
DS_LOG_JSON=Trueto gettrace_id/span_idin log records - Secure Endpoints: Use TLS and authentication for production OTLP endpoints
- Monitor Metrics Alongside Traces: Use
operator.error.countto alert on operator failures
Additional Resources
Support
For issues or questions:
1. Review logs for telemetry-related messages
2. Verify OTLP endpoint connectivity
3. Ensure telemetry dependencies are installed