Commit Graph

16658 Commits

Author SHA1 Message Date
Pratik Mankawde
eb76645f69 docs(telemetry): name the collector stanzas instead of citing line numbers
The `service_name`, not `job` note pointed at three file:line locations.
All three had drifted, because the cited files move on every merge forward
and nothing checks the references. Name the `resource/logs` processor and
the `loki` service instead — those survive line drift and a rename breaks
a grep loudly.
2026-09-10 15:43:00 +01:00
Pratik Mankawde
ddff6019f2 docs(telemetry): give the Phase 11 validator board its own dashboard uid
Grafana keys a dashboard by uid, so the Phase 9 and Phase 11 rows both
claiming `validator-health` meant one would silently overwrite the other.
Phase11_taskList.md already requires `validator-health-external`; the
reference table now agrees with it, and says why.
2026-09-10 15:42:16 +01:00
Pratik Mankawde
5f68b22cec fix(telemetry): stop publishing the Grafana renderer on the host
Grafana reaches the image renderer over the compose network at
http://renderer:8081, so the host publish gave nothing the stack needs.
AUTH_TOKEN is the only guard on the endpoint and its default is a fixed
string in this file.

Update the service table in the configuration reference to match.
2026-09-10 15:42:02 +01:00
Pratik Mankawde
2b6a5733e7 merge: bring the component renames forward from phase8-log-correlation
Four doc conflicts, all where this branch had rewritten a passage that upstream
had only renamed. This branch's text is kept in seven of the eight hunks and
the spanmetrics -> span_metrics and otlp/tempo -> otlp_grpc/tempo spellings
carried into it, so the rewrite is not lost and the names stay current.

The exception is the TESTING.md span-call-count comment, where the incoming
side is the fuller text: it explains that the span_ prefix comes from the
connector's namespace setting. That side is taken.

Metric names are untouched — span_calls_total and traces_span_metrics_* are
produced by the connector's namespace, not by its component name.
2026-09-09 19:22:16 +01:00
Pratik Mankawde
6f28d46682 merge: bring the component renames forward from phase7-native-metrics
Two conflicts, both where this branch's log-pipeline additions sat next to the
upstream spanmetrics -> span_metrics rename: the config header comment, which
this branch extended with a logs line, and the integration test, where the
log-correlation step precedes the span-metrics step. This branch's content is
kept in both and the rename carried into it.
2026-09-09 19:20:45 +01:00
Pratik Mankawde
366bcaa328 merge: bring the component renames forward from phase6-statsd
Four conflicts, all where this branch's replacement of the StatsD path with
native OTLP met the upstream spanmetrics -> span_metrics rename. This branch's
design wins in every case; the rename is carried into its text rather than
reverting it, so the connector, its pipeline references, the header comment,
the TESTING.md summary and the runbook all use span_metrics while keeping the
native-OTLP wording.

One addition beyond a straight take-a-side: publish the collector's health
check port. This branch restored the health_check extension and its own
TESTING.md polls http://localhost:13133/ to decide the collector is ready, but
the port was never published on this side of docker-compose.yml, so that check
could not pass from the host. Verified the merged config loads with no
deprecation warnings and that 13133 is published exactly once.

No metric name changed: traces_span_metrics_* already read that way before the
rename, which only ever touched component names and prose.
2026-09-09 19:19:22 +01:00
Pratik Mankawde
988015dc65 merge: bring the OTLP gRPC exporter rename forward from phase5-docs-deployment
Conflict in docker/telemetry/otel-collector-config.yaml, in the service
pipelines: this branch renamed the deprecated spanmetrics connector to
span_metrics and adds the statsd metrics pipeline, while upstream renamed the
deprecated otlp exporter to otlp_grpc. Both kept.

With both renames present the collector now starts with no deprecation
warnings at all, which was the point of the pair.
2026-09-09 19:17:07 +01:00
Pratik Mankawde
49a3302e29 Merge branch 'pratik/otel-phase4-consensus-tracing' into pratik/otel-phase5-docs-deployment 2026-09-09 19:16:08 +01:00
Pratik Mankawde
c0024c7c57 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing 2026-09-09 19:16:08 +01:00
Pratik Mankawde
b98a9d3d13 Merge branch 'pratik/otel-phase2-rpc-tracing' into pratik/otel-phase3-tx-tracing 2026-09-09 19:16:08 +01:00
Pratik Mankawde
4dc706c606 merge: bring the OTLP gRPC exporter rename forward from phase1c-rpc-integration
Conflict in docker/telemetry/otel-collector-config.yaml, in the traces
pipeline: this branch added the attributes/hash processor while upstream
renamed the deprecated otlp exporter to otlp_grpc. Both sides kept — the
processor list keeps attributes/hash and the exporter list takes the new name.
Checked that the exporter definition key was renamed to match the reference,
and that the collector still loads the merged config.
2026-09-09 19:15:52 +01:00
Pratik Mankawde
282ce3620d Merge branch 'pratik/otel-phase1b-telemetry-infra' into pratik/otel-phase1c-rpc-integration 2026-09-09 19:13:28 +01:00
Pratik Mankawde
8b1e577931 fix(telemetry): tear down the docker volumes on every integration-test run
`docker compose down` keeps the `tempo-data` named volume, so a previous
run's traces stay in Tempo and can satisfy this run's span searches. Pass
-v in cleanup(), and tear the stack down before starting it: a run that
reaches the summary deliberately leaves the stack up, so nothing else
clears it.
2026-09-09 16:10:46 +01:00
Pratik Mankawde
d141405ff3 merge: bring the lock-free ValidationTracker forward from phase8-log-correlation
The three tracker files conflicted because both sides had rewritten them. Took
the incoming lock-free class, then put this branch's two monotonic accessors
back on top of it: totalAgreementsEver() and totalMissedEver(), backed by a
gross pair incremented at first classification and left alone by the repair
branch. MetricsRegistry reads both, so dropping them would not compile.

The test file kept the incoming suite, which renames every case, and gained
this branch's two gross-counter cases adapted to the injected clock.
2026-09-09 15:59:55 +01:00
Pratik Mankawde
5cb829a755 merge: bring the lock-free ValidationTracker forward from phase7-native-metrics
No conflicts. Carries the lock-free tracker, the metrics_endpoint scheme guard
with its repaired test set, and phase-6's Tempo time bounds.
2026-09-09 15:49:43 +01:00
Pratik Mankawde
a59dfe33e3 fix(telemetry): make the snapshot portable and repair the mTLS test set
Three CI failures, one cause each.

macOS could not compile the tracker: Apple's libc++ has no std::atomic for a
shared_ptr, so the primary template's trivially-copyable assert fired. Publish
through boost::atomic_shared_ptr instead, which every standard library the
matrix covers can build. boost/smart_ptr is already used in this tree. The
header's note no longer claims libstdc++ as the assumption.

Four tests that predate the metrics_endpoint scheme guard set tls_client_cert
and only put traces_endpoint on https, so the new guard threw before the check
each one asserts. They now set both endpoints. Nine tests in the two files set a
client cert; the other five stay correct because the pairing, use_tls and traces
checks all run ahead of the metrics one.

Eleven clang-tidy findings: redundant member initialisers, two aggregate
initialisations that wanted designated form, two unbraced bodies, three
unparenthesised multiplications, and a reserve before a loop that emplaces.
Dropping std::make_shared also left <memory> unused in both files.
2026-09-09 15:49:04 +01:00
Pratik Mankawde
c622da5a76 refactor(telemetry): use the current name for the span metrics connector
The pinned collector warns on every start that "spanmetrics" is a deprecated
alias for "span_metrics". Rename the connector, its pipeline references and
the prose that names it.

The derived metric names are untouched. They come from the connector's
`namespace` setting rather than its component name, so occurrences inside a
metric name such as traces_spanmetrics_calls_total are deliberately left as
they are; renaming those would break every span panel. The rename is applied
only to the bare word, never where it is joined to a metric name by
underscores.

This branch introduces the connector, so the change belongs here.
2026-09-09 15:40:11 +01:00
Pratik Mankawde
87457446be refactor(telemetry): use the current name for the OTLP gRPC exporter
The pinned collector warns on every start that "otlp" is a deprecated alias
for "otlp_grpc". Rename the trace exporter to otlp_grpc/tempo.

Only the exporter is affected. The otlp RECEIVER keeps its name: it serves
both gRPC and HTTP under one component and is not deprecated, verified by
renaming the exporter alone and seeing the warning stop.

This belongs on this branch because it introduces the exporter, and it is the
last of four deprecated aliases in the collector config; the other three are
owned by later branches in the chain.
2026-09-09 15:40:09 +01:00
Pratik Mankawde
7f8f5b4be3 feat(telemetry): ship logs through Alloy, alongside traces and metrics
Alloy carried no log pipeline at all: no loki.source, loki.write or
loki.process anywhere in the config, against a working metrics and OTLP path.
Any deployment fed through Alloy rather than the reference collector therefore
sent traces and metrics but no logs, so log-to-trace correlation was
unavailable there even though both ends of the link were configured.

Logs now leave through the same otlphttp exporter as the other two signals, so
all three carry one resource identity. Alloy's own filelog receiver would have
been the closest match to the reference collector, but it is still
public-preview and refuses to load unless the service is started with
--stability.level=public-preview, which would mean editing the unit on a box
reachable only by RunCommand. The Loki source components are generally
available, so they are used and bridged into OTLP by otelcol.receiver.loki;
the service needs no extra flag.

That bridge hands over an empty resource and puts everything on the log
record, so the transform sets the resource attributes in log context.
service.instance.id is concatenated in from XRPLD_HOST_LABEL because OTTL has
no env() converter, and it is a resource attribute rather than a record one
because only resource attributes are promoted to indexed Loki labels. It must
equal the node's own service_instance_id or the logs join nothing. devnet
writes one flat file rather than a per-node directory, so identity cannot be
read off the path the way the docker collector does it; XRPLD_LOG_GLOB
overrides the path for other layouts.

The line is parsed for its own timestamp, severity and trace context, and
trace_id/span_id are set on the first-class OTLP record fields so Grafana
links a log to its trace without re-parsing the body. Lines emitted outside a
sampled span keep an empty trace id rather than an invalid one.

Also carry the node-identity operators into the Grafana Cloud collector
variant, align this config's log directory with its service_instance_id, and
rename the deprecated otlphttp/filelog collector component names. Alloy's
otelcol.exporter.otlphttp is that product's own name and is unchanged.
2026-09-09 15:12:18 +01:00
Pratik Mankawde
cbb8581997 fix(telemetry): make the log pipeline actually deliver, and fix its docs
Addresses the open review findings on this branch.

The log root was never delivered at all. Docker creates a missing bind-mount
source as root, Config::getDebugLogFile() only warns when it cannot create the
network subdirectory inside it, and Application carries on. The node therefore
looked healthy while writing no debug.log, and Loki stayed empty with no error
at any layer. docker/telemetry/data/logs has in fact been root-owned in a
working checkout since it was first created. A one-shot xrpld-logdir-init
service now creates the directory and hands it to XRPLD_UID/XRPLD_GID,
following the pattern the storage-init service already uses.

Ingested logs carried no node identity, so a multi-node stack collapsed into
one indistinguishable stream while every dashboard filters on
service_instance_id. The receiver now sets include_file_path and lifts the
per-node directory onto the resource attribute service.instance.id, which is
on the allow-list Loki promotes to an indexed stream label. A record attribute
would only become structured metadata and could not be used in a selector.
For that to join anything the directory name has to equal the emitter's
service_instance_id, so the node directories are renamed to match: node$i
becomes Node-$i, and the standalone config writes to logs/xrpld-standalone.

The integration test aborted before reporting. Under set -o pipefail the
grep | head -1 pipeline is killed by SIGPIPE once the log exceeds the pipe
buffer, so the run exited 141 somewhere past a few hundred matching lines and
read as a flaky test. grep -m1 stops on its own. The test also verified the
local file and Tempo but never that a line reached Loki, which is the one hop
this branch adds, so a bounded Loki assertion is added alongside a readiness
wait.

Documentation fixes: the Tempo cross-check counted .data, but Tempo returns
OTLP shape so the array is batches and one trace can span several; the Loki
step used the instant /query endpoint, which rejects a bare log selector with
HTTP 400 and a text/plain body, so jq could never parse it and the step never
printed a number even when ingestion worked. The filelog comment claimed six
fractional digits where the node always emits nine. The two flowcharts used
<br/>, carried no legend, and advertised GetSpan(), which Log.cpp deliberately
avoids in favour of reading the thread-local context directly.

Finally, rename the deprecated collector component names: the pinned
collector warns on every start that otlphttp and filelog are aliases for
otlp_http and file_log. Alloy's otelcol.exporter.otlphttp and
otelcol.receiver.filelog are that product's own component names and are not
deprecated, so they are left alone.
2026-09-09 15:11:49 +01:00
Pratik Mankawde
ea4f7fd0d4 fix(telemetry): restore health_check and batch the metrics pipeline
The health_check extension was present on the previous branch and dropped
here with no replacement, while this branch's own TESTING.md still polls
http://localhost:13133/ to decide the collector is ready. That check has had
no listener since, so the documented readiness step cannot pass.

Also add batch to the metrics pipeline. Without it the OTLP metric path
exports one request per instrument; the added delay is bounded by the batch
timeout, well under the Prometheus scrape interval.

Both belong here rather than downstream: this branch owns the metrics
pipeline and is the one that regressed the extension.
2026-09-09 15:11:25 +01:00
Pratik Mankawde
cbd6cdbbdd merge: bring the integration-test bounds forward from phase6-statsd
Two files conflicted and both were composed rather than taken from one side.

integration-test.sh: kept this branch's spanmetrics names, since the collector
sets namespace: "span" here and traces_span_metrics_* matches nothing, and took
phase-6's --max-time on every probe. The Tempo time bound needed restoring by
hand: RUN_START, the check_span guard and the start/end parameters are on
phase-6 and absent here, so a plain resolution kept phase-6's comment about
bounding the search while shipping no bound. All four pieces are back.

TESTING.md: kept server=otel with the metrics endpoint. Phase-6's template sets
server=statsd and documents prefix, which this branch's OTel path ignores.
2026-09-09 15:03:08 +01:00
Pratik Mankawde
b6b7d46827 refactor(telemetry): make ValidationTracker lock-free
The two writer paths run on the consensus and ledger-publish threads, and both
took a single mutex that the nine window getters also held while scanning their
deques. At a week of 4-second ledgers those deques hold about 151,000 records,
the same record stored three times, so a read walked half a million entries and
the writers waited behind it.

Each writer now owns a 128-slot ring and shares nothing with the other, so a
record is one timestamp read and one store. A full ring drops the event and
bumps droppedEvents() rather than blocking a consensus thread; at 4-second
ledgers that needs about eight and a half minutes with no drain, against a
worst normal drain gap of one export interval plus one export timeout.

The windows become a grid of one-minute buckets with a running agreed and total
per span, so a getter reads a published snapshot instead of counting. Missed is
derived from the two, which leaves a late repair touching only the agreed side.
Steady-state footprint drops from about 8 MB to about 91 KB, and window edges
quantise to a minute.

Whichever thread is inside reconcile() owns the decision state; a second caller
returns instead of waiting. Readers take a shared_ptr to the snapshot, so the
values they read cannot be overwritten underneath them.

The clock is now injected, which is what makes bucket expiry reachable in a
test. The suite covers pairing, single-sided misses, the grace boundary, late
repair across a bucket edge and past the window, each window edge exactly, two
grid lengths of steady traffic, ring overflow, and three real-thread cases.
It no longer sleeps: nine sleep_for calls totalling 81 seconds are gone.

Every public method keeps its name and signature.
2026-09-09 14:38:56 +01:00
Pratik Mankawde
db0eae1b35 fix(telemetry): pass StartOptions, not a string, in telemetry-off tests
MetricsRegistry::start() takes a StartOptions aggregate. Six call sites in
the #ifndef XRPL_ENABLE_TELEMETRY block still passed a std::string, so the
block did not compile. Nothing in CI compiles it: telemetry defaults ON, and
the block is skipped whenever the macro is defined.

Add one shared kTestStartOptions carrying just the endpoint -- the other
fields are never read on the no-op path -- and correct two comments that
still described the old signature and a #else stub that does not exist.
2026-09-09 14:05:33 +01:00
Pratik Mankawde
8d2e2d15af docs(telemetry): correct stale code citations and the log-correlation claim
Every citation below was checked against the file it names:

- LedgerMaster.cpp:463 is fixIndex, not the ledger.store span; that guard is at
  :470 and the insert it wraps at :476
- LedgerMaster.cpp:987 is the tvc assignment, which sits BEFORE the tvc < minVal
  return at :988; the ledger.validate span opens at :1003
- ServerHandler.cpp:705 is inside makeJsonError; processRequest is at :718
- docker-compose.yml:71 and :75 are comments in the collector's volume block;
  the loki service is at :112 and its config command at :116

Two claims were also wrong rather than merely stale. Log-trace correlation is
gated in CI, because the workflow passes no --skip-loki, and the separate check
in integration-test.sh is run by no workflow at all. The Loki label note
described the Grafana Cloud collector config rather than the local one: only the
cloud variant sets job=xrpld, and the local config's own comment says to select
on service_name. The dashboards carry 35 Loki queries, not 38.
2026-09-09 13:14:21 +01:00
Pratik Mankawde
d972772747 docs(telemetry): match the manual test template to the OTel insight path
This branch switches integration-test.sh to [insight] server=otel and adds an
assertion that no StatsD listener is needed, but TESTING.md still described the
metrics it verifies as StatsD-derived and its manual node-config template had no
[insight] stanza at all.

CollectorManagerImp falls through to NullCollector when server is neither statsd
nor otel, so a reader building configs from that template got zero
beast::insight metrics. The template also omitted service_instance_id and
metrics_endpoint, which the script writes; without the former every node is
indistinguishable in the $node dashboard filter.
2026-09-09 13:14:15 +01:00
Pratik Mankawde
6976539274 fix(telemetry): bound the integration-test probes and align its config template
curl applies no overall timeout of its own, so a server that accepts the
connection and then stops answering parks a poll loop for the rest of the run
and the loop's attempt count stops bounding anything. Add a CURL_MAX_TIME
ceiling and apply it to all 18 executable probes in integration-test.sh.

TESTING.md's manual node-config template also disagreed with what the script
writes, so a reader following it could not reproduce the automated path:

- no [insight] stanza, so no beast::insight metric leaves the node at all and
  Step 10b's ten rippled_* assertions cannot pass
- [ips_fixed] listed all six peer ports including the node's own

The log level is deliberately untouched: the template and the script agree on
warning here.
2026-09-09 13:13:41 +01:00
Pratik Mankawde
bcf2d098d3 fix(telemetry): require https for metrics_endpoint under mTLS
With tls_client_cert set, only traces_endpoint was checked for an https
scheme. Telemetry::makeMetricExporter() attaches the client certificate and
key to the metric exporter whenever use_tls=1, and metrics_endpoint defaults
to a plain http URL, so an operator who set up mTLS and overrode only
traces_endpoint exported every metric in the clear with the configured client
identity unused.

Check both endpoints, and state the requirement under metrics_endpoint and
tls_client_cert in the example config. Four config tests cover an explicit
http metrics endpoint, the omitted-key default, both endpoints on https, and
a one-way-TLS control that must stay accepted.
2026-09-09 12:25:17 +01:00
Pratik Mankawde
144dcfe5a8 Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill 2026-09-09 12:02:30 +01:00
Pratik Mankawde
4a7ce8585b Merge branch 'pratik/otel-phase7-native-metrics' into pratik/otel-phase8-log-correlation 2026-09-09 12:02:22 +01:00
Pratik Mankawde
b978b52028 Merge branch 'pratik/otel-phase6-statsd' into pratik/otel-phase7-native-metrics 2026-09-09 12:02:22 +01:00
Pratik Mankawde
8b0bd8839d Merge branch 'pratik/otel-phase4-consensus-tracing' into pratik/otel-phase5-docs-deployment 2026-09-09 12:02:12 +01:00
Pratik Mankawde
f318191b18 Merge branch 'pratik/otel-phase5-docs-deployment' into pratik/otel-phase6-statsd 2026-09-09 12:02:12 +01:00
Pratik Mankawde
148d126c63 Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing 2026-09-09 12:01:59 +01:00
Pratik Mankawde
0eb4291688 fix(telemetry): drive the collection lifecycle in the StatsD tests
A StatsDCollector polls its metrics only after onCollectionReady(), so
neither test flushed anything: the gauge test timed out and the counter test
passed for the wrong reason. Call it in both, and drop the two includes
whose symbols the file never names.
2026-09-09 12:01:44 +01:00
Pratik Mankawde
302a76f73e fix(telemetry): make the readability probe stream const
The stream is only tested for failure, and both operator bool and operator!
are const members.
2026-09-09 12:01:42 +01:00
Pratik Mankawde
08a1ab8cd3 fix(telemetry): drop the unused <string> include from NullTelemetry
The file names only std::string_view, which <string_view> already provides.
2026-09-09 12:01:39 +01:00
Pratik Mankawde
96a3495328 fix(telemetry): brace the direct-apply status branch in TxQ
clang-tidy's readability-braces-around-statements exempts statements of at
most two lines, but not an if/else chain. Brace both arms.
2026-09-09 12:01:36 +01:00
Pratik Mankawde
bae31955db merge: bring the CI fixes forward from phase8-log-correlation
One conflicted file, docs/telemetry-runbook.md, with three spots:

- Build section: both sides added different text at one point. Kept both,
  incoming sentence first, then this branch's "Run against a live network".
- Disabling section, first spot: this branch's wording names the config
  section and says no rebuild is needed, so it already covers the incoming
  sentence.
- Disabling section, second spot: kept this branch's paragraph and folded in
  the one point it lacked, that both flags have to be passed.
2026-09-08 17:22:04 +01:00
Pratik Mankawde
75f2d042f4 merge: bring the CI fixes forward from phase7-native-metrics 2026-09-08 17:19:40 +01:00
Pratik Mankawde
ff1a905ede merge: bring the CI fixes forward from phase6-statsd
Three conflicts, all resolved by keeping this branch's rewrite and
re-applying the incoming change onto it:

- 09-data-collection-reference.md: phase-7 rewrote both attribute tables,
  so the incoming table would have reverted them. Kept phase-7's and
  re-applied the two "XRPL epoch" spellings.
- integration-test.sh: phase-7 moved these checks from StatsD to OTel and
  no longer defines check_statsd_metric, so only this side compiles.
- TelemetryConfig.cpp: the incoming side carried networkTypeFromId(), which
  this branch already has. Kept one definition and took the incoming
  doc wording, which the auto-merged body below it already matches.
2026-09-08 17:17:50 +01:00
Pratik Mankawde
e612e5d9fa merge: bring the CI fixes forward from phase5-docs-deployment 2026-09-08 17:14:06 +01:00
Pratik Mankawde
6528b02469 merge: bring the CI fixes forward from phase4-consensus-tracing 2026-09-08 17:14:06 +01:00
Pratik Mankawde
55ae890efd merge: bring the CI fixes forward from phase1c-rpc-integration 2026-09-08 17:13:51 +01:00
Pratik Mankawde
2ec09abf12 merge: bring the CI fixes forward from phase3-tx-tracing 2026-09-08 17:13:51 +01:00
Pratik Mankawde
b0fd72619a merge: bring the CI fixes forward from phase2-rpc-tracing 2026-09-08 17:13:51 +01:00
Pratik Mankawde
5190f643fa merge: bring the CI fixes forward from phase1b-telemetry-infra 2026-09-08 17:13:41 +01:00
Pratik Mankawde
cf719f224d merge: bring the CI fixes forward from phase1a-plan-docs 2026-09-08 17:13:41 +01:00
Pratik Mankawde
f3dc5c4e27 fix(test): compare the trace strategy against its enumerator
getConsensusTraceStrategy() returns ConsensusTraceStrategy, so comparing
it to a string literal does not compile.

No coverage is lost: strategyName()'s spelling has its own assertions in
the TelemetryConfig test, in this same binary.
2026-09-08 17:13:08 +01:00
Pratik Mankawde
bfa9f1b1e0 fix(test): rename the tlsPath namespace to tls_path
readability-identifier-naming wants lower_case for a namespace, so
clang-tidy failed on this file under warnings-as-errors. All seven use
sites move with the declaration.
2026-09-08 17:13:06 +01:00