Prometheus and Elastic export¶
The native_observability_export submodule provides the Prometheus scrape endpoint and the Elastic NDJSON export. It is part of the RAW preset (drush no:preset:raw), the minimum tier for machine-only consumers.
Routes¶
| Route | Path | Permission |
|---|---|---|
native_observability_export.settings |
/admin/config/development/native-observability/settings/export |
administer native observability export |
native_observability_export.prometheus_metrics |
/admin/reports/native-observability/prometheus |
access native observability prometheus endpoint |
native_observability_export.prometheus_scrape |
/native-observability/prometheus |
none, route declares _access: 'TRUE'. The exposure grade and the bearer token decide. |
native_observability_export.elastic_export |
/admin/reports/native-observability/elastic |
view native observability traces |
Admin view and scrape endpoint¶
Both prometheus_metrics and prometheus_scrape return identical Prometheus text output; only the access model differs. prometheus_scrape resolves to PrometheusMetricsController::render, which checks the bearer token. prometheus_metrics resolves to PrometheusMetricsController::renderForOperator, which skips the bearer check because the router has already enforced the permission.
/admin/reports/native-observability/prometheusis gated by theaccess native observability prometheus endpointpermission, for manual inspection from inside the Drupal admin./native-observability/prometheuscarries no Drupal permission, because external scrapers do not send a Drupal session. Access control on this route is the bearer token described below.
sequenceDiagram
participant Prometheus
participant Drupal as PrometheusMetricsController
Prometheus->>Drupal: GET /native-observability/prometheus (Authorization: Bearer <token>)
alt exposure grade 0 and not legacy mode
Drupal-->>Prometheus: 403
else no token configured and prometheus_allow_unauthenticated is false
Drupal-->>Prometheus: 403
else no token configured and prometheus_allow_unauthenticated is true
Drupal-->>Prometheus: 200 metrics body
else Authorization header missing
Drupal-->>Prometheus: 401
else token mismatch
Drupal-->>Prometheus: 403
else token matches
Drupal-->>Prometheus: 200 metrics body
end
The scrape endpoint also follows the External exposure grade. At grade 0 it answers 403 whatever the token, and from grade 1 it serves aggregates by route name. A site that updated with this module enabled starts in legacy mode and keeps serving until a grade is chosen. The admin route /admin/reports/native-observability/prometheus is an operator view behind its permission and is outside the grade.
When no bearer token is configured, the scrape endpoint answers 403 to every request. To serve it without a token anyway, set prometheus_allow_unauthenticated to true (default false, form label "Serve without a bearer token"); the endpoint is then publicly readable and the settings form shows a security warning. The admin route is unaffected by this key.
Metrics exposed¶
Ten metrics are served, plus the truncation marker below. Names and help text below are read from the endpoint itself, so they are what a scrape actually returns.
| Metric | Type | Meaning |
|---|---|---|
drupal_http_requests_total |
counter | Total HTTP requests recorded, all time. |
drupal_http_requests_window |
gauge | HTTP requests inside the rolling window. |
drupal_http_request_duration_ms |
summary | Request duration by quantile and route. |
drupal_http_error_rate |
gauge | Percentage of 5xx responses in the rolling window. |
drupal_http_ajax_requests_total |
counter | Total AJAX requests recorded, all time. |
drupal_cache_operations_total |
counter | Cache events by operation and bin, all time. |
drupal_span_count |
gauge | Spans by category in the rolling window. |
drupal_span_duration_avg_ms |
gauge | Average span duration by category in the rolling window. |
drupal_outbound_http_requests_total |
gauge | Outbound HTTP spans by destination host in the rolling window. |
drupal_outbound_http_duration_avg_ms |
gauge | Average outbound HTTP span duration by destination host. |
Two naming details will bite you when writing queries. Metrics ending in
_total are counters over all time, and the ones scoped to the rolling window
say window or are gauges, so mixing them in one query compares different time
bases. And drupal_outbound_http_requests_total is a gauge despite the
_total suffix, because it counts spans inside the window rather than
accumulating. Do not wrap it in rate().
The rolling window is prometheus_window_seconds, 300 seconds by default. See
Export settings for that and for the
label strategy, whose machine names do not describe what they emit.
Request metrics carry the route label with the route machine name (unknown for a request that matched no route, so a scan that produces 404s lands there), plus method and status_code depending on the label strategy. The path label of the former route_path_method strategy is gone: a site that used it was moved to route_only and its series changed identity, so queries and dashboards that filter on path need rewriting. No label carries a request path, a query string or a URL. The host label of the outbound HTTP metrics holds the destination host only.
Cache metrics carry operation and bin labels:
drupal_cache_operations_total{operation="HIT",bin="page_cache"} 828
drupal_cache_operations_total{operation="INVALIDATED",bin="cache_tags"} 1263
drupal_cache_operations_total{operation="CACHEABLE",bin="response_cacheability"} 63
When the series cap is exceeded the reader drops the lowest-count series and
emits drupal_prometheus_truncated{reason="series_limit"} 1. Alert on that
series: without it, truncation looks like a traffic drop.
Bearer token¶
Generate and store the token with Drush:
drush native_observability:token:generate prometheus
# alias:
drush no:token:generate prometheus
The command prints the generated value once and stores it in the state key native_observability_export.secret.prometheus_bearer_token. The token can also be set from the settings form field, which is write-only: an empty submission keeps the current token, and the sentinel value __clear__ removes it.
Metrics window, label strategy, and max series settings for the scrape output are documented in Configuration.
Elastic export¶
ElasticTelemetryExportController::export returns stored traces as application/x-ndjson, one JSON document per line, built by ElasticTelemetryBuilder. This is an operator download behind the view native observability traces permission, not an outward flow: it carries the stored path, redacted as described under Paths, and the client IP as capture.client_ip_mode stored it. Do not feed it to an external system. Supported query arguments:
| Argument | Meaning |
|---|---|
limit |
Row limit, clamped between 1 and 1000, default 200 |
status_code |
Filter by HTTP status code |
path |
Filter by request path |
min_duration_ms |
Minimum trace duration in milliseconds |
max_duration_ms |
Maximum trace duration in milliseconds |
Example:
curl "https://your-site.example/admin/reports/native-observability/elastic?limit=500&status_code=500"
Example Prometheus scrape_config¶
scrape_configs:
- job_name: drupal_native_observability
bearer_token: <your-token>
metrics_path: /native-observability/prometheus
scheme: https
static_configs:
- targets: ['your-site.example']