Skip to content

Configuration

The sections below follow the order of the tabs on the settings pages: Settings, Sanity check, External exposure, MCP (with the MCP module, see Native Observability via MCP), Cleanup, Exclusions, Cache Observer, Database Observer, OpenTelemetry, Report, Overhead measurement. Export settings come last because they are the one settings form the tab strip does not link: reach them at /admin/config/development/native-observability/settings/export.

Permissions

The permission machine names and descriptions below are quoted verbatim from each module's *.permissions.yml.

Permission Defined by Description
access native observability native_observability View Native Observability status and diagnostics pages.
view native observability traces native_observability View request traces, filter them, and export trace payload as JSON.
administer native observability native_observability Configure what data Native Observability captures and stores.
administer native observability exclusions native_observability Manage request-log exclusion rules for Native Observability.
administer native observability exposure native_observability Decide what data Native Observability may send to MCP, the Prometheus export and OpenTelemetry, and make the declaration that detail may reach AI models.
delete all native observability traces native_observability Permanently deletes all stored request traces.
access native observability cache observer native_observability_cache_observer View raw cache observer events and exports.
administer native observability cache observer native_observability_cache_observer Configure cache observer settings and delete retained cache observer data.
access native observability dashboard native_observability_dashboard View the Native Observability dashboard and operational monitoring panels.
access native observability database observer native_observability_database_observer View raw database observer records, details pages and JSON exports.
administer native observability database observer native_observability_database_observer Configure Database Observer settings and remove stored raw records.
access native observability execution native_observability_execution View execution timelines and performance diagnostics.
administer native observability export native_observability_export Manage scrape-side export settings (Prometheus, Elastic) for Native Observability.
access native observability prometheus endpoint native_observability_export Access the Prometheus-compatible metrics endpoint exposed by Native Observability.
view native observability metrics native_observability_metrics View aggregated route performance metrics, including throughput and latency percentiles.
administer native observability otel native_observability_otel Manage OpenTelemetry exporter settings for Native Observability.
access native observability calibration routes native_observability Allows requests to reach the fixed-load calibration routes the overhead measurement protocol uses to measure the cost of this module on this server. These routes stay closed at all other times, and answer only while a measurement started by an account holding this permission is actually running. Grant this only to the account(s) that run overhead measurements.

access native observability gates the status page (/admin/reports/native-observability/status), the sanity check (/admin/reports/native-observability/sanity-check) and the trace lookup by request ID (/admin/reports/native-observability/trace/request/{request_id}). The calibration permission is granted to no role at install.

Core settings

Two forms edit the core native_observability.settings configuration object.

Capture and retention settings

Form: native_observability.settings at /admin/config/development/native-observability/settings.

Config key Type Default Meaning
capture.enabled boolean true Master switch. When disabled, no traces are recorded.
capture.request_id boolean true Store the request ID field.
capture.method boolean true Store the HTTP method. Always true; the form checkbox is disabled (required by the storage schema for indexing).
capture.path boolean true Store the request path. Always true; the form checkbox is disabled (required by the storage schema for indexing).
capture.route_name boolean true Store the resolved Drupal route name.
capture.status_code boolean true Store the response HTTP status code.
capture.duration_ms boolean true Store request duration in milliseconds.
capture.exception boolean true Store exception class/message/code (best-effort).
capture.exception_message_max_length integer 1024 Longest exception message kept, in characters. Longer messages are cut and end in .... No form field: set it with drush cset or configuration import.
capture.store_uid boolean true Store the acting Drupal user ID.
capture.client_ip_mode string anonymized Client IP capture mode: off, anonymized, hashed, or full.
(State) native_observability.client_ip_hash_salt string generated Salt used when client_ip_mode is hashed. Kept in State, not in config, so it is never exported. Generated on first use; $settings['native_observability_client_ip_hash_salt'] in settings.php overrides it.
capture.user_agent_mode string family User Agent capture mode: off, family, or full.
capture.headers_mode string off Request header capture mode: off or allowlist.
capture.headers_allowlist sequence {} Header names captured when headers_mode is allowlist.
capture.query_mode string off Query string capture mode: off, keys_only, allowlist, or full.
capture.query_allowlist sequence {} Query keys captured when query_mode is allowlist.
capture.body_mode string off Request body capture mode: off, keys_only, allowlist, or full.
capture.body_allowlist sequence {} Body keys captured when body_mode is allowlist.
capture.exclude_own_routes boolean true Skip requests to routes whose name starts with native_observability (dashboards, Prometheus endpoint, admin forms) so they never reach any storage this module family writes to (trace, spans, cache observer, database observer) or the OTLP exporter.
capture.profile_self boolean false Self-profile the cost of the family's own instrumentation points. Off by default; turn it on only to measure the module's own overhead, then turn it back off.
overhead.budget_ms integer 800 Declared TTFB budget, in milliseconds, the overhead report colors each HTTP workload's delta against.
retention.max_rows integer 50000 Row cap enforced on each raw storage: traces, spans, cache events and database queries. Cleanup deletes the oldest rows above it, in cleanup_batch_limit batches. 0 means no cap. Route metrics are exempt, see below.

What is stored about people, and how it is reduced

Every trace row can carry data that identifies a person: an IP address, a user agent, a Drupal user ID, query strings, headers, request bodies. The defaults are the privacy-preserving end of each setting, and broader capture is opt-in. This section states what each mode actually writes to the database, because "anonymized" on its own is not a commitment anyone can audit.

Capture settings form with the exclude-own-routes checkbox
Capture settings. "Exclude module-own routes from tracing" is enabled by default; its own help text is precise about the scope, naming the trace table and the OTLP collector rather than all storage.

Client IP address

Setting: capture.client_ip_mode, default anonymized. Applied in PrivacyPolicy::applyToIp(), the single place that decides what is stored. The value is kept in the client_ip column only, not in the payload.

Mode What lands in the database Example
off Nothing. The column stays empty.
anonymized (default) IPv4: the last octet is replaced with 0. IPv6: the first 64 bits are kept and the rest is zeroed, written ::. An IPv4-mapped IPv6 address follows the IPv4 rule. 203.0.113.47 becomes 203.0.113.0; 2001:db8:1:2:3:4:5:6 becomes 2001:db8:1:2::
hashed sha256(salt + "\|" + ip), using the salt kept in State (see above). The original address is not recoverable, but the same address always produces the same hash, so sessions remain correlatable. 64 hex characters
full The address exactly as received. 203.0.113.47

Two things follow from the implementation:

  • anonymized keeps a /24 for IPv4 and a /64 for IPv6. That is coarse enough to defeat identification of a single household in most cases, and precise enough to still identify a small office or a single-tenant network.
  • hashed needs a salt: without one it would be a plain SHA-256 of the address, and the IPv4 space is small enough to brute-force in minutes. The salt lives in State (native_observability_update_10112() moves a legacy config value there, so hashes stay comparable), is generated on first use, and an empty salt is never used. Changing the salt, or running that update on a site that had none, makes new hashes not comparable with the ones already stored.

User agent

Setting: capture.user_agent_mode, default family. Applied in TraceSubscriber::userAgentFamily().

Mode What lands in the database
off Nothing.
family (default) A normalized browser and OS pair, matched on substrings of the lowercased agent (edge, chrome, safari, and so on, unknown when nothing matches). The version string, build numbers and device details are discarded.
full The raw User-Agent header, which is often precise enough to fingerprint a device.

User identity

Setting: capture.store_uid, default true. Stores the acting Drupal user ID on the trace row. Turn it off when you do not need to attribute requests to accounts; the form itself flags the field as potentially personal data.

Headers, query strings and request bodies

All three default to off, and all three are opt-in through an allowlist rather than a blocklist. That direction matters: a field you forget to list is not captured, instead of being captured until someone notices.

Setting Default Modes
capture.headers_mode off off, allowlist (names from capture.headers_allowlist)
capture.query_mode off off, keys_only, allowlist (keys from capture.query_allowlist), full
capture.body_mode off off, keys_only, allowlist (keys from capture.body_allowlist), full

keys_only records which parameters were present without their values, which answers "was a filter applied" without storing what was filtered on.

full captures credentials

query_mode: full and body_mode: full store the request verbatim, including password fields, tokens in query strings, and any personal data a form carried. Use them for short debugging sessions, and set them back to off afterwards. Only the keys listed under Sensitive keys are redacted on the way in.

Sensitive keys

Two settings control which values are replaced with [REDACTED] before storage. They apply to request parameters captured by query_mode and body_mode, and to the query string of outbound HTTP call URLs recorded by the spans module. The user:password part of an outbound URL follows the same switch.

Setting Type Default Effect
capture.redact_sensitive_keys boolean true Master switch. When false, nothing is redacted: keys, tokens and passwords can be stored in clear text.
capture.sensitive_keys list of strings password, passwd, pwd, secret, token, key, apikey, auth, authorization, session, sid, cookie, jwt, signature, sig, credential, credentials, pass, code, state, hash, itok, verifier, code_verifier, oauth_verifier, otp, nonce, sess, phpsessid Names to redact. Saved trimmed, lowercase, without blanks or duplicates. The form requires at least one while the switch is on.

The comparison is case-insensitive and works on segments, not on substrings. It is recursive: a nested array under a sensitive name is redacted whole, and a sensitive name nested at any depth is redacted on its own. A parameter name is split on _, -, ., [, ], spaces and camelCase boundaries (accessToken gives access, token; X-Amz-Signature gives x, amz, signature). The name is sensitive when the whole name or at least one segment equals an entry. So key matches access_key, api_key and accessKey, but not keyword or monkey.

Behavior change for auth

Earlier versions matched by substring, so author was redacted because it contains auth. With segment matching it is not. This is intended. Add author to the list if a site needs it redacted.

Route parameters bound to sensitive names are redacted in the stored path as well, see Paths.

native_observability_update_10111() adds the new default keys (pass, code, state, hash, itok, verifier, code_verifier, oauth_verifier, otp, nonce, sess, phpsessid) to a customized list and keeps the keys already there.

If capture.sensitive_keys is missing or empty while the switch is on, the default list applies, so a site that has not run native_observability_update_10107 yet is protected as well.

Paths

The path of a request is redacted before it is stored, in the path column and in the payload. Three rules apply, in this order:

  • A segment bound to a route parameter with a sensitive name (the list above plus hash, timestamp, token, key, secret, code, signature, sig, otp, nonce, verifier) becomes [REDACTED].
  • The hash and timestamp of a one-time login link (/user/reset/{uid}/{timestamp}/{hash}) and of a cancel link (/user/{uid}/cancel/confirm/{timestamp}/{hash}) become [REDACTED].
  • Any segment of 20 or more URL-safe characters that mixes letters and digits, with few dashes, becomes [REDACTED]. A slug of words is kept.

The payload also carries query_keys: the names of the query parameters, at most 20, never their values. A name that is odd or looks like a token is stored as [KEY]. This holds whatever capture.query_mode is.

Exceptions

capture.exception_message_max_length (default 1024 characters) cuts a stored exception message, which then ends in .... Before that, the referer that core appends to routing errors is removed, a database exception loses its argument dump and quoted literals, and every URL in the text has its sensitive query values redacted. The request body of an OAuth token or client registration request is never captured, whatever capture.body_mode is.

Retention is the other half

Reducing what is stored only limits exposure per row. retention.max_rows (default 50000) bounds how many rows exist at once, trace_retention_hours and the other per-storage windows in the cleanup settings bound how long rows live. A privacy posture that sets every capture mode to its narrowest value and leaves cleanup disabled still accumulates data indefinitely.

The row cap applies to the raw per-request storages: traces, spans, cache events and database queries. Route metrics are exempt on purpose. That table holds one row per route and time window, so its size follows the number of routes rather than the volume of traffic, and capping it would drop older aggregates instead of bounding an uncontrolled stream.

trace_retention_hours MUST NOT be shorter than spans_retention_hours, native_observability_cache_observer.settings.retention_hours or native_observability_database_observer.settings.retention_hours: spans, cache events and database query rows reference a trace row, and a trace purged ahead of the rows that point at it leaves those rows orphaned. Each settings form validates this ordering on submit and rejects a combination that would invert it, naming both the submitted and the conflicting value.

External exposure

Form: native_observability.exposure_settings at /admin/config/development/native-observability/exposure (tab External exposure of the settings hub). Requires administer native observability exposure, a restricted permission granted to no role at install.

One rule decides what leaves the site towards the three outward consumers: MCP clients, the Prometheus export and the OpenTelemetry exporter. MCP clients can hand data to an AI model, local or remote, which the server cannot tell apart.

Grade What leaves How it is switched on
0 (default) Nothing. Default of a new install.
1 Aggregates by route name (for example entity.node.canonical), never a path. Choose it on the form.
2 Detail: traces and spans, always filtered by the privacy policy. No consumer receives a path, a query string or a URL: location is the route name. Never the client IP. Choose it and tick the three statements of the declaration.

The page ends with a table, "What each consumer receives now", that lists the MCP, Prometheus and OpenTelemetry consumers, whether their module is enabled and what they get.

What each consumer receives at each grade:

Consumer Grade 0 Grade 1 Grade 2
Prometheus export (/native-observability/prometheus) The endpoint answers 403. Metrics by route name. A request without a route name is labelled unknown. Same metrics. No label ever carries a path. Outbound HTTP metrics carry the destination host only.
OpenTelemetry No span. No span: a span describes one request, so it is detail. Spans named METHOD route_name, with http.route set to the route name (or unmatched), the status, the duration, the exception class, the user ID as the capture settings allow and no client address. Never url.path, query, referer, exception message or stack trace.
MCP Nothing. Aggregates. Detail without paths, URLs or exception messages, with the second confirmation of the MCP module (the observability:raw scope).

Client IP addresses never leave the site at any grade, in any consumer. The one exception is the legacy behavior below, where OpenTelemetry keeps sending client.address as capture.client_ip_mode allows.

The Elastic NDJSON export and the operator Prometheus page are admin downloads pulled by a user with the right permission, not automatic outbound flows, hence outside the grade.

Grade 2 stays at 0 until the declaration is recorded. The declaration is three statements: visitor-related data may be sent to AI models, including remote ones; the privacy notice of the site says so; the person confirming is entitled to decide for the site. Saving stores the user and the time. Choosing another grade removes the declaration, so going back to 2 asks for it again.

Config key Type Default Meaning
exposure.grade integer 0 The grade chosen: 0, 1 or 2.
exposure.declaration mapping, nullable null uid and timestamp of the grade 2 declaration.
exposure.legacy_external_consumers boolean false Export and OpenTelemetry send without a grade (legacy mode), with the 2.2 content. Never applies to MCP.
exposure.detail_page_size integer 50 Rows per page returned by tools that list detail.
exposure.untrusted_text_max_length integer 256 Visitor-written text is cut to this many characters before it is sent out.

Sites that update with export or OpenTelemetry enabled

native_observability_update_10110() sets exposure.legacy_external_consumers to true when the export or OpenTelemetry module is enabled, so nothing stops sending. The status report shows a warning while the flag is on: legacy mode keeps Prometheus and OpenTelemetry sending without a grade, but with the 2.2 content (no paths, http.route instead of url.path, client.address as capture.client_ip_mode allows). Saving the External exposure form chooses a grade and turns the flag off. Sites without those modules start at grade 0.

From grade 1 the status report carries a warning reminding that the privacy notice has to match what is sent.

Cleanup settings

Cleanup settings with retention per storage
Cleanup settings. Retention is set per storage, in hours for traces and spans and in days for aggregated metrics, and the batch limit bounds how many rows one cron run deletes per handler.

Form: native_observability.cleanup_settings at /admin/config/development/native-observability/cleanup. Requires the administer site configuration permission.

Config key Type Default Meaning
cleanup_enabled boolean true Enable the automatic cleanup routine for traces, spans, and metrics.
trace_retention_hours integer 72 Hours a trace row is kept before cleanup removes it. Must not be lower than spans_retention_hours, cache_observer.retention_hours or database_observer.retention_hours.
spans_retention_hours integer 24 Hours a span row is kept before cleanup removes it.
spans_payload_max_bytes integer 8192 Largest payload, as JSON bytes, that SpanRecorder stores for a custom span. A bigger one is replaced by {"_truncated":true,"_original_bytes":N,"_preview":"..."}. Values below 256 fall back to the default. No form field: set it with drush cset or configuration import.
metrics_retention_days integer 7 Days an aggregated metrics row is kept before cleanup removes it.
cleanup_batch_limit integer 5000 Maximum number of rows deleted per cleanup batch run.

deferred_persistence_enabled (boolean, default true) also lives in native_observability.settings but has no form element; it is set via configuration import or drush cset native_observability.settings deferred_persistence_enabled. When enabled, hot-path INSERTs (spans, observer events) are deferred to a single multi-row INSERT at kernel.terminate instead of one INSERT per event.

Excluding self-observability traffic

Once an external tool scrapes or polls this module, the module starts observing that traffic too. A Prometheus job on a 15 second interval adds 4 requests per minute forever, and every one of them is recorded as if it were application traffic. It inflates counters, skews latency percentiles, and fills the tables with rows that tell you nothing about your site.

Two separate mechanisms exclude that traffic. They do not cover the same channels, so knowing which is which matters.

The exclude_own_routes checkbox

capture.exclude_own_routes is a checkbox on /admin/config/development/native-observability/settings, enabled by default. It excludes any request whose route name starts with native_observability, which covers the dashboards, the admin forms, the Prometheus scrape endpoint and the Elastic export route.

RequestLogExclusionSubscriber decides this on kernel.request (and again on kernel.exception, for a route whose access check fails before the first check runs), setting the same request attribute an exclusion rule match sets. Every observer that already asks RequestLogExclusionMatcher::isRequestExcluded() picks it up unchanged, so the checkbox now stops these requests from reaching the trace table, the OTLP exporter, the cache observer, the database observer and the spans storage alike.

A request that Drupal's Internal Page Cache serves as a HIT never runs the routing or request events, so the subscriber above never sees it. CachePageObserverMiddleware, which reads the Internal Page Cache's own outcome header, takes the same decision there with the shared matcher: exclusion rules first, then the own-route check on the path. A HIT that matches is not recorded.

Turn the checkbox off only while debugging the module's own runtime behavior, and turn it back on afterwards.

Request log exclusion rules

Rules live at /admin/config/development/native-observability/exclusions and are managed from a list with add, edit and delete operations. A matching rule sets the native_observability.request_excluded request attribute, which RequestLogExclusionMatcher::isRequestExcluded() reports to every observer that asks. Changing a rule invalidates a runtime cache, so the list is rebuilt on the next request.

Exclusion rules list with one enabled rule
The rule list. Each row shows the label, whether it is enabled, the method, the path match and how many query conditions it carries.

A rule has seven fields:

Field Required Values Meaning
Label yes free text Name shown in the list. Not matched against anything.
Enabled no checkbox A disabled rule is kept but ignored.
HTTP method yes ANY, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS Restricts the rule to one method. ANY matches all.
Path match type yes exact, prefix, regex How Path pattern is compared to the request path.
Path pattern yes e.g. /foo/bar, /reports/, @^/reports/.*$@ The path to match. Regex patterns carry their own delimiters.
Query conditions no one per line Further narrowing, see below. All conditions must match.
Notes no free text For whoever reads the rule later. Write down why it exists.

Query conditions use key|operator|value, one per line. Supported operators are exists, missing, equals, regex, gte, lte and between, and between takes two values: from|between|2026-01-01|2026-01-31.

Add exclusion rule form filled in for the scrape endpoints
The rule that keeps external scrapers out of the observer tables: a prefix match on /native-observability/ for any method.

Use rules for traffic the checkbox cannot describe: a monitoring probe on a custom path, a health check, a cron endpoint, an integration that polls a route belonging to another module.

Rules have no retention: they stay until someone deletes them, and cleanup never touches them. That is deliberate. A rule is configuration, not observed data: it says what the site chooses not to record, and one that expired by itself would silently start recording a probe or a health check again, which is the opposite of what the operator asked for. The cost is that the Label and Notes fields are free text kept for as long as the rule exists, so do not write personal data in them; they are shown to everyone who holds the permission to manage exclusions. Delete a rule when the traffic it described is gone.

What each mechanism actually covers

Both mechanisms now reach every storage this module family writes to:

Storage Honours exclude_own_routes Honours exclusion rules
native_observability_trace Yes Yes
OTLP export Yes Yes
native_observability_cache_event Yes Yes
native_observability_database_query Yes Yes
native_observability_spans Yes Yes

The checkbox only ever matches this module's own route names; keep using exclusion rules for everything else, as above.

Custom span payloads

SpanRecorder is open to any code, so what it stores is not vouched for by this module family. The JSON payload of a span is capped at spans_payload_max_bytes. A reader, including a tool that exposes span rows to a client, must treat payload and name of non-http.client spans as untrusted input: it can be arbitrary text and, when over the limit, is only a marker with a preview of the original.

Cache observer settings

Cache observer settings
Cache observer settings. The three capture toggles are independent, so response headers, cacheability metadata and tag invalidations can be recorded separately.

Form: native_observability_cache_observer.settings at /admin/config/development/native-observability/settings/cache-observer.

Config key Type Default Meaning
capture_enabled boolean true Enable cache observer capture.
capture_response_headers boolean true Capture Drupal cache response headers.
capture_response_cacheability boolean true Capture response cacheability metadata.
capture_tag_invalidations boolean true Capture cache tag invalidation events.
retention_hours integer 72 Hours a cache observer record is retained.
report_limit integer 100 Maximum number of rows shown in the cache observer report.
max_stored_invalidations_per_request integer 50 Maximum number of tag invalidation rows stored per request.

Database observer settings

Database observer settings
Database observer settings. Only queries above the threshold reach the RAW table; normal counters stay in memory for trace enrichment. The form carries its own reference timing guidance.

Form: native_observability_database_observer.settings at /admin/config/development/native-observability/settings/database-observer.

Config key Type Default Meaning
capture_enabled boolean true Enable database observer capture.
slow_query_threshold_ms integer 100 Minimum query duration, in milliseconds, to be recorded as slow. Zero marks every query as slow.
store_slow_query_text boolean false Store the SQL text for queries above the threshold. New installations default to false; sites that already had the setting keep their value. String and numeric literals are always replaced with ? before storing.
max_stored_queries_per_request integer 20 Maximum number of raw query rows stored per request. Zero stores all of them, the way a row_limit of zero works in dblog.
retention_hours integer 72 Hours a database observer record is retained.
report_limit integer 100 Default maximum number of rows shown in the database observer report.

OpenTelemetry settings

OpenTelemetry settings
OpenTelemetry settings. The collector bearer token is stored in Drupal State, not in configuration, and the field shows only whether one is set.

Form: native_observability_otel.settings at /admin/config/development/native-observability/settings/otel.

Config key Type Default Meaning
enabled boolean false Enable the OpenTelemetry (OTLP) push exporter.
endpoint string '' OTLP HTTP endpoint traces are pushed to.
timeout float 2.0 HTTP timeout, in seconds, for each export call.
service_name string drupal service.name resource attribute sent with every export.
service_namespace string native_observability service.namespace resource attribute sent with every export.
service_version string 1.0.0 service.version resource attribute sent with every export.
headers sequence {} Extra HTTP headers sent with every OTLP export request.

The OTLP collector bearer token is stored in the Drupal State API, managed from this same form, drush no:token:generate otlp, or drush sset native_observability_otel.otlp_bearer_token.

Report settings

Form: native_observability_report.settings at /admin/config/development/native-observability/settings/report. Requires the administer site configuration permission.

Config key Type Default Meaning
use_custom_narrative_file boolean false Load report narrative text from a custom YAML file instead of the bundled one.
custom_narrative_file_path string '' Path to the custom narrative YAML file, used when use_custom_narrative_file is enabled.
fallback_language string en Language used when a narrative string is missing in the requested language.
strict_fallback boolean true Require strict language fallback handling instead of silently mixing languages.
default_detail_level string intermediate Default report detail level shown in the report builder.
default_mask_user_identifiers boolean true Mask user identifiers by default in generated reports.
default_hide_ip_addresses boolean true Hide IP addresses by default in generated reports.
default_hide_route_parameters boolean true Hide route parameters by default in generated reports.
overhead.display string hidden Publication state of the observability overhead section: hidden, manual (a value declared by the document author), or measured (the last value recorded by the overhead measurement command). See Observability overhead disclosure.
overhead.manual_percent float, nullable null Percentage shown when overhead.display is manual. Not a measurement: entered by whoever configures the report.
overhead.manual_note string '' Optional context shown alongside the manual value.
overhead.include_in_document boolean true Default value of the per-document "Include observability overhead" choice on the report builder form. Controls only whether the section is shown, independently of overhead.display.

Metrics settings

Config object native_observability_metrics.settings. It has no form: change it with drush cset or configuration import.

Config key Type Default Meaning
minimum_request_count integer 5 Smallest number of requests a route needs in the period to be returned by the MCP tools. Smaller groups are left out and counted as withheld.

Export settings

Form: native_observability_export.settings at /admin/config/development/native-observability/settings/export.

Config key Type Default Meaning
prometheus_window_seconds integer 300 Rolling window, in seconds, used to compute the Prometheus metrics served at scrape time.
prometheus_label_strategy string route_only Which trace columns become Prometheus label dimensions. See the warning below: the machine names do not describe what they emit.
prometheus_max_series integer 5000 Hard cap on label series per scrape. Beyond it the reader drops the lowest-count series and emits drupal_prometheus_truncated{reason="series_limit"} 1, so the truncation is visible in Grafana rather than silent.
prometheus_allow_unauthenticated boolean false Serve the Prometheus scrape endpoint without a bearer token. With no token configured and this off, the endpoint answers 403.

The bearer token is not configuration. It lives in Drupal State (native_observability_export.secret.prometheus_bearer_token) and is never written to exported configuration. Generate it with drush native_observability:token:generate prometheus, set it on the form, or submit __clear__ to remove it. The form renders a ready-to-copy scrape_configs block underneath, prefilled with this site's URL.

The label strategy machine names contradict their labels

The stored values do not describe what they emit, and exported configuration shows the value, not the label:

Stored value What it actually emits
route_only (default) route plus method plus status code
route_and_method route plus method only

Reading route_only in a config export and concluding that only the route is a label dimension is the natural reading, and it is wrong. The default is the middle option by cardinality, not the narrowest.

The former route_path_method strategy is gone: no label carries a path. Update 10003 of the export module switches a site that used it to route_only.

Export settings with the Prometheus endpoint options
Export settings. The token field is write-only, and the scrape config block at the bottom is generated for this site.

The Prometheus scrape bearer token is not part of this configuration object: it is stored in the Drupal State API and managed from this same form, drush no:token:generate prometheus, or drush sset native_observability_export.secret.prometheus_bearer_token.