gtest's EXPECT_EQ expands to an if/else, so an unbraced if around it is a
dangling-else error with warnings treated as errors. Found by the local
build of xrpl_tests.
Conflicts, all resolved by keeping phase-9's rewritten text and applying
the upstream change to it:
- PathRequest.cpp: drop the Redaction.h include, keep MetricMacros.h.
- XrplCore.cmake: keep the PUBLIC-link comment, note the SField table use.
- OpenTelemetryPlan.md, 02-design-decisions.md, 05-configuration-reference.md,
telemetry-runbook.md: account addresses are emitted raw; no hashing at
any layer; tx.process lists the per-role account attributes.
Conflicts in src/libxrpl/telemetry/TelemetryConfig.cpp and its test:
phase-5's "require an https endpoint whenever use_tls=1" met phase-7's
version of the same guard, which already covers both the traces and the
metrics endpoint. Kept phase-7's side.
A transaction names one or more accounts: the sender in Account, and by
type a Destination, Owner, Issuer, Holder and so on. tx.process now
carries each top-level account-typed field as its own attribute, keyed
tx_<field> in lower snake case (tx_account, tx_destination, ...), so an
account can be searched for in traces whatever role it played.
Addresses are public ledger identifiers and are emitted raw.
The keys live in TxAccountSpanNames.h in libxrpl, with a field-to-key
table in TxAccountSpanNames.cpp. A library test walks TxFormats and the
SField registry: every account field a transaction can carry has a key,
and no field that only ledger entries carry has one. An empty account
field is skipped rather than rendered as the zero address.
Rewrite the privacy policy (design decisions section 2.4.4) and the two
plan summaries that still described account hashing and a configurable
redaction. Add the two account attributes to the path-finding attribute
table and describe pathfind_dest_currency as the rendered asset.
An XRP account address is a public ledger identifier drawn from an
enumerable set. An unsalted hash of it is reversible by lookup, so it
protected nothing and only broke the join against explorers, RPC
responses and logs that show the same address.
- pathfind_source_account and pathfind_dest_account carry the request's
r-address. A value that does not parse as an r-address is not emitted,
so a malformed or mistaken request value never reaches a span. Both
handlers share setAccountAttribute() in PathFindSpanAttributes.h.
- pathfind_dest_currency is to_string(Asset): "XRP", "<issuer>/<CUR>",
or the MPT issuance id.
- The collector's attributes/hash processor is removed. No layer hashes.
- redactAccount() stays available; its header no longer claims to sit in
the emit path.
The alert notification ended with `Values: A=0 B=0 C=1`, the internal ids of the
three query steps, which mean nothing to a reader. The previous attempt removed
that line by replacing the whole email body with a custom template. That worked,
but it threw away everything else Grafana's default email gives: bold section
headings, a styled label table, annotations rendered as clickable links, and the
View and Silence buttons. The result read as a flat block of text and raw URLs.
Fix the line instead of the body. Rename all 13 rules' steps from A/B/C to names
that say what they hold -- close_rate_5m, close_rate_now, is_stalled -- so the
default body's value line reads close_rate_5m=0 close_rate_now=0 is_stalled=1.
The threshold step is named is_* so a 0/1 verdict is obviously a verdict and not
a rate.
Drop the email subject/message override so the default layout returns, and drop
the now-unused xrpld.email.body template. Slack keeps its own body: Grafana
escapes HTML in an email message, so a custom email body can only ever be plain
text, while Slack needs mrkdwn and <url|label> links. One body cannot serve both,
and only Slack benefits from having one at all.
Verified by provisioning the real files into Grafana: all 13 rules load with the
new step names and evaluate health=ok, and the Slack template renders with no
parse error.
The Collector interface said instruments are created in the owning class's
constructor, but not why, or what the boundary is. onCollectionReady() runs
once, after every service the hooks read exists; a collector starts polling and
arms its observable instruments at that point. A gauge made after it is never
armed, so it is never exported, and nothing reports that.
Name the boundary on the interface, and the consequence on makeGauge(), the one
instrument where being late fails silently.
Sharing one body between the Slack and email receivers made the email
unreadable. Email cannot render Slack markup, so the bold asterisks, the
backticks and the `🚨` shortcodes all arrived as literal
characters, and a `<url|label>` link could not become a link at all -- it dumped
the whole dashboard URL inline. Four panel links then buried the prose.
Split the body in two. The Slack body keeps mrkdwn. The email body is plain
text, one fact per line, with each dashboard link on its own labelled line.
Both still share the title and the node-identity fallback.
Measured while fixing this, and now recorded in templates.yaml: email escapes
any HTML in the message, so `<br>` arrives as `<br>` and no tag or anchor
is possible; but a newline in the template does become a real `<br>`, so line
breaks are the only layout tool email has.
Also correct two comments that were wrong. A missing template define does not
ship raw template text: it logs one warning and silently delivers Grafana's
default body, the value dump this file exists to remove, while the rule still
reports health=ok. And the Cloud contact point is not email-only; it holds a
Slack receiver and an email receiver, on an instance shared with other teams.
The parenting section still described them as ambient spans that could land
under an RPC command or under consensus.accept.apply. On this branch all three
derive their trace id from the ledger hash and are roots of the per-ledger
trace, whatever thread creates them.
expected_spans.json: composed both sides. Kept this branch's 16 extra spans,
relationships and the per_ledger join group; took phase-10's allowed_parents
gate for the shared entries and derived allowed_parents for the 16 from their
factories. ledger.store, ledger.validate and ledger.acquire are hashSpan roots
on this branch, so they list ROOT alone rather than consensus.accept.apply.
tx.apply carries its own ledger_seq. On the consensus path ledger.build,
ledger.store, ledger.validate and the queue spans nest under
consensus.accept.apply, which doAccept opens as a scoped guard, so the passage
that said no ambient span exists there is replaced by the edge it now has. The
per-stage failure-rate comment no longer claims the stages leave status unset
on a failing result; all three set an error status.