Direct OTLP Configuration for OpenTelemetry Python

This document shows how to export telemetry to Uptrace using the OTLP exporter directly. If you prefer the wrapper, see Getting started with OpenTelemetry Python.

Direct OTLP Configuration

If you prefer to use the OTLP exporter directly or are already using OpenTelemetry exporters, you can configure OpenTelemetry manually without the uptrace-python wrapper.

For more details on the OpenTelemetry APIs used below, see:

Uptrace fully supports the OpenTelemetry Protocol (OTLP) over both gRPC and HTTP transports.
If you already have an OTLP exporter configured, you can continue using it with Uptrace by simply pointing it to the Uptrace OTLP endpoint.

Connecting to Uptrace

Choose an OTLP endpoint from the table below and pass your DSN via the uptrace-dsn header for authentication:

TransportEndpointPort
gRPChttps://api.uptrace.dev:43174317
HTTPhttps://api.uptrace.dev443

When using HTTP transport, you often need to specify the full URL for each signal type:

  • https://api.uptrace.dev/v1/traces
  • https://api.uptrace.dev/v1/logs
  • https://api.uptrace.dev/v1/metrics

Note: Most OpenTelemetry SDKs support both transports. Use HTTP unless you're already familiar with gRPC.

For performance and reliability, we recommend:

  • Use BatchSpanProcessor and BatchLogProcessor for batching spans and logs, reducing the number of export requests.
  • Enable gzip compression to reduce bandwidth usage.
  • Prefer delta metrics temporality (Uptrace converts cumulative metrics automatically).
  • Use Protobuf encoding instead of JSON (Protobuf is more efficient and widely supported).
  • Use HTTP transport for simplicity and fewer configuration issues (unless you're already familiar with gRPC).
  • Optionally, use the AWS X-Ray ID generator to produce trace IDs compatible with AWS X-Ray.

Common Environment Variables

You can use environment variables to configure resource attributes and propagators::

VariableDescription
OTEL_RESOURCE_ATTRIBUTESComma-separated resource attributes, e.g., service.name=myservice,service.version=1.0.0.
OTEL_SERVICE_NAME=myserviceSets the service.name attribute (overrides OTEL_RESOURCE_ATTRIBUTES).
OTEL_PROPAGATORSComma-separated list of context propagators (default: tracecontext,baggage).

Most language SDKs allow configuring the OTLP exporter entirely via environment variables:

shell
# Endpoint (choose HTTP or gRPC)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev"         # HTTP
#export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev:4317"   # gRPC

# Pass DSN for authentication
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<FIXME>"

# Performance optimizations
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA

Configure BatchSpanProcessor to balance throughput and payload size:

shell
export OTEL_BSP_EXPORT_TIMEOUT=10000         # Max export timeout (ms)
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=10000  # Avoid >32MB payloads
export OTEL_BSP_MAX_QUEUE_SIZE=30000         # Adjust for available memory
export OTEL_BSP_MAX_CONCURRENT_EXPORTS=2     # Parallel exports

Exporting Traces

This example shows how to configure the OTLP trace exporter to send distributed traces to Uptrace. For information on creating spans and using the tracing API, see OpenTelemetry Python Tracing.

python
#!/usr/bin/env python3

import os
import grpc
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# Get DSN from environment variable
dsn = os.getenv("UPTRACE_DSN")
if not dsn:
    raise ValueError("UPTRACE_DSN environment variable is required")

print(f"Using DSN: {dsn}")

# Configure resource attributes
resource = Resource.create({
    ResourceAttributes.SERVICE_NAME: "myservice",
    ResourceAttributes.SERVICE_VERSION: "1.0.0",
})

# Create tracer provider
tracer_provider = TracerProvider(resource=resource)
trace.set_tracer_provider(tracer_provider)

# Configure OTLP exporter
exporter = OTLPSpanExporter(
    endpoint="https://api.uptrace.dev:4317",
    headers={"uptrace-dsn": dsn},
    timeout=30,
    compression=grpc.Compression.Gzip,
)

# Set up batch span processor
span_processor = BatchSpanProcessor(
    exporter,
    max_queue_size=1000,
    max_export_batch_size=1000,
)
tracer_provider.add_span_processor(span_processor)

# Create and use tracer
tracer = trace.get_tracer("app_or_package_name", "1.0.0")

with tracer.start_as_current_span("main") as span:
    trace_id = span.get_span_context().trace_id
    print(f"Trace ID: {trace_id:032x}")

# Ensure all spans are exported before shutdown
trace.get_tracer_provider().shutdown()

Exporting Metrics

This example shows how to configure the OTLP metrics exporter to send OpenTelemetry metrics to Uptrace. For information on creating counters, histograms, and observers, see OpenTelemetry Python Metrics.

python
#!/usr/bin/env python3

import os
import time
import grpc
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import Resource
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

# Get DSN from environment variable
dsn = os.getenv("UPTRACE_DSN")
if not dsn:
    raise ValueError("UPTRACE_DSN environment variable is required")

print(f"Using DSN: {dsn}")

# Configure OTLP metric exporter
exporter = OTLPMetricExporter(
    endpoint="https://api.uptrace.dev:4317",
    headers={"uptrace-dsn": dsn},
    timeout=30,
    compression=grpc.Compression.Gzip,
)

# Set up periodic metric reader
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=60000)

# Configure resource and meter provider
resource = Resource.create({
    ResourceAttributes.SERVICE_NAME: "myservice",
    ResourceAttributes.SERVICE_VERSION: "1.0.0",
})

provider = MeterProvider(metric_readers=[reader], resource=resource)
metrics.set_meter_provider(provider)

# Create and use meter
meter = metrics.get_meter("app_or_package_name", "1.0.0")
counter = meter.create_counter("requests_total", description="Total number of requests")

# Generate sample metrics
while True:
    counter.add(1, attributes={"status": "success"})
    time.sleep(1)

Exporting Logs

This example shows how to configure the OTLP logs exporter to send logs to Uptrace. The logs are correlated with traces using the active span context.

python
#!/usr/bin/env python3

import os
import logging
import grpc
from opentelemetry._logs import set_logger_provider
from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.semconv.resource import ResourceAttributes

# Get DSN from environment variable
dsn = os.getenv("UPTRACE_DSN")
if not dsn:
    raise ValueError("UPTRACE_DSN environment variable is required")

print(f"Using DSN: {dsn}")

# Configure resource attributes
resource = Resource.create({
    ResourceAttributes.SERVICE_NAME: "myservice",
    ResourceAttributes.SERVICE_VERSION: "1.0.0",
})

# Set up logger provider
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)

# Configure OTLP log exporter
exporter = OTLPLogExporter(
    endpoint="https://api.uptrace.dev:4317",
    headers={"uptrace-dsn": dsn},
    timeout=30,
    compression=grpc.Compression.Gzip,
)

# Add log record processor
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))

# Set up logging handler
handler = LoggingHandler(level=logging.INFO, logger_provider=logger_provider)
logging.getLogger().addHandler(handler)

# Create and use logger
logger = logging.getLogger("myapp.area1")
logger.error("Houston, we have a problem.")

# Ensure all logs are exported before shutdown
logger_provider.shutdown()

What's next?