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.
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:
anonymizedkeeps 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.hashedneeds 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¶
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.
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.
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¶
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¶
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¶
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.
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.