From c3ccde3e3978086c86b0e883b74c5c83e489eb59 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Thu, 11 Jun 2026 22:17:31 +0100 Subject: [PATCH] docs(telemetry): document the span-attribute naming rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit State the rules so they stay consistent across code, collector, Tempo, dashboards, and docs: - Per-span-unique field -> bare name (the span name carries the domain). - Same concept on more than one span -> ONE shared key, reused verbatim and distinguished by span name, never tagged with the emitting workflow (e.g. ledger_hash, full_validation, proposal_trusted/validation_trusted). Defined once in the base SpanNames.h and re-exported by each domain header. - Collision qualifier _ only to separate DIFFERENT concepts that share a word, or the OTel-reserved status key (rpc_status, consensus_state). - Dotted xrpl.<...> is reserved for resource attributes (xrpl.network.*). Updates CONTRIBUTING.md (permanent home) and OpenTelemetryPlan §2.3.3. --- CONTRIBUTING.md | 27 ++++++++++++++++------- OpenTelemetryPlan/02-design-decisions.md | 28 +++++++++++++++--------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc56d5a6bb..1edb689756 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -307,14 +307,25 @@ across the code, the OTel collector, Tempo, Grafana dashboards, and docs. The constants in the `*SpanNames.h` headers are the single source of truth; every other layer must match them. A CI check enforces this end to end. -1. Per-span unique attribute: bare field name — the span name already carries - the domain (e.g. `command`, `local`, `version` on `rpc.command` / `tx.process`). -2. Collision qualifier: `_` when a bare name would collide across - domains (in the shared spanmetrics label space) or with the OTel-reserved - `status` key (e.g. `rpc_status`, `grpc_status`, `proposal_trusted`, - `validation_trusted`). -3. Shared cross-span attribute: `_` underscore form - (e.g. `tx_hash`, `peer_id`, `ledger_seq`, `consensus_round`). +1. Per-span unique attribute: bare field name — allowed when the field is + recorded by a single span/workflow, so the span name already supplies the + domain (e.g. `command`, `local`, `version` on `rpc.command` / `tx.process`). +2. Shared attribute (same concept on more than one span): ONE key, reused + verbatim on every span that records it — the span name tells the occurrences + apart, so no per-emitter prefix is added. Pick the name by the field's + meaning: a property of a domain object keeps that object's bare field name + (`ledger_hash`, `ledger_seq`, `tx_hash`, `peer_id`, `full_validation`); a + field already qualified by a sub-kind keeps that qualifier on every emitter + (`proposal_trusted` on both `consensus.proposal.receive` and + `peer.proposal.receive`; `validation_trusted` likewise). Define it once in + the base `SpanNames.h` `namespace attr` block and re-export (`using`) it from + each domain header, so all emitters share the exact string. +3. Collision qualifier: `_` — only when a bare name would collide + with a DIFFERENT concept in the shared spanmetrics label space, or with the + OTel-reserved `status` key (e.g. `rpc_status`, `grpc_status`, + `consensus_state`, `consensus_round`). This disambiguates distinct concepts + that share a word; it is NOT used to tag the same concept with the workflow + that emitted it — that is rule 2 (one shared name). 4. Resource attribute: dotted `xrpl..` — reserved ONLY for process/network identity set once at startup (`xrpl.network.id`, `xrpl.network.type`). Never use the dotted `xrpl.` form for span attributes. diff --git a/OpenTelemetryPlan/02-design-decisions.md b/OpenTelemetryPlan/02-design-decisions.md index 732eda3661..64d1da123b 100644 --- a/OpenTelemetryPlan/02-design-decisions.md +++ b/OpenTelemetryPlan/02-design-decisions.md @@ -170,16 +170,24 @@ headers are the single source of truth; the collector, Tempo, the Grafana dashboards, and the runbook all consume these exact keys, so every layer must agree with the code. A CI check enforces this end to end. -1. **Per-span unique attribute** → bare field name. The span name already - carries the domain, so no prefix is needed (e.g. `command`, `version`, - `local` on `rpc.command`). -2. **Collision qualifier** → `_` when a bare name would collide - across domains in the shared spanmetrics label space, or with the - OTel-reserved `status` key (e.g. `rpc_status`, `grpc_status`, - `proposal_trusted`, `validation_trusted`). -3. **Shared cross-span attribute** → `_` underscore form, used - wherever the same field appears on more than one span (e.g. `tx_hash`, - `peer_id`, `ledger_seq`, `consensus_round`, `consensus_mode`). +1. **Per-span unique attribute** → bare field name, allowed when the field is + recorded by a single span/workflow so the span name already supplies the + domain (e.g. `command`, `version`, `local` on `rpc.command`). +2. **Shared attribute (same concept on more than one span)** → ONE key, reused + verbatim on every span that records it; the span name tells the occurrences + apart, so no per-emitter prefix is added. Name it by the field's meaning: a + property of a domain object keeps that object's bare field name (`ledger_hash`, + `ledger_seq`, `tx_hash`, `peer_id`, `full_validation`); a field already + qualified by a sub-kind keeps that qualifier on every emitter (`proposal_trusted` + on both `consensus.proposal.receive` and `peer.proposal.receive`; + `validation_trusted` likewise). Defined once in the base `SpanNames.h` + `namespace attr` block and re-exported (`using`) by each domain header. +3. **Collision qualifier** → `_`, only when a bare name would + collide with a DIFFERENT concept in the shared spanmetrics label space or with + the OTel-reserved `status` key (e.g. `rpc_status`, `grpc_status`, + `consensus_state`, `consensus_round`, `consensus_mode`). This disambiguates + distinct concepts that share a word; it is NOT used to tag the same concept + with its emitting workflow — that is rule 2 (one shared name). 4. **Resource attribute** → dotted `xrpl..`, reserved ONLY for process/network identity set once at startup (`xrpl.network.id`, `xrpl.network.type`). Span attributes are never dotted in the `xrpl.` form —