Metadata-Version: 2.5
Name: dash0-opentelemetry
Version: 0.3.2
Summary: Dash0 OpenTelemetry distribution for Python
Author: Dash0
License-Expression: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: asgiref==3.12.1
Requires-Dist: dash0-opentelemetry-configuration==0.65b0.post1
Requires-Dist: dash0-opentelemetry-exporter-otlp-pyproto-grpc==1.44.0.post1
Requires-Dist: dash0-opentelemetry-exporter-otlp-pyproto-http==1.44.0.post1
Requires-Dist: opentelemetry-api==1.44.0
Requires-Dist: opentelemetry-instrumentation-aio-pika==0.65b0
Requires-Dist: opentelemetry-instrumentation-aiohttp-client==0.65b0
Requires-Dist: opentelemetry-instrumentation-aiohttp-server==0.65b0
Requires-Dist: opentelemetry-instrumentation-aiokafka==0.65b0
Requires-Dist: opentelemetry-instrumentation-aiopg==0.65b0
Requires-Dist: opentelemetry-instrumentation-asgi==0.65b0
Requires-Dist: opentelemetry-instrumentation-asyncclick==0.65b0
Requires-Dist: opentelemetry-instrumentation-asyncio==0.65b0
Requires-Dist: opentelemetry-instrumentation-asyncpg==0.65b0
Requires-Dist: opentelemetry-instrumentation-boto3sqs==0.65b0
Requires-Dist: opentelemetry-instrumentation-botocore==0.65b0
Requires-Dist: opentelemetry-instrumentation-cassandra==0.65b0
Requires-Dist: opentelemetry-instrumentation-celery==0.65b0
Requires-Dist: opentelemetry-instrumentation-click==0.65b0
Requires-Dist: opentelemetry-instrumentation-confluent-kafka==0.65b0
Requires-Dist: opentelemetry-instrumentation-dbapi==0.65b0
Requires-Dist: opentelemetry-instrumentation-django==0.65b0
Requires-Dist: opentelemetry-instrumentation-exceptions==0.65b0
Requires-Dist: opentelemetry-instrumentation-falcon==0.65b0
Requires-Dist: opentelemetry-instrumentation-fastapi==0.65b0
Requires-Dist: opentelemetry-instrumentation-flask==0.65b0
Requires-Dist: opentelemetry-instrumentation-grpc==0.65b0
Requires-Dist: opentelemetry-instrumentation-httpx==0.65b0
Requires-Dist: opentelemetry-instrumentation-jinja2==0.65b0
Requires-Dist: opentelemetry-instrumentation-kafka-python==0.65b0
Requires-Dist: opentelemetry-instrumentation-logging==0.65b0
Requires-Dist: opentelemetry-instrumentation-mysql==0.65b0
Requires-Dist: opentelemetry-instrumentation-mysqlclient==0.65b0
Requires-Dist: opentelemetry-instrumentation-openai-v2==2.3b0
Requires-Dist: opentelemetry-instrumentation-pika==0.65b0
Requires-Dist: opentelemetry-instrumentation-psycopg2==0.65b0
Requires-Dist: opentelemetry-instrumentation-psycopg==0.65b0
Requires-Dist: opentelemetry-instrumentation-pymemcache==0.65b0
Requires-Dist: opentelemetry-instrumentation-pymongo==0.65b0
Requires-Dist: opentelemetry-instrumentation-pymssql==0.65b0
Requires-Dist: opentelemetry-instrumentation-pymysql==0.65b0
Requires-Dist: opentelemetry-instrumentation-pyramid==0.65b0
Requires-Dist: opentelemetry-instrumentation-redis==0.65b0
Requires-Dist: opentelemetry-instrumentation-remoulade==0.65b0
Requires-Dist: opentelemetry-instrumentation-requests==0.65b0
Requires-Dist: opentelemetry-instrumentation-sqlalchemy==0.65b0
Requires-Dist: opentelemetry-instrumentation-sqlite3==0.65b0
Requires-Dist: opentelemetry-instrumentation-starlette==0.65b0
Requires-Dist: opentelemetry-instrumentation-structlog==0.65b0
Requires-Dist: opentelemetry-instrumentation-system-metrics==0.65b0
Requires-Dist: opentelemetry-instrumentation-threading==0.65b0
Requires-Dist: opentelemetry-instrumentation-tornado==0.65b0
Requires-Dist: opentelemetry-instrumentation-tortoiseorm==0.65b0
Requires-Dist: opentelemetry-instrumentation-urllib3==0.65b0
Requires-Dist: opentelemetry-instrumentation-urllib==0.65b0
Requires-Dist: opentelemetry-instrumentation-vertexai==2.2b0
Requires-Dist: opentelemetry-instrumentation-wsgi==0.65b0
Requires-Dist: opentelemetry-instrumentation==0.65b0
Requires-Dist: opentelemetry-propagator-aws-xray==1.0.2
Requires-Dist: opentelemetry-sdk==1.44.0
Requires-Dist: opentelemetry-semantic-conventions==0.65b0
Requires-Dist: opentelemetry-util-genai==0.2b0
Requires-Dist: opentelemetry-util-http==0.65b0
Requires-Dist: packaging==26.3
Requires-Dist: psutil==7.2.2
Requires-Dist: pyyaml
Requires-Dist: typing-extensions==4.16.0
Requires-Dist: wrapt==1.17.3
Description-Content-Type: text/x-rst

===================
dash0-opentelemetry
===================

The Dash0 OpenTelemetry distribution for Python. It is the Python counterpart of
the Dash0 Node.js distribution (``@dash0/opentelemetry``), adapted to Python's
distro/configurator machinery, and is meant to be injected into a process (for
example by the OpenTelemetry injector) with no code changes.

Entry points
============

``Dash0Distro`` (``opentelemetry_distro``)
    Runs first, before the configurator. It:

    * **Gates** the distribution: does nothing if ``DASH0_DISABLE=true`` or if
      ``DASH0_OTEL_COLLECTOR_BASE_URL`` is unset (nowhere to send telemetry). In
      either case it sets ``OTEL_SDK_DISABLED=true`` and skips instrumentation.
    * Defaults all three signals to the pure-Python OTLP/HTTP exporter
      (``otlp_proto_http``, ``http/protobuf``) and points
      ``OTEL_EXPORTER_OTLP_ENDPOINT`` at ``DASH0_OTEL_COLLECTOR_BASE_URL``. The
      pure-Python exporter has no native dependencies, which is what makes the
      distribution safe to inject onto an arbitrary process' ``PYTHONPATH``.
      When a signal's OTLP protocol does not match the transport the shared
      endpoint targets — its port is the *other* protocol's conventional
      default (``4317`` gRPC / ``4318`` HTTP) — a per-signal
      ``OTEL_EXPORTER_OTLP_<SIGNAL>_ENDPOINT`` is derived by rewriting the
      port (appending the ``v1/<signal>`` path for HTTP), so mixed-protocol
      setups and a base URL on the "wrong" default port still export
      correctly. Custom ports and explicit per-signal endpoints are always
      left untouched.
    * Injects detected **resource attributes** into ``OTEL_RESOURCE_ATTRIBUTES``/
      ``OTEL_SERVICE_NAME`` (see below) so the SDK's Resource picks them up.
    * Overrides ``load_instrumentor`` to activate each instrumentor
      **defensively** — a disabled or failing instrumentor is skipped and logged
      rather than aborting auto-instrumentation of the host process.

``Dash0Configurator`` (``opentelemetry_configurator``)
    Runs after the distro. Delegates to the standard OpenTelemetry SDK
    configurator to build the providers/exporters/Resource from the environment,
    then adds two behaviors from the Node.js distribution:

    * an optional **bootstrap span** emitted once at startup
      (``DASH0_BOOTSTRAP_SPAN=<name>``);
    * optional **graceful flush** on ``SIGTERM``/``SIGINT``
      (``DASH0_FLUSH_ON_SIGTERM_SIGINT=true``) — normal-exit flushing is already
      handled by the SDK's ``atexit`` provider shutdown.

Resource detectors (``opentelemetry_resource_detector``)
    The detected resource attributes (see below) as standard SDK resource
    detectors, one per concern so each can be used (or omitted) independently:

    * ``dash0_distribution`` — ``telemetry.distro.name``/``telemetry.distro.version``;
    * ``dash0_kubernetes`` — ``k8s.pod.uid``;
    * ``dash0_service_name`` — wraps the upstream service detection (a
      process-stable ``service.instance.id`` and ``service.name`` from
      ``OTEL_SERVICE_NAME``) and adds the distro's detected ``service.name``
      and ``service.version``, so there is no need to also list the built-in
      ``service`` detector.

    They can be referenced via ``OTEL_EXPERIMENTAL_RESOURCE_DETECTORS`` on the
    ``opentelemetry-instrument`` path, or by name in a declarative config file
    under ``resource.detection/development.detectors``.

    Declarative configuration (``OTEL_CONFIG_FILE``) runs only the detectors
    the file lists, and does not read ``OTEL_RESOURCE_ATTRIBUTES`` or
    ``OTEL_SERVICE_NAME`` by itself, although it does substitute environment
    variables, so both can be named explicitly. The distro adds nothing to the
    file, so a user who wants ``telemetry.distro.*``, ``k8s.pod.uid`` or the
    ``service.name`` fallback names the detectors themselves. The Ruby and
    JavaScript distributions handle the ``dash0_*`` detectors the same way,
    though the complete resource still differs between them because the SDKs
    ship different built-in detectors.

Instrumentations
================

The distribution depends on the full upstream auto-instrumentation set — every
package enumerated by the ``bootstrap_gen`` module of
``opentelemetry-instrumentation`` (the list behind ``opentelemetry-bootstrap``)
— pinned to a single contrib version, so injected processes need no bootstrap
step. Instrumentations only activate when their target library is present,
which makes carrying all of them safe.

Deviations from the upstream list:

* ``opentelemetry-propagator-aws-xray`` is shipped although upstream's
  bootstrap list only covers instrumentations (the Dash0 operator's
  instrumentation image always carried it).
* ``opentelemetry-exporter-prometheus``,
  ``opentelemetry-propagator-ot-trace``, and
  ``opentelemetry-instrumentation-aws-lambda`` are deliberately not shipped.

``tests/test_instrumentations.py`` cross-checks the dependency block against
the installed upstream list, so a contrib version bump that adds or removes
instrumentations fails CI until the block is re-synced. Dependabot keeps the
pins themselves moving (all ``opentelemetry-*`` packages are grouped into one
update, since the contrib instrumentations only resolve when they share one
contrib version).

Resource detection
==================

Ported from the Node.js distribution's custom detectors:

* **Kubernetes pod UID** (``k8s.pod.uid``): confirms it is running in Kubernetes
  (via ``/etc/hosts``), then extracts the pod UID from cgroup v1
  (``/proc/self/mountinfo``) or cgroup v2 (``/proc/self/cgroup``).
* **Service name and version**: if ``OTEL_SERVICE_NAME`` / ``service.name`` are
  not set (and ``DASH0_AUTOMATIC_SERVICE_NAME`` is not ``false``), reads them
  from the installed distribution the process was started from, then from the
  nearest ``pyproject.toml``, and falls back to the entrypoint script basename.
  Only the first two carry a ``service.version``; no version is invented. Node.js
  reads ``package.json``, and reading installed metadata is the Python analog.
* **Distribution attributes**: ``telemetry.distro.name=dash0-python`` and
  ``telemetry.distro.version``.

Existing attributes are never overridden.

Dependency policy
=================

The distribution ships a curated set of upstream OpenTelemetry packages, pinned
exactly in its ``pyproject.toml``: each release is validated against precisely
those versions, and the code assumes them. Version bumps are deliberate,
standalone changes. The pins cover the *entire* installed tree: every package
the distribution pulls in transitively is also declared directly with an exact
pin, so no version is left to transitive resolution. CI enforces both rules via
``scripts/check_pinned_dependencies.py`` (in-repo workspace members are exempt,
as their version is fixed by the checkout).

Injector bootstrap (``sitecustomize.py``)
=========================================

The wheel ships the bootstrap script used for ``PYTHONPATH``-based injection at
``dash0/opentelemetry/injector/sitecustomize.py``. It is not meant to be
imported from that location (importing it executes it); instead, a consumer
building an injectable tree — such as the dash0-operator's instrumentation
image —

1. installs the distribution into a self-contained directory:
   ``pip install --target <dir> dash0-opentelemetry``;
2. copies the script to ``<dir>/sitecustomize.py``;
3. copies ``dash0/opentelemetry/injector/all-dependencies.txt`` to
   ``<dir>/all-dependencies.txt``.

Both files ship in the wheel, so a consumer copies them and derives nothing.
``all-dependencies.txt`` is generated by
``scripts/refresh_injector_manifest.py``, which installs this package into a
scratch directory, hands the tree to ``scripts/generate_all_dependencies.py``
and writes the result back into the source tree. CI runs the same script with
``--check`` and fails when the checked-in file is not what it produces, so
editing it by hand does not survive.

Each line of ``all-dependencies.txt`` carries what the packages of the tree
declare about that package, not the version the tree contains. The bootstrap
puts the injected tree last on ``sys.path``, so the application's copy of a
shared package already wins at import; the only thing left to check is whether
this distribution's code runs against the version the application brought, and
``Requires-Dist`` is where that is stated. Writing the installed version
instead would reject versions upstream declares compatible, and would stand the
whole distribution down for an application that pinned any of them.

The exact pins the distribution's own packages make are skipped when the
manifest is built, because they record the set each release was validated
against rather than what the code supports. Ranges those same packages declare
are kept: ``opentelemetry-sdk~=1.12`` comes from the vendored pyproto
exporters, and no other package in the tree declares ``opentelemetry-sdk`` at
all.

The `OpenTelemetry injector
<https://github.com/open-telemetry/opentelemetry-injector>`_ then prepends
``<dir>`` to the ``PYTHONPATH`` of the processes to instrument, so Python's
``site`` machinery runs the script on interpreter startup. The script
initializes the auto-instrumentation only after a set of safety checks —
supported interpreter version, no OpenTelemetry packages already shipped with
the application (double instrumentation), no version conflicts between the
application's dependencies and ``all-dependencies.txt`` — and otherwise
deactivates the injection gracefully, for the current process and its children.
It also bridges ``OTEL_EXPORTER_OTLP_ENDPOINT`` to
``DASH0_OTEL_COLLECTOR_BASE_URL`` when only the former is set. The script stays
valid Python 2.7+ so it can self-deactivate instead of crashing on interpreters
the distribution does not support.

Environment variables
=====================

============================================  ====================================================
Variable                                      Effect
============================================  ====================================================
``DASH0_OTEL_COLLECTOR_BASE_URL`` (required)  Collector base URL; also sets the OTLP endpoint.
``DASH0_DISABLE``                             ``true`` disables the distribution entirely.
``DASH0_AUTOMATIC_SERVICE_NAME``              ``false`` opts out of the service-name fallback.
``DASH0_BOOTSTRAP_SPAN``                      Emit one span with this name at startup.
``DASH0_FLUSH_ON_SIGTERM_SIGINT``             ``true`` flushes telemetry on SIGTERM/SIGINT.
============================================  ====================================================

Status
======

Prototype. The ``otlp_proto_http`` exporter is resolved from the in-repo pyproto
workspace member, published as ``dash0-opentelemetry-exporter-otlp-pyproto-http``
on the Dash0 package index and on public PyPI. Not yet done: the injector's
per-``libc`` packaging and the integration/injection test matrix. Unit tests do
not require a running collector.
