Skip to content

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.

  • A Celery application with a broker configured (Redis, RabbitMQ, or similar).
  • Python 3.8 or newer.
  • Your Osuite ingest token and region.
  1. Install the distro and OTLP exporter, then let bootstrap add the Celery instrumentor.

    Terminal window
    pip install opentelemetry-distro opentelemetry-exporter-otlp
    opentelemetry-bootstrap -a install
  2. Create telemetry.py and connect the SDK setup to worker_process_init. This runs once in each forked worker, after the fork.

    import logging
    from celery.signals import worker_process_init
    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
    from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
    from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
    from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
    from 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()
  3. Import telemetry from the module that defines your Celery app so the signal is registered before workers start.

    import telemetry # noqa: F401
    from celery import Celery
    app = Celery("tasks", broker="redis://localhost:6379/0")

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.

Terminal window
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=INFO

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.

No data in Osuite after a few minutes? Work through these checks.

  • Endpoint — confirm the exporter targets exactly ingest.<region>.osuite.io:443 for 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/osuite exporter 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.