OpenTelemetry export¶
The native_observability_otel submodule provides the OTLP push exporter. It is part of the INTEGRATIONS preset (drush no:preset:integrations), which also enables native_observability_eca_bridge.
Routes¶
| Route | Path | Permission |
|---|---|---|
native_observability_otel.settings |
/admin/config/development/native-observability/settings/otel |
administer native observability otel |
OTLP settings¶
NativeObservabilityOtelSettingsForm configures the push-side exporter: enable/disable, the OTLP HTTP endpoint, request timeout, service name/namespace/version, and additional HTTP headers sent on every export request. Current defaults are documented in Configuration.
The Prometheus scrape endpoint and its bearer token live in the separate native_observability_export module; see Prometheus and Elastic export.
When a span is sent¶
A span is per-request detail, so it follows the External exposure grade. Grade 0 and grade 1 send nothing. Grade 2, with its declaration recorded, sends. A site that updated with the module enabled starts in legacy mode and keeps sending until a grade is chosen. Nothing is sent while capture.enabled is off, or for a request matched by an exclusion rule or by capture.exclude_own_routes.
What a span carries¶
One span per completed request, named METHOD route_name, for example GET entity.node.canonical. A request that matched no route is named GET unmatched.
| Attribute | Value |
|---|---|
http.route, drupal.route_name |
The route machine name (http.route is unmatched when there is none). |
http.request.method, http.response.status_code, drupal.duration_ms, drupal.request_id |
Method, status, duration, request id. |
exception.type |
The exception class, when the request failed. |
enduser.id |
Only when capture.store_uid is on. |
client.address |
Only in legacy mode, as capture.client_ip_mode allows. |
No span carries a path, query string, URL, referer, user agent, exception message or stack trace, at any exposure grade. Outbound HTTP calls and SQL statements are not exported.
Exporter plugins¶
Exporters implement ExporterInterface and are discovered through the NativeObservabilityExporter attribute (namespace Drupal\native_observability_otel\Plugin\NativeObservabilityExporter), managed by ExporterManager and exposed through the native_observability_otel.exporter_registry service. Three plugins ship with the module:
| Plugin ID | Class | Role |
|---|---|---|
opentelemetry |
OpenTelemetryExporter |
Pushes each completed request trace to the configured OTLP HTTP endpoint |
prometheus |
PrometheusExporter |
No-op export(). Registers Prometheus in the plugin list because scraping is pull-based, not pushed by this subsystem |
null_exporter |
NullExporter |
No-op exporter used to validate exporter discovery |
OpenTelemetryTraceSubscriber always resolves the opentelemetry plugin ID through the registry; there is no settings field to switch the active exporter. The plugin type exists as an extension point: a custom module can register an additional NativeObservabilityExporter plugin and call it through ExporterRegistry::get() or ExporterRegistry::getAvailableExporters().
flowchart LR
A[kernel.response] --> B[OpenTelemetryTraceSubscriber]
B --> H{Capture on and exposure allows detail?}
H -- no --> Z
H -- yes --> C{Excluded by a rule or own route?}
C -- yes --> Z[No export]
C -- no --> D[ExporterRegistry.get 'opentelemetry']
D --> E{Exporter available?}
E -- no, disabled or empty endpoint --> Z
E -- yes --> F[OpenTelemetryPayloadBuilder]
F --> G[POST OTLP endpoint]
OTLP bearer token¶
When the collector requires authentication, generate and store the collector token with Drush:
drush native_observability:token:generate otlp
# alias:
drush no:token:generate otlp
The command prints the generated value once and stores it in the state key native_observability_otel.otlp_bearer_token. OpenTelemetryExporter reads it and sends Authorization: Bearer <token> on every OTLP export request. The token field on the settings form is write-only: an empty submission keeps the current token, and the sentinel value __clear__ removes it. For collectors that expect a different header name, use the Additional HTTP headers field instead.
Example target: OTLP collector or Tempo¶
drush config:set native_observability_otel.settings endpoint 'http://collector:4318/v1/traces' -y
drush config:set native_observability_otel.settings enabled true -y
A Grafana Alloy collector forwarding to Tempo needs a matching OTLP HTTP receiver, for example a otelcol.receiver.otlp block listening on 4318 with the http protocol enabled, exporting to otelcol.exporter.otlp pointed at Tempo's gRPC or HTTP ingest port.