Skip to content

OpenTelemetry

Open-source observability framework for generating, collecting, analyzing, and exporting telemetry data (logs, metrics, and traces). Vendor-agnostic standard for observability.

OpenTelemetry Components

ComponentDescription
APIProgramming interfaces for telemetry
SDKLanguage-specific implementations of API
CollectorService to receive, process, and export telemetry
InstrumentationLibraries for instrumenting applications
ExportersSend telemetry to backends (Jaeger, Prometheus, Datadog)
ProcessorsProcess telemetry before export
ReceiversReceive telemetry from external sources

OpenTelemetry Concepts

Signals

SignalDescriptionExamples
TracesRequest lifecycle across servicesHTTP requests, database queries
MetricsNumerical measurements over timeRequest count, CPU usage
LogsText records with metadataApplication logs, error logs

Key Concepts

ConceptDescription
SpanSingle operation or unit of work
TraceTree of spans representing a request
Trace IDUnique identifier for entire request
Span IDUnique identifier for individual span
Parent SpanSpan that initiated another span
Root SpanFirst span in a trace
ContextPropagates trace information across services
BaggageKey-value pairs propagated across spans
AttributesKey-value pairs attached to spans
EventsTimed events within a span
LinksRelationship between spans

OpenTelemetry Installation

Python

bash
# Install SDK and exporters
pip install opentelemetry-api opentelemetry-sdk
pip install opentelemetry-instrumentation-flask
pip install opentelemetry-instrumentation-requests
pip install opentelemetry-exporter-jaeger
pip install opentelemetry-exporter-prometheus
pip install opentelemetry-exporter-otlp

JavaScript/Node.js

bash
# Install SDK and exporters
npm install @opentelemetry/api
npm install @opentelemetry/sdk-node
npm install @opentelemetry/auto-instrumentations-node
npm install @opentelemetry/exporter-trace-jaeger
npm install @opentelemetry/exporter-metrics-prometheus
npm install @opentelemetry/exporter-trace-otlp

Go

bash
# Install SDK and exporters
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/jaeger
go get go.opentelemetry.io/otel/exporters/prometheus
go get go.opentelemetry.io/otel/exporters/otlp

Java

xml
<!-- Maven dependency -->
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-api</artifactId>
  <version>1.0.0</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk</artifactId>
  <version>1.0.0</version>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-jaeger</artifactId>
  <version>1.0.0</version>
</dependency>

OpenTelemetry Tracing (Python)

Basic Tracing Setup

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# Configure tracer provider
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Add console exporter
span_processor = SimpleSpanProcessor(ConsoleSpanExporter())
trace.get_tracer_provider().add_span_processor(span_processor)

# Create a span
with tracer.start_as_current_span("operation"):
    # Do work here
    pass

Jaeger Exporter

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.jaeger.thrift import JaegerExporter

# Configure tracer provider
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Add Jaeger exporter
jaeger_exporter = JaegerExporter(
    agent_host_name="localhost",
    agent_port=6831,
)
span_processor = BatchSpanProcessor(jaeger_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# Create span with attributes
with tracer.start_as_current_span("operation") as span:
    span.set_attribute("operation.name", "my_operation")
    span.set_attribute("operation.type", "sync")

OTLP Exporter

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# Configure tracer provider
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Add OTLP exporter
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4317")
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

Manual Instrumentation

python
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

# Create root span
with tracer.start_as_current_span("parent-operation") as parent_span:
    parent_span.set_attribute("user.id", "123")
    
    # Create child span
    with tracer.start_as_current_span("child-operation") as child_span:
        child_span.set_attribute("db.query", "SELECT * FROM users")
        
        # Add event
        child_span.add_event(
            "database-query-started",
            attributes={"query": "SELECT * FROM users"}
        )
        
        # Do work
        result = query_database()
        
        # Set status
        child_span.set_status(Status(StatusCode.OK))

Automatic Instrumentation (Flask)

python
from flask import Flask
from opentelemetry import trace
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter

app = Flask(__name__)

# Set up tracing
trace.set_tracer_provider(TracerProvider())
FlaskInstrumentor().instrument_app(app)

@app.route('/')
def hello():
    return "Hello, World!"

Automatic Instrumentation (Requests)

python
import requests
from opentelemetry import trace
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.trace import TracerProvider

# Set up tracing
trace.set_tracer_provider(TracerProvider())
RequestsInstrumentor().instrument()

# This request will be automatically traced
response = requests.get('https://api.example.com/data')

OpenTelemetry Metrics (Python)

Basic Metrics Setup

python
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider

# Configure meter provider
metrics.set_meter_provider(MeterProvider())
meter = metrics.get_meter(__name__)

# Create a counter
counter = meter.create_counter(
    "requests",
    description="Number of requests",
)

# Record a metric
counter.add(1, {"endpoint": "/api/users", "method": "GET"})

Gauge

python
# Create a gauge
gauge = meter.create_gauge(
    "cpu_usage",
    description="CPU usage percentage",
)

# Record a gauge value
gauge.set(75.5, {"host": "web-01", "cpu": "cpu0"})

Histogram

python
# Create a histogram
histogram = meter.create_histogram(
    "request_duration",
    description="Request duration in seconds",
)

# Record a histogram value
histogram.record(0.123, {"endpoint": "/api/users", "method": "GET"})

Prometheus Exporter

python
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export.controller import PushController
from opentelemetry.exporter.prometheus import PrometheusMetricReader

# Set up Prometheus exporter
reader = PrometheusMetricReader()
metrics.set_meter_provider(MeterProvider(metric_readers=[reader]))
meter = metrics.get_meter(__name__)

# Create and record metrics
counter = meter.create_counter("requests")
counter.add(1, {"endpoint": "/"})

OpenTelemetry Logs (Python)

Basic Logs Setup

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk._logs import LoggerProvider
from opentelemetry.sdk._logs.export import SimpleLogRecordProcessor
from opentelemetry.sdk._logs.export import ConsoleLogRecordExporter

# Set up logging
trace.set_tracer_provider(TracerProvider())
logger_provider = LoggerProvider()
log_processor = SimpleLogRecordProcessor(ConsoleLogRecordExporter())
logger_provider.add_log_record_processor(log_processor)

# Create logger
logger = logger_provider.get_logger(__name__)

# Log an event
logger.info("User logged in", attributes={"user_id": "123", "ip": "192.168.1.1"})

OpenTelemetry Collector

Collector Architecture

Receivers → Processors → Exporters
ComponentDescriptionExamples
ReceiversReceive telemetry from external sourcesOTLP, Jaeger, Prometheus, Zipkin
ProcessorsProcess and transform telemetryBatch, Attributes, Sampling
ExportersSend telemetry to backendsJaeger, Prometheus, Datadog, OTLP

Collector Installation

bash
# Download collector binary
curl -LO https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.68.0/otelcol_0.68.0_linux_amd64.tar.gz
tar -xzf otelcol_0.68.0_linux_amd64.tar.gz

# Run collector
./otelcol --config=config.yaml

Collector Configuration (YAML)

yaml
receivers:
  # Receive OTLP traces
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

  # Receive Prometheus metrics
  prometheus:
    config:
      scrape_configs:
        - job_name: 'otel-collector'
          scrape_interval: 10s
          static_configs:
            - targets: ['0.0.0.0:8888']

processors:
  # Batch traces
  batch:
    timeout: 5s
    max_queue_size: 1000

  # Add attributes
  attributes:
    actions:
      - key: environment
        value: production
        action: insert

exporters:
  # Export to Jaeger
  jaeger:
    endpoint: jaeger:14250
    tls:
      insecure: true

  # Export to Prometheus
  prometheus:
    endpoint: "0.0.0.0:8889"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, attributes]
      exporters: [jaeger]

    metrics:
      receivers: [otlp, prometheus]
      processors: [batch]
      exporters: [prometheus]

Collector Exporters

ExporterDescriptionConfiguration
JaegerExport traces to Jaegerjaeger: endpoint: localhost:14250
PrometheusExport metrics to Prometheusprometheus: endpoint: 0.0.0.0:8889
DatadogExport to Datadogdatadog: api: { key: "your-api-key" }
OTLPExport to OTLP endpointotlp: endpoint: http://localhost:4317
ZipkinExport traces to Zipkinzipkin: endpoint: http://localhost:9411/api/v2/spans
LoggingExport to logslogging: loglevel: debug

OpenTelemetry Best Practices

  1. Use Automatic Instrumentation - Start with auto-instrumentation libraries
  2. Use Semantic Conventions - Follow naming standards for attributes
  3. Sample Appropriately - Don't sample 100% in production
  4. Use Context Propagation - Propagate context across services
  5. Add Meaningful Attributes - Add business context to spans
  6. Use Spans for Long Operations - Break long operations into child spans
  7. Add Events to Spans - Mark important moments in spans
  8. Use Baggage for Propagation - Propagate user context
  9. Configure Exporters Carefully - Choose appropriate exporters
  10. Monitor Collector Health - Monitor collector resource usage

OpenTelemetry vs Other Tools

FeatureOpenTelemetryJaegerPrometheusDatadog
Vendor AgnosticYesNoNoNo
TracingYesYesNoYes
MetricsYesNoYesYes
LogsYesNoNoYes
Language SupportMultipleMultipleGo/JavaMultiple
Backend RequiredYesYesYesYes
Open SourceYesYesYesNo
StandardCNCF standardToolToolSaaS

OpenTelemetry Semantic Conventions

Common Span Attributes

AttributeDescriptionExample
http.methodHTTP methodGET, POST
http.urlFull HTTP URLhttps://api.example.com/users
http.status_codeHTTP status code200, 404
http.schemeHTTP schemehttp, https
http.hostHTTP hostapi.example.com
db.systemDatabase systempostgresql, mysql
db.nameDatabase namemydb
db.operationDatabase operationSELECT, INSERT
net.peer.nameRemote hostnamedb.example.com
net.peer.portRemote port5432

Common Metric Attributes

AttributeDescriptionExample
service.nameService namemy-service
service.versionService version1.0.0
deployment.environmentDeployment environmentproduction
host.nameHostnameweb-01

OpenTelemetry Resources

Resource Configuration

python
from opentelemetry.sdk.resources import Resource, SERVICE_NAME

# Create resource
resource = Resource.create({
    SERVICE_NAME: "my-service",
    "service.version": "1.0.0",
    "deployment.environment": "production",
    "host.name": "web-01"
})

# Use resource with tracer provider
from opentelemetry.sdk.trace import TracerProvider

provider = TracerProvider(resource=resource)
trace.set_tracer_provider(provider)

Common Use Cases

Use Case: Microservices Tracing

python
# Service A
import requests
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

@tracer.start_as_current_span("process-request")
def handle_request(user_id):
    with tracer.start_as_current_span("fetch-user") as span:
        span.set_attribute("user.id", user_id)
        response = requests.get(f"http://service-b/users/{user_id}")
        return response.json()

# Service B
@tracer.start_as_current_span("get-user")
def get_user(user_id):
    # Query database
    with tracer.start_as_current_span("query-database") as span:
        span.set_attribute("db.system", "postgresql")
        return query_db(user_id)

Use Case: Metrics Collection

python
from opentelemetry import metrics

meter = metrics.get_meter(__name__)

# Request counter
request_counter = meter.create_counter("http_requests")

@app.route("/api/users")
def get_users():
    request_counter.add(1, {"endpoint": "/api/users", "method": "GET"})
    return jsonify(users=[])

# Request duration histogram
duration_histogram = meter.create_histogram("http_request_duration")

@app.route("/api/users")
def get_users():
    start_time = time.time()
    result = jsonify(users=[])
    duration_histogram.record(time.time() - start_time)
    return result

Useful Tips

Debugging Tracing

python
# Enable debug logging
import logging
logging.basicConfig(level=logging.DEBUG)

# Export to console for debugging
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor

exporter = ConsoleSpanExporter()
processor = SimpleSpanProcessor(exporter)
trace.get_tracer_provider().add_span_processor(processor)

Sampling Configuration

python
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased

# Configure 10% sampling
tracer_provider = TracerProvider(
    sampler=TraceIdRatioBased(0.1)
)
trace.set_tracer_provider(tracer_provider)

Batching Configuration

python
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Configure batch processor
batch_processor = BatchSpanProcessor(
    exporter,
    max_queue_size=2048,
    schedule_delay_millis=5000,
    max_export_batch_size=512
)

Common Issues

IssueSolution
Traces not appearingCheck exporter configuration, network connectivity
High CPU usageReduce sampling rate, adjust batch size
Missing contextEnsure context propagation is enabled
Spans not linkedCheck trace ID propagation

OpenTelemetry Collector Commands

COMMANDDESCRIPTION
otelcol --config config.yamlRun collector with config file
otelcol --config=config.yaml --health-checkRun with health check
otelcol --versionShow collector version
otelcol validate config.yamlValidate collector configuration

Released under MIT License.