Celery OpenTelemetry instrumentation
Instrument Celery workers with OpenTelemetry so each task runs in its own trace, linked to the request that enqueued it. Celery’s default prefork pool forks worker processes after startup, so the SDK must be configured inside each worker rather than once at import time. This guide configures telemetry from the worker_process_init signal.
Prerequisites
Section titled “Prerequisites”- A Celery application with a broker configured (Redis, RabbitMQ, or similar).
- Python 3.8 or newer.
- Your Osuite ingest token and region.
Install and configure
Section titled “Install and configure”-
Install the distro and OTLP exporter, then let bootstrap add the Celery instrumentor.
Terminal window pip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a install -
Create
telemetry.pyand connect the SDK setup toworker_process_init. This runs once in each forked worker, after the fork.import loggingfrom celery.signals import worker_process_initfrom opentelemetry import tracefrom opentelemetry.sdk.trace import TracerProviderfrom opentelemetry.sdk.trace.export import BatchSpanProcessorfrom opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporterfrom opentelemetry.sdk._logs import LoggerProvider, LoggingHandlerfrom opentelemetry.sdk._logs.export import BatchLogRecordProcessorfrom opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporterfrom opentelemetry.instrumentation.celery import CeleryInstrumentor@worker_process_init.connect(weak=False)def init_telemetry(*args, **kwargs):tracer_provider = TracerProvider()tracer_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))trace.set_tracer_provider(tracer_provider)logger_provider = LoggerProvider()logger_provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))logging.getLogger().addHandler(LoggingHandler(logger_provider=logger_provider))CeleryInstrumentor().instrument() -
Import
telemetryfrom the module that defines your Celery app so the signal is registered before workers start.import telemetry # noqa: F401from celery import Celeryapp = Celery("tasks", broker="redis://localhost:6379/0")
Run the worker
Section titled “Run the worker”The worker_process_init handler configures telemetry, so start the worker with your normal command. Set the exporter environment variables in the worker’s environment.
export OTEL_SERVICE_NAME="<service-name>"export OTEL_RESOURCE_ATTRIBUTES="service.environment=production"export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443"export OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>"
celery -A tasks worker --loglevel=INFOFROM python:3.12-slim
WORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt opentelemetry-distro opentelemetry-exporter-otlp \ && opentelemetry-bootstrap -a installCOPY . .
CMD ["celery", "-A", "tasks", "worker", "--loglevel=INFO"]Pass the exporter variables at runtime:
docker run \ -e OTEL_SERVICE_NAME="<service-name>" \ -e OTEL_RESOURCE_ATTRIBUTES="service.environment=production" \ -e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.osuite.io:443" \ -e OTEL_EXPORTER_OTLP_HEADERS="x-osuite-ingest-token=<your-ingest-token>" \ myimageapiVersion: apps/v1kind: Deploymentmetadata: name: celery-workerspec: template: spec: containers: - name: worker image: myimage command: ["celery", "-A", "tasks", "worker", "--loglevel=INFO"] env: - name: OTEL_SERVICE_NAME value: "<service-name>" - name: OTEL_RESOURCE_ATTRIBUTES value: "service.environment=production" - name: OTEL_EXPORTER_OTLP_ENDPOINT value: "https://ingest.<region>.osuite.io:443" - name: OTEL_EXPORTER_OTLP_HEADERS value: "x-osuite-ingest-token=<your-ingest-token>"Framework gotchas
Section titled “Framework gotchas”Verify in Osuite
Section titled “Verify in Osuite”What you should see
Enqueue a task. A trace for the task appears in APM within a minute, showing the task name and any database or HTTP spans inside it. If the producer is instrumented, the task span is nested under the request that scheduled it. Task logs appear in the Logs explorer with the matching trace ID.
Troubleshooting
Section titled “Troubleshooting”No data in Osuite after a few minutes? Work through these checks.
- Endpoint — confirm the exporter targets exactly
ingest.<region>.osuite.io:443for your region, over TLS. - Token — confirm the ingest header carries a valid
<your-ingest-token>and has not been rotated. - Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the
otlp/osuiteexporter attached. - Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
- Network — confirm the host has outbound access to the ingest endpoint on port
443. - Timing — allow up to a minute for the first data to appear before assuming a failure.
Still stuck? Ask the Investigation Agent or contact support.