Skip to content

Setup and Lifecycle

How Telemetry Reaches the Backend

Application code records telemetry through the OpenTelemetry API. The SDK processes those records and measurements, and an exporter sends them to a Collector or directly to a backend.

Component Responsibility
Provider Supplies tracers, meters, or loggers and owns their SDK configuration
Span or log processor Handles records, commonly buffering them for batch export
Metric reader Collects aggregated measurements, commonly at regular intervals
Exporter Sends a signal to its configured destination
Resource Identifies the service or instance producing telemetry

Application startup owns these components and their shutdown. Reusable libraries generally depend only on the API and leave SDK configuration to the host application.

Dependencies

An application needs opentelemetry-api, opentelemetry-sdk, and an exporter for each enabled signal. The opentelemetry-instrumentation-* packages, often called contrib packages, add integrations for specific frameworks and clients. Install only the integrations the application uses.

For an application using uv and HTTP/protobuf export:

 uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http

Lock compatible package versions. Core packages use stable version numbers while many contrib packages use beta versions, so matching version strings is not a compatibility rule. Use documentation that matches the installed packages, especially for logging integration and newer metric instruments.

Resource and Transport Configuration

Resource describes the service or instance producing telemetry, not the current request. Use the same resource for trace, metric, and log providers so the backend can associate their output. Set a deliberate service name; add version, deployment environment, and instance identity where available. User and request identifiers do not belong in the resource.

The following settings identify the service and select a local HTTP/protobuf destination:

 export OTEL_SERVICE_NAME=checkout
 export OTEL_RESOURCE_ATTRIBUTES='service.version=1.0.0,deployment.environment.name=development'
 export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
 export OTEL_PROPAGATORS=tracecontext,baggage

OTLP supports two transports. Their endpoint forms differ:

Transport Usual port Endpoint form
HTTP/protobuf 4318 A base URL in OTEL_EXPORTER_OTLP_ENDPOINT; a full signal path in a signal-specific setting or explicit endpoint= argument
gRPC 4317 A service endpoint without an HTTP signal path

HTTP signal paths are /v1/traces, /v1/metrics, and /v1/logs. Importing an HTTP exporter selects that transport; setting a protocol environment variable does not turn it into a gRPC exporter.

Use TLS and authenticated export in production. Supply authentication headers through deployment secrets rather than embedding credentials in code. Check which environment variables the chosen exporter supports. These settings configure components when they are created; they do not create providers or install instrumentation by themselves.

Minimal Trace and Metric Setup

This example creates providers using the environment settings above. Run it once per process during startup, before instrumented work begins. Do not run it in a process whose SDK is already configured by opentelemetry-instrument.

from opentelemetry import metrics, trace
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

resource = Resource.create({})
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter())
metric_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
trace.set_tracer_provider(trace_provider)
metrics.set_meter_provider(metric_provider)

Keep references to the providers for teardown. force_flush requests export of pending telemetry at a checkpoint; it does not stop the provider. shutdown finishes the provider's lifecycle and releases its export components. Neither operation guarantees that the backend has indexed the data.

Global provider setters establish process-wide configuration; they are not a way to replace it repeatedly. Keep setup out of modules that might run it again. In tests, use providers local to the test rather than replacing global providers.

Frameworks, Workers, and CLIs

An instrumentor installs telemetry hooks into a framework or client. Server integrations normally create spans for incoming requests; client integrations create spans for outgoing calls and transmit trace context. Add attributes to these existing spans rather than creating a second span for the same HTTP or database operation. Use custom spans for distinct business work.

Check the integration's supported dependency versions, excluded operations, and customization hooks. Also check its semantic conventions: these define standard names and meanings for telemetry fields, and may change between releases.

With zero-code instrumentation, a launcher configures the SDK and installs integrations before starting the application. Manual API calls can still add business spans and metrics. Do not also configure global providers or explicitly instrument the same library.

For servers that fork worker processes, initialize exporters and their background threads in each worker after the fork, using the server's lifecycle hooks. Each process exports its own measurements; the backend combines them. A process-local measurement must not be described as a global queue size. Check context propagation separately for the actual thread-pool or task-queue integration.

Shutdown

Stop accepting work, allow active operations to finish, record final logs and measurements, then shut down the providers. Run blocking flush and shutdown calls outside an async application's event-loop thread. Short-lived CLIs need final export before exit rather than relying on the next periodic collection.

Abrupt process termination may lose pending telemetry. SDK export is best-effort delivery, not a durable audit channel.

Collector and Delivery Diagnosis

In the Collector, a receiver accepts incoming telemetry and each signal pipeline processes and exports it. Configure an OTLP receiver and separate pipelines for traces, metrics, and logs as needed. Enabling one pipeline does not enable the others.

Production pipelines typically limit memory, batch records, remove unwanted or sensitive fields, and authenticate to the backend. Keep debug output local because it may expose telemetry contents.

When telemetry is missing, locate the stage where it disappears:

  • Recording: Is the SDK configured, is instrumentation enabled, and did the application record anything?
  • Selection and collection: Was the trace sampled out, is the span still open, or did a logger level suppress the log?
  • Export: Are records still buffered, or is the endpoint using the wrong protocol or signal path?
  • Collector and backend: Is the signal pipeline enabled, did the Collector receive the data, and did the backend index it?

Use console exporters for local inspection, but never write to stdout when it carries a machine protocol such as MCP stdio. Test Collector outages and configure limits for export timeouts, queue sizes, and shutdown waits. Exporter errors need a diagnostic destination that does not send them back through the same failing exporter.