mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-29 01:50:43 +00:00
Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
# Conflicts: # src/xrpld/telemetry/MetricsRegistry.h # src/xrpld/telemetry/ValidationTracker.h
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
Loop: xrpl.telemetry xrpld.rpc
|
||||
xrpld.rpc > xrpl.telemetry
|
||||
|
||||
Loop: xrpld.app xrpld.overlay
|
||||
xrpld.app > xrpld.overlay
|
||||
|
||||
|
||||
@@ -322,6 +322,7 @@ xrpld.rpc > xrpl.rdb
|
||||
xrpld.rpc > xrpl.resource
|
||||
xrpld.rpc > xrpl.server
|
||||
xrpld.rpc > xrpl.shamap
|
||||
xrpld.rpc > xrpl.telemetry
|
||||
xrpld.rpc > xrpl.tx
|
||||
xrpld.shamap > xrpl.basics
|
||||
xrpld.shamap > xrpld.core
|
||||
|
||||
45
.github/scripts/otel-naming/check_otel_naming.py
vendored
45
.github/scripts/otel-naming/check_otel_naming.py
vendored
@@ -19,6 +19,16 @@ Design principles
|
||||
and the `join(seg::..., ...)` dotted resource compositions), and
|
||||
* the keys the code passes to `Resource::Create({ ... })` in Telemetry.cpp
|
||||
(the standard `semconv::service::*` keys -> service.name/version/...).
|
||||
The one narrow, explicit exception is EXTERNAL_INFRA_LABELS (Rules D & E):
|
||||
identity labels stamped by infrastructure outside this repo's OTel code
|
||||
(the perf-iac harness), which by definition have no source in-tree to
|
||||
derive from. Kept separate from the generic Prometheus/Grafana builtins
|
||||
set so the exception stays visible rather than blending into "things
|
||||
every OTel setup has". perf-iac's alloy pipeline stamps each identity at
|
||||
two layers -- dotted on the OTel resource attribute (xrpl.work.item/
|
||||
.branch/.node.role, checked by Rule E) and underscore on the derived
|
||||
Prometheus metric-datapoint label (xrpl_work_item/_branch/_node_role,
|
||||
checked by Rule D) -- so both forms are exempt from the same constant.
|
||||
|
||||
2. Presence-gated enforcement. Every rule runs ONLY when the source files it
|
||||
needs are present in the tree, and is otherwise skipped (never failed). This
|
||||
@@ -57,7 +67,9 @@ Rules (each FAILS the build, when its inputs are present)
|
||||
native-metric label, or a builtin. TraceQL `span.`/`resource.` scope
|
||||
prefixes are stripped before the L1 lookup.
|
||||
E No dotted `xrpl.<domain>.<field>` attribute key in the runbook (only the
|
||||
L1 resource attrs xrpl.network.* may be dotted). Span names, filenames,
|
||||
L1 resource attrs xrpl.network.* and the EXTERNAL_INFRA_LABELS dotted
|
||||
form -- xrpl.work.item/.branch/.node.role -- may be dotted). Span names,
|
||||
filenames,
|
||||
OTel-standard keys, and metric labels are not flagged.
|
||||
|
||||
Warnings (printed, but do NOT fail the build)
|
||||
@@ -800,6 +812,24 @@ def metric_label_names(root: Path) -> Set[str]:
|
||||
return labels
|
||||
|
||||
|
||||
# Identity labels stamped by EXTERNAL infrastructure the OTel pipeline in this
|
||||
# repo does not own: the perf-iac harness attaches these to every metric it
|
||||
# scrapes so dashboards can filter by which build/role produced a series. They
|
||||
# have no L1 (*SpanNames.h), L2 (collector config), or L6 (MetricsRegistry.cpp)
|
||||
# source to derive from, so — unlike every other dashboard label — they cannot
|
||||
# be validated dynamically. This is a deliberate, narrow exception to the "no
|
||||
# hardcoded allowlist" design principle, kept separate from the generic
|
||||
# Prometheus/Grafana `builtins` set below so it stays visible and auditable.
|
||||
# Add a label here ONLY if it is genuinely injected by infra outside this
|
||||
# repo's OTel code (never as a workaround for a dashboard querying a label
|
||||
# that nothing actually emits — that is a real Rule D violation).
|
||||
EXTERNAL_INFRA_LABELS = {
|
||||
"xrpl_work_item", # perf-iac: ticket/work-item id for the perf comparison run
|
||||
"xrpl_branch", # perf-iac: git ref of the xrpld build under test
|
||||
"xrpl_node_role", # perf-iac: validator/peer role in the perf cluster
|
||||
}
|
||||
|
||||
|
||||
def run_rule_d_dashboards(
|
||||
root: Path, l1_keys: Set[str], metric_labels: Set[str], report: Report
|
||||
) -> None:
|
||||
@@ -825,8 +855,9 @@ def run_rule_d_dashboards(
|
||||
"instance",
|
||||
}
|
||||
# A dashboard label is valid if it is a span attribute (L1), a native-metric
|
||||
# label (L6), or a Prometheus/Grafana builtin.
|
||||
valid = l1_keys | metric_labels | builtins
|
||||
# label (L6), a Prometheus/Grafana builtin, or an external-infra identity
|
||||
# label (EXTERNAL_INFRA_LABELS).
|
||||
valid = l1_keys | metric_labels | builtins | EXTERNAL_INFRA_LABELS
|
||||
found = False
|
||||
for f in files:
|
||||
try:
|
||||
@@ -877,10 +908,18 @@ def run_rule_e_runbook(root: Path, l1_keys: Set[str], report: Report) -> None:
|
||||
# Legitimate dotted resource attrs (`xrpl.network.id`/`.type`) are in L1 and
|
||||
# are skipped. A dotted `xrpl.` token absent from L1 is a genuine doc/code
|
||||
# mismatch (e.g. `xrpl.tx.hash` where the code emits `tx_hash`).
|
||||
# EXTERNAL_INFRA_LABELS (Rule D) holds the underscore/metric-label form of
|
||||
# the perf-iac identity attrs; the resource-attribute layer stamps the same
|
||||
# identities dotted (xrpl.work.item/.branch/.node.role -- see the alloy
|
||||
# pipeline that owns them), so also skip a token whose dotted-to-underscore
|
||||
# form is in that set.
|
||||
external_infra_dotted = {lbl.replace("_", ".") for lbl in EXTERNAL_INFRA_LABELS}
|
||||
for m in re.finditer(r"`(xrpl\.[a-z][a-z0-9_.]*)`", text):
|
||||
token = m.group(1)
|
||||
if token in l1_keys: # legitimate dotted resource attr (xrpl.network.*)
|
||||
continue
|
||||
if token in external_infra_dotted: # perf-iac resource-attribute layer
|
||||
continue
|
||||
found = True
|
||||
report.violation(
|
||||
"E", str(path.relative_to(root)), token, "underscore, not dotted"
|
||||
|
||||
@@ -121,6 +121,14 @@ class RuleERunbook(unittest.TestCase):
|
||||
def test_legit_dotted_resource_attrs_in_l1(self):
|
||||
self.assertEqual(_run_rule_e("`xrpl.network.id` `xrpl.network.type`"), [])
|
||||
|
||||
def test_external_infra_dotted_resource_attrs_not_flagged(self):
|
||||
# perf-iac stamps these as dotted resource attrs (alloy pipeline);
|
||||
# EXTERNAL_INFRA_LABELS (Rule D) holds their underscore metric-label
|
||||
# form -- Rule E must also exempt the dotted resource-attr form.
|
||||
self.assertEqual(
|
||||
_run_rule_e("`xrpl.work.item` `xrpl.branch` `xrpl.node.role`"), []
|
||||
)
|
||||
|
||||
def test_prose_word(self):
|
||||
self.assertEqual(_run_rule_e("the `command` attribute"), [])
|
||||
|
||||
@@ -744,6 +752,12 @@ class RuleDDashboards(unittest.TestCase):
|
||||
[],
|
||||
)
|
||||
|
||||
def test_external_infra_labels_not_flagged(self):
|
||||
# EXTERNAL_INFRA_LABELS (perf-iac identity labels with no in-tree
|
||||
# source) must be recognized as valid, distinct from `builtins`.
|
||||
expr = "sum by (" + ", ".join(sorted(chk.EXTERNAL_INFRA_LABELS)) + ") (x)"
|
||||
self.assertEqual(self._run(f'"expr": "{expr}"', set()), [])
|
||||
|
||||
def test_prometheus_name_label_not_flagged(self):
|
||||
# `__name__` is the Prometheus reserved metric-name label; the renamed
|
||||
# system-*.json dashboards use `sum by (le, __name__)`.
|
||||
|
||||
202
docker/telemetry/grafana/dashboards/fix_server_info_panels.py
Normal file
202
docker/telemetry/grafana/dashboards/fix_server_info_panels.py
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fix three Server Info panels on node-health (legends + colors), matching the
|
||||
conventions of the user's reference "Validated Ledger Seq — Current (Stat)"
|
||||
panel. Throwaway helper (not committed).
|
||||
|
||||
Operates on a dashboard JSON that was pulled FRESH from Grafana Cloud, so the
|
||||
user's live layout and their Validated Ledger Seq / Build Version panels are
|
||||
preserved verbatim -- only these three panels are edited in place:
|
||||
|
||||
- Server State : pie -> horizontal bargauge, one bar per state (node count),
|
||||
each bar coloured by state via value mappings (red/orange/yellow/blue/green).
|
||||
- Peer Count : bargauge -> stat, value_and_name + per-node legend + gradient
|
||||
colour (clones the reference stat panel's options/color).
|
||||
- Uptime : bargauge -> stat, same stat treatment, unit seconds.
|
||||
|
||||
Build Version and the Validated Ledger Seq * panels are intentionally left
|
||||
untouched (the user owns those).
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
FILTERS = (
|
||||
'service_instance_id=~"$node", deployment_environment=~"$deployment_environment", '
|
||||
'xrpl_network_type=~"$xrpl_network_type", service_name=~"$service_name", '
|
||||
'xrpl_work_item=~"$xrpl_work_item", xrpl_branch=~"$xrpl_branch", '
|
||||
'xrpl_node_role=~"$xrpl_node_role"'
|
||||
)
|
||||
|
||||
DS = {"type": "prometheus", "uid": "${DS_PROMETHEUS}"}
|
||||
|
||||
# state value -> (name, colour) for the Server State bars.
|
||||
STATES = [
|
||||
("0", "DISCONNECTED", "red"),
|
||||
("1", "CONNECTED", "orange"),
|
||||
("2", "SYNCING", "yellow"),
|
||||
("3", "TRACKING", "blue"),
|
||||
("4", "FULL", "green"),
|
||||
]
|
||||
|
||||
|
||||
def find(panels, title):
|
||||
for p in panels:
|
||||
if p.get("title") == title:
|
||||
return p
|
||||
if "panels" in p:
|
||||
r = find(p["panels"], title)
|
||||
if r:
|
||||
return r
|
||||
return None
|
||||
|
||||
|
||||
def fix_server_state(p):
|
||||
"""Pie -> bargauge, one bar per state (node count), state-coloured."""
|
||||
expr = 'count_values("state", server_info{%s, metric="server_state"})' % FILTERS
|
||||
for num, name, _ in STATES:
|
||||
expr = 'label_replace(%s, "state", "%s", "state", "%s")' % (expr, name, num)
|
||||
p["type"] = "bargauge"
|
||||
p["targets"] = [
|
||||
{
|
||||
"datasource": DS,
|
||||
"expr": expr,
|
||||
"instant": True,
|
||||
"legendFormat": "{{state}}",
|
||||
"refId": "A",
|
||||
}
|
||||
]
|
||||
p["options"] = {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": False},
|
||||
"showUnfilled": True,
|
||||
"valueMode": "color",
|
||||
}
|
||||
# colour each bar by its state name (value mapping sets text colour).
|
||||
p["fieldConfig"] = {
|
||||
"defaults": {
|
||||
"color": {"mode": "fixed"},
|
||||
"displayName": "${__field.labels.state}",
|
||||
"mappings": [
|
||||
{"type": "value", "options": {name: {"index": i, "color": colour}}}
|
||||
for i, (_, name, colour) in enumerate(STATES)
|
||||
],
|
||||
"unit": "short",
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {"id": "byName", "options": name},
|
||||
"properties": [
|
||||
{"id": "color", "value": {"mode": "fixed", "fixedColor": colour}}
|
||||
],
|
||||
}
|
||||
for _, name, colour in STATES
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def to_reference_stat(p, expr, unit):
|
||||
"""bargauge -> stat, cloning the reference 'Current (Stat)' panel: per-node
|
||||
legend, value_and_name text, continuous colour."""
|
||||
p["type"] = "stat"
|
||||
p["targets"] = [
|
||||
{
|
||||
"datasource": DS,
|
||||
"expr": expr,
|
||||
"instant": True,
|
||||
"legendFormat": "{{service_instance_id}}",
|
||||
"refId": "A",
|
||||
}
|
||||
]
|
||||
p["options"] = {
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "center",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": False},
|
||||
"textMode": "value_and_name",
|
||||
"wideLayout": True,
|
||||
}
|
||||
p["fieldConfig"] = {
|
||||
"defaults": {
|
||||
"color": {"mode": "continuous-RdYlGr"},
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [{"color": "green", "value": 0}],
|
||||
},
|
||||
"unit": unit,
|
||||
},
|
||||
"overrides": [],
|
||||
}
|
||||
|
||||
|
||||
def fix(dash):
|
||||
panels = dash["panels"]
|
||||
fix_server_state(find(panels, "Server State"))
|
||||
to_reference_stat(
|
||||
find(panels, "Peer Count"), 'server_info{%s, metric="peers"}' % FILTERS, "short"
|
||||
)
|
||||
to_reference_stat(
|
||||
find(panels, "Uptime"), 'server_info{%s, metric="uptime"}' % FILTERS, "s"
|
||||
)
|
||||
return dash
|
||||
|
||||
|
||||
def _dump(obj, lvl=0):
|
||||
pad = " " * lvl
|
||||
pad1 = " " * (lvl + 1)
|
||||
if isinstance(obj, dict):
|
||||
if not obj:
|
||||
return "{}"
|
||||
return (
|
||||
"{\n"
|
||||
+ ",\n".join(
|
||||
"%s%s: %s" % (pad1, json.dumps(k), _dump(v, lvl + 1))
|
||||
for k, v in obj.items()
|
||||
)
|
||||
+ "\n"
|
||||
+ pad
|
||||
+ "}"
|
||||
)
|
||||
if isinstance(obj, list):
|
||||
if not obj:
|
||||
return "[]"
|
||||
if all(isinstance(x, (str, int, float, bool)) or x is None for x in obj):
|
||||
return "[" + ", ".join(json.dumps(x) for x in obj) + "]"
|
||||
return (
|
||||
"[\n"
|
||||
+ ",\n".join("%s%s" % (pad1, _dump(x, lvl + 1)) for x in obj)
|
||||
+ "\n"
|
||||
+ pad
|
||||
+ "]"
|
||||
)
|
||||
return json.dumps(obj)
|
||||
|
||||
|
||||
# Runtime-only keys the Grafana API returns that must NOT be written to the
|
||||
# repo file (they are instance state, not dashboard definition).
|
||||
_RUNTIME_KEYS = ("id", "version", "liveNow")
|
||||
|
||||
|
||||
def main():
|
||||
"""argv: <live.json> <dest1.json> [dest2.json ...]
|
||||
|
||||
The live pull is the source of truth for layout + the user's panels. For each
|
||||
dest we start from that dest's EXISTING top-level shape (to keep repo-only
|
||||
keys like ``links`` and avoid importing runtime keys), then replace only the
|
||||
``panels`` array with the fixed live layout."""
|
||||
live = json.load(open(sys.argv[1]))
|
||||
fix(live)
|
||||
for dest in sys.argv[2:]:
|
||||
base = json.load(open(dest)) # keep dest's own top-level keys
|
||||
base["panels"] = live["panels"] # adopt live layout + fixed panels
|
||||
for k in _RUNTIME_KEYS:
|
||||
base.pop(k, None)
|
||||
out = _dump(base) + "\n"
|
||||
json.loads(out)
|
||||
open(dest, "w").write(out)
|
||||
print("wrote", dest, "(%d top-level panels)" % len(base["panels"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
177
docker/telemetry/grafana/dashboards/retype_server_info.py
Normal file
177
docker/telemetry/grafana/dashboards/retype_server_info.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Change the render type of four Server Info tiles on node-health, and append
|
||||
the user's live-authored ledger-seq panels. Throwaway helper (not committed).
|
||||
|
||||
Transforms (type + query/options only; gridPos kept):
|
||||
- Build Version : stat -> piechart, slices = count by (version, xrpl_branch)
|
||||
- Server State : stat -> piechart, slices = node count per state (keep 0-4
|
||||
value/color mappings)
|
||||
- Peer Count : stat -> bargauge (horizontal), one bar per node
|
||||
- Uptime : stat -> bargauge (horizontal), one bar per node
|
||||
|
||||
The user added three panels directly on Grafana Cloud (a row + two ledger-seq
|
||||
stat panels); those are re-appended verbatim from /tmp/nh_added_panels.json so a
|
||||
repo write does not drop them.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
FILTERS = (
|
||||
'service_instance_id=~"$node", deployment_environment=~"$deployment_environment", '
|
||||
'xrpl_network_type=~"$xrpl_network_type", service_name=~"$service_name", '
|
||||
'xrpl_work_item=~"$xrpl_work_item", xrpl_branch=~"$xrpl_branch", '
|
||||
'xrpl_node_role=~"$xrpl_node_role"'
|
||||
)
|
||||
|
||||
ADDED_PANELS = "/tmp/nh_added_panels.json"
|
||||
|
||||
|
||||
def find(panels, title):
|
||||
for p in panels:
|
||||
if p.get("title") == title:
|
||||
return p
|
||||
if "panels" in p:
|
||||
r = find(p["panels"], title)
|
||||
if r:
|
||||
return r
|
||||
return None
|
||||
|
||||
|
||||
def to_piechart(p, expr, legend):
|
||||
"""Convert a panel to a piechart with one target + legend."""
|
||||
p["type"] = "piechart"
|
||||
p["targets"] = [
|
||||
{
|
||||
"datasource": {"type": "prometheus", "uid": "${DS_PROMETHEUS}"},
|
||||
"expr": expr,
|
||||
"legendFormat": legend,
|
||||
"refId": "A",
|
||||
}
|
||||
]
|
||||
p["options"] = {
|
||||
"legend": {"displayMode": "list", "placement": "right", "values": ["value"]},
|
||||
"pieType": "pie",
|
||||
"reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": False},
|
||||
"tooltip": {"mode": "single", "sort": "desc"},
|
||||
}
|
||||
# piechart uses field displayName for slice labels, not the xrpl_ident stat form
|
||||
p.setdefault("fieldConfig", {}).setdefault("defaults", {}).pop("displayName", None)
|
||||
|
||||
|
||||
def to_bargauge(p, expr):
|
||||
"""Convert a panel to a horizontal bar gauge, one bar per node."""
|
||||
p["type"] = "bargauge"
|
||||
for t in p["targets"]:
|
||||
t["expr"] = expr
|
||||
t["instant"] = False
|
||||
t["legendFormat"] = "{{service_instance_id}}"
|
||||
p["options"] = {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": False},
|
||||
"showUnfilled": True,
|
||||
"valueMode": "color",
|
||||
}
|
||||
# bar label is the per-node legend, not the stat xrpl_ident bracket
|
||||
p.setdefault("fieldConfig", {}).setdefault("defaults", {})[
|
||||
"displayName"
|
||||
] = "{{service_instance_id}}"
|
||||
|
||||
|
||||
def retype(dash):
|
||||
panels = dash["panels"]
|
||||
|
||||
# 1) Build Version -> pie by (version, xrpl_branch)
|
||||
bv = find(panels, "Build Version")
|
||||
to_piechart(
|
||||
bv,
|
||||
"count by (version, xrpl_branch) (build_info{%s})" % FILTERS,
|
||||
"{{version}} {{xrpl_branch}}",
|
||||
)
|
||||
|
||||
# 2) Server State -> pie of node count per state. server_state is a metric
|
||||
# VALUE (0-4), not a label, so count_values buckets nodes by that value
|
||||
# into a new "state" label; the nested label_replace chain then renames
|
||||
# 0-4 to DISCONNECTED..FULL so the pie slices read as state names. (Value
|
||||
# mappings can't help here -- they map field values, not legend labels.)
|
||||
ss = find(panels, "Server State")
|
||||
state_expr = (
|
||||
'count_values("state", server_info{%s, metric="server_state"})' % FILTERS
|
||||
)
|
||||
for num, name in (
|
||||
("0", "DISCONNECTED"),
|
||||
("1", "CONNECTED"),
|
||||
("2", "SYNCING"),
|
||||
("3", "TRACKING"),
|
||||
("4", "FULL"),
|
||||
):
|
||||
state_expr = 'label_replace(%s, "state", "%s", "state", "%s")' % (
|
||||
state_expr,
|
||||
name,
|
||||
num,
|
||||
)
|
||||
to_piechart(ss, state_expr, "{{state}}")
|
||||
|
||||
# 3) Peer Count -> bargauge per node
|
||||
pc = find(panels, "Peer Count")
|
||||
to_bargauge(pc, 'server_info{%s, metric="peers"}' % FILTERS)
|
||||
|
||||
# 4) Uptime -> bargauge per node
|
||||
up = find(panels, "Uptime")
|
||||
to_bargauge(up, 'server_info{%s, metric="uptime"}' % FILTERS)
|
||||
|
||||
# 5) Re-append the user's live-authored panels if not already present.
|
||||
have = {p.get("title") for p in panels}
|
||||
added = json.load(open(ADDED_PANELS))
|
||||
for p in added:
|
||||
if p.get("title") not in have:
|
||||
panels.append(p)
|
||||
|
||||
return dash
|
||||
|
||||
|
||||
# --- serializer matching the dashboards' on-disk byte format ---------------
|
||||
def _dump(obj, lvl=0):
|
||||
pad = " " * lvl
|
||||
pad1 = " " * (lvl + 1)
|
||||
if isinstance(obj, dict):
|
||||
if not obj:
|
||||
return "{}"
|
||||
return (
|
||||
"{\n"
|
||||
+ ",\n".join(
|
||||
"%s%s: %s" % (pad1, json.dumps(k), _dump(v, lvl + 1))
|
||||
for k, v in obj.items()
|
||||
)
|
||||
+ "\n"
|
||||
+ pad
|
||||
+ "}"
|
||||
)
|
||||
if isinstance(obj, list):
|
||||
if not obj:
|
||||
return "[]"
|
||||
if all(isinstance(x, (str, int, float, bool)) or x is None for x in obj):
|
||||
return "[" + ", ".join(json.dumps(x) for x in obj) + "]"
|
||||
return (
|
||||
"[\n"
|
||||
+ ",\n".join("%s%s" % (pad1, _dump(x, lvl + 1)) for x in obj)
|
||||
+ "\n"
|
||||
+ pad
|
||||
+ "]"
|
||||
)
|
||||
return json.dumps(obj)
|
||||
|
||||
|
||||
def main():
|
||||
for path in sys.argv[1:]:
|
||||
d = json.load(open(path))
|
||||
retype(d)
|
||||
out = _dump(d) + "\n"
|
||||
json.loads(out)
|
||||
open(path, "w").write(out)
|
||||
print("%s: retyped 4 panels, +%d user panels" % (path, 3))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -231,9 +231,10 @@ public:
|
||||
virtual telemetry::Telemetry&
|
||||
getTelemetry() = 0;
|
||||
|
||||
/** Return the MetricsRegistry, or nullptr if telemetry is disabled.
|
||||
Used by PerfLog and other hot paths to record OTel metrics.
|
||||
*/
|
||||
/**
|
||||
* Return the MetricsRegistry, or nullptr if telemetry is disabled.
|
||||
* Used by PerfLog and other hot paths to record OTel metrics.
|
||||
*/
|
||||
virtual telemetry::MetricsRegistry*
|
||||
getMetricsRegistry() = 0;
|
||||
|
||||
|
||||
@@ -103,11 +103,12 @@ public:
|
||||
|
||||
[[nodiscard]] virtual OperatingMode
|
||||
getOperatingMode() const = 0;
|
||||
/** Time spent in the current operating mode so far, in microseconds.
|
||||
Same value as `server_state_duration_us` in server_info; exposed as a
|
||||
lightweight accessor so metrics can read it without building the full
|
||||
server_info JSON on every collection tick.
|
||||
*/
|
||||
/**
|
||||
* Time spent in the current operating mode so far, in microseconds.
|
||||
* Same value as `server_state_duration_us` in server_info; exposed as a
|
||||
* lightweight accessor so metrics can read it without building the full
|
||||
* server_info JSON on every collection tick.
|
||||
*/
|
||||
[[nodiscard]] virtual std::chrono::microseconds
|
||||
getServerStateDurationUs() const = 0;
|
||||
[[nodiscard]] virtual std::string
|
||||
|
||||
@@ -1,56 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
/** Thread-local discard signaling between SpanGuard and the span processor.
|
||||
|
||||
SpanGuard::discard() wants to drop a span without sending it to the
|
||||
exporter. The OTel SDK calls SpanProcessor::OnEnd() synchronously on the
|
||||
same thread that calls Span::End(), so a thread-local flag set just before
|
||||
End() and read inside OnEnd() lets FilteringSpanProcessor drop the span
|
||||
before it enters the batch export queue.
|
||||
|
||||
This side-channel avoids inspecting the Recordable's internals (which vary
|
||||
by exporter type — SpanData vs OtlpRecordable).
|
||||
|
||||
The flag is a *private* thread-local member of DiscardScope, mutated only by
|
||||
its constructor and destructor. This gives real access control rather than a
|
||||
naming convention: no code that includes this header can flip the flag
|
||||
directly — it can only enter a DiscardScope (which sets and clears the flag
|
||||
over its own lifetime) and observe the state via DiscardScope::isActive().
|
||||
Binding set/clear to a scope also means the flag cannot leak onto the next
|
||||
span even if End() were to throw.
|
||||
|
||||
Kept in a separate header to avoid transitive include bloat: SpanGuard.h
|
||||
only needs this signaling, not the full Telemetry.h with BasicConfig/Journal.
|
||||
|
||||
Usage:
|
||||
@code
|
||||
// In SpanGuard::discard():
|
||||
{
|
||||
DiscardScope discardScope; // flag set for this scope only
|
||||
span->End(); // OnEnd() runs synchronously, sees flag
|
||||
} // flag cleared here, unconditionally
|
||||
|
||||
// In FilteringSpanProcessor::OnEnd():
|
||||
if (DiscardScope::isActive())
|
||||
return; // drop the span
|
||||
@endcode
|
||||
|
||||
@note Thread safety: the flag is thread-local, so each thread observes only
|
||||
its own discard signal — no synchronization is required.
|
||||
|
||||
@see SpanGuard::discard(), FilteringSpanProcessor (Telemetry.cpp)
|
||||
*/
|
||||
/**
|
||||
* Thread-local discard signaling between SpanGuard and the span processor.
|
||||
*
|
||||
* SpanGuard::discard() wants to drop a span without sending it to the
|
||||
* exporter. The OTel SDK calls SpanProcessor::OnEnd() synchronously on the
|
||||
* same thread that calls Span::End(), so a thread-local flag set just before
|
||||
* End() and read inside OnEnd() lets FilteringSpanProcessor drop the span
|
||||
* before it enters the batch export queue.
|
||||
*
|
||||
* This side-channel avoids inspecting the Recordable's internals (which vary
|
||||
* by exporter type — SpanData vs OtlpRecordable).
|
||||
*
|
||||
* The flag is a *private* thread-local member of DiscardScope, mutated only by
|
||||
* its constructor and destructor. This gives real access control rather than a
|
||||
* naming convention: no code that includes this header can flip the flag
|
||||
* directly — it can only enter a DiscardScope (which sets and clears the flag
|
||||
* over its own lifetime) and observe the state via DiscardScope::isActive().
|
||||
* Binding set/clear to a scope also means the flag cannot leak onto the next
|
||||
* span even if End() were to throw.
|
||||
*
|
||||
* Kept in a separate header to avoid transitive include bloat: SpanGuard.h
|
||||
* only needs this signaling, not the full Telemetry.h with BasicConfig/Journal.
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* // In SpanGuard::discard():
|
||||
* {
|
||||
* DiscardScope discardScope; // flag set for this scope only
|
||||
* span->End(); // OnEnd() runs synchronously, sees flag
|
||||
* } // flag cleared here, unconditionally
|
||||
*
|
||||
* // In FilteringSpanProcessor::OnEnd():
|
||||
* if (DiscardScope::isActive())
|
||||
* return; // drop the span
|
||||
* @endcode
|
||||
*
|
||||
* @note Thread safety: the flag is thread-local, so each thread observes only
|
||||
* its own discard signal — no synchronization is required.
|
||||
*
|
||||
* @see SpanGuard::discard(), FilteringSpanProcessor (Telemetry.cpp)
|
||||
*/
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** RAII guard that marks the current thread's span for discard.
|
||||
|
||||
Sets the thread-local discard flag on construction and clears it on
|
||||
destruction, so a span ended within the guard's scope is dropped by
|
||||
FilteringSpanProcessor::OnEnd() while the flag stays confined to that scope.
|
||||
The flag is private and mutated only here, so no other code can set it.
|
||||
Non-copyable and non-movable — its sole purpose is the scoped flag lifetime.
|
||||
*/
|
||||
/**
|
||||
* RAII guard that marks the current thread's span for discard.
|
||||
*
|
||||
* Sets the thread-local discard flag on construction and clears it on
|
||||
* destruction, so a span ended within the guard's scope is dropped by
|
||||
* FilteringSpanProcessor::OnEnd() while the flag stays confined to that scope.
|
||||
* The flag is private and mutated only here, so no other code can set it.
|
||||
* Non-copyable and non-movable — its sole purpose is the scoped flag lifetime.
|
||||
*/
|
||||
class DiscardScope
|
||||
{
|
||||
public:
|
||||
@@ -71,9 +73,11 @@ public:
|
||||
DiscardScope&
|
||||
operator=(DiscardScope&&) = delete;
|
||||
|
||||
/** @return true if the current thread is inside a DiscardScope, i.e. the
|
||||
span ending now should be dropped rather than exported. Read by
|
||||
FilteringSpanProcessor::OnEnd(). */
|
||||
/**
|
||||
* @return true if the current thread is inside a DiscardScope, i.e. the
|
||||
* span ending now should be dropped rather than exported. Read by
|
||||
* FilteringSpanProcessor::OnEnd().
|
||||
*/
|
||||
[[nodiscard]] static bool
|
||||
isActive() noexcept
|
||||
{
|
||||
@@ -81,8 +85,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/** Thread-local discard flag. Private, so only this class's ctor/dtor can
|
||||
mutate it; observers use isActive(). One instance per thread. */
|
||||
/**
|
||||
* Thread-local discard flag. Private, so only this class's ctor/dtor can
|
||||
* mutate it; observers use isActive(). One instance per thread.
|
||||
*/
|
||||
inline static thread_local bool discarding = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,63 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
/** Account-address redaction for telemetry span attributes.
|
||||
|
||||
Path-finding RPC handlers would otherwise emit the caller's raw
|
||||
account addresses as span attributes. To keep plaintext addresses out
|
||||
of the telemetry backend, they are hashed at the point of emission.
|
||||
This header exposes a single pure helper that turns an address into a
|
||||
short, stable, obfuscated token.
|
||||
|
||||
Data flow:
|
||||
|
||||
handler -> redactAccount(addr) -> span attribute -> OTLP export
|
||||
|
||||
The returned token is the first 16 hex characters (lowercase) of the
|
||||
SHA-512Half digest of the address. It is deterministic (same address
|
||||
always maps to the same token) so operators can still correlate spans
|
||||
for a given account across nodes and restarts.
|
||||
|
||||
The hash is unsalted, so it is obfuscation, not a secrecy guarantee:
|
||||
XRP account addresses are a public, enumerable set, so a determined
|
||||
observer with the telemetry stream could rebuild the address->token
|
||||
mapping. The goal here is to keep plaintext addresses out of traces
|
||||
and dashboards, not to defend against a precomputation attack. A salt
|
||||
is intentionally omitted because it would break cross-node/restart
|
||||
correlation, which is the reason for hashing rather than dropping.
|
||||
|
||||
A second, independent hashing layer runs in the OpenTelemetry
|
||||
Collector (an `attributes/hash` processor) as defense-in-depth for
|
||||
any node that emits a raw value.
|
||||
|
||||
@note This function is pure and reentrant: it holds no global state,
|
||||
performs no I/O, and is safe to call concurrently from any thread.
|
||||
|
||||
Usage example:
|
||||
@code
|
||||
#include <xrpl/telemetry/Redaction.h>
|
||||
using namespace xrpl::telemetry;
|
||||
|
||||
span.setAttribute(
|
||||
pathfind_span::attr::sourceAccount, redactAccount(src.asString()));
|
||||
@endcode
|
||||
|
||||
Edge case (empty input yields empty output):
|
||||
@code
|
||||
assert(redactAccount("") == "");
|
||||
@endcode
|
||||
*/
|
||||
/**
|
||||
* Account-address redaction for telemetry span attributes.
|
||||
*
|
||||
* Path-finding RPC handlers would otherwise emit the caller's raw
|
||||
* account addresses as span attributes. To keep plaintext addresses out
|
||||
* of the telemetry backend, they are hashed at the point of emission.
|
||||
* This header exposes a single pure helper that turns an address into a
|
||||
* short, stable, obfuscated token.
|
||||
*
|
||||
* Data flow:
|
||||
*
|
||||
* handler -> redactAccount(addr) -> span attribute -> OTLP export
|
||||
*
|
||||
* The returned token is the first 16 hex characters (lowercase) of the
|
||||
* SHA-512Half digest of the address. It is deterministic (same address
|
||||
* always maps to the same token) so operators can still correlate spans
|
||||
* for a given account across nodes and restarts.
|
||||
*
|
||||
* The hash is unsalted, so it is obfuscation, not a secrecy guarantee:
|
||||
* XRP account addresses are a public, enumerable set, so a determined
|
||||
* observer with the telemetry stream could rebuild the address->token
|
||||
* mapping. The goal here is to keep plaintext addresses out of traces
|
||||
* and dashboards, not to defend against a precomputation attack. A salt
|
||||
* is intentionally omitted because it would break cross-node/restart
|
||||
* correlation, which is the reason for hashing rather than dropping.
|
||||
*
|
||||
* A second, independent hashing layer runs in the OpenTelemetry
|
||||
* Collector (an `attributes/hash` processor) as defense-in-depth for
|
||||
* any node that emits a raw value.
|
||||
*
|
||||
* @note This function is pure and reentrant: it holds no global state,
|
||||
* performs no I/O, and is safe to call concurrently from any thread.
|
||||
*
|
||||
* Usage example:
|
||||
* @code
|
||||
* #include <xrpl/telemetry/Redaction.h>
|
||||
* using namespace xrpl::telemetry;
|
||||
*
|
||||
* span.setAttribute(
|
||||
* pathfind_span::attr::sourceAccount, redactAccount(src.asString()));
|
||||
* @endcode
|
||||
*
|
||||
* Edge case (empty input yields empty output):
|
||||
* @code
|
||||
* assert(redactAccount("") == "");
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** Hash an account address into a short, stable, obfuscated token.
|
||||
|
||||
@param addr The account address to redact (e.g. an r-address).
|
||||
@return The first 16 lowercase hex characters of sha512Half(addr),
|
||||
or an empty string when @p addr is empty.
|
||||
*/
|
||||
/**
|
||||
* Hash an account address into a short, stable, obfuscated token.
|
||||
*
|
||||
* @param addr The account address to redact (e.g. an r-address).
|
||||
* @return The first 16 lowercase hex characters of sha512Half(addr),
|
||||
* or an empty string when @p addr is empty.
|
||||
*/
|
||||
[[nodiscard]] std::string
|
||||
redactAccount(std::string_view addr);
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time string concatenation utility and shared telemetry constants.
|
||||
/**
|
||||
* Compile-time string concatenation utility and shared telemetry constants.
|
||||
*
|
||||
* Provides StaticStr<N> — a compile-time string buffer that implicitly
|
||||
* converts to std::string_view — and join() for dot-separated concatenation.
|
||||
* Module-specific span names (e.g. RPC, consensus) live in their respective
|
||||
* modules and build upon these shared primitives.
|
||||
*
|
||||
* @note These constants are NOT guarded by XRPL_ENABLE_TELEMETRY because
|
||||
* @note These constants are NOT guarded by XRPL_ENABLE_TELEMETRY because
|
||||
* call sites reference them even when SpanGuard methods are no-ops
|
||||
* (the no-op stubs still accept string_view parameters). The compiler
|
||||
* elides all inline constexpr values whose only uses are in dead code.
|
||||
*
|
||||
* @note Json::StaticString (jss.h) is a pointer wrapper without
|
||||
* @note Json::StaticString (jss.h) is a pointer wrapper without
|
||||
* concatenation support. boost::static_string is not constexpr.
|
||||
* StaticStr<N> exists specifically for compile-time dot-join composition.
|
||||
*
|
||||
@@ -37,8 +38,10 @@ namespace xrpl::telemetry {
|
||||
|
||||
// ===== Compile-time string utility =========================================
|
||||
|
||||
/// Fixed-size character buffer for compile-time string operations.
|
||||
/// Implicitly converts to std::string_view at zero cost.
|
||||
/**
|
||||
* Fixed-size character buffer for compile-time string operations.
|
||||
* Implicitly converts to std::string_view at zero cost.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
struct StaticStr
|
||||
{
|
||||
@@ -60,11 +63,15 @@ struct StaticStr
|
||||
}
|
||||
};
|
||||
|
||||
/// Deduction guide: StaticStr from string literal.
|
||||
/**
|
||||
* Deduction guide: StaticStr from string literal.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
StaticStr(char const (&)[N]) -> StaticStr<N - 1>;
|
||||
|
||||
/// Create a StaticStr from a string literal.
|
||||
/**
|
||||
* Create a StaticStr from a string literal.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
constexpr auto
|
||||
makeStr(char const (&str)[N])
|
||||
@@ -72,7 +79,9 @@ makeStr(char const (&str)[N])
|
||||
return StaticStr<N - 1>(str);
|
||||
}
|
||||
|
||||
/// Concatenate two StaticStr values with a dot separator.
|
||||
/**
|
||||
* Concatenate two StaticStr values with a dot separator.
|
||||
*/
|
||||
template <std::size_t A, std::size_t B>
|
||||
constexpr auto
|
||||
join(StaticStr<A> const& lhs, StaticStr<B> const& rhs)
|
||||
@@ -108,27 +117,33 @@ namespace attr {
|
||||
inline constexpr auto networkId = join(join(seg::xrpl, seg::network), makeStr("id"));
|
||||
inline constexpr auto networkType = join(join(seg::xrpl, seg::network), makeStr("type"));
|
||||
|
||||
/// Canonical shared attrs (rule 5 — <domain>_<field> underscore form).
|
||||
///
|
||||
/// Per the naming convention header note: shared cross-span attribute
|
||||
/// keys use the underscore form, reserving the dotted xrpl.<domain>.<field>
|
||||
/// form for resource attributes set on the OTel resource at startup.
|
||||
/// Defined once here, aliased by domain-specific headers. These are
|
||||
/// literal underscore-joined names, not dot-joined via `join()`, since
|
||||
/// `join()` always inserts `.` between its arguments.
|
||||
/**
|
||||
* Canonical shared attrs (rule 5 — <domain>_<field> underscore form).
|
||||
*
|
||||
* Per the naming convention header note: shared cross-span attribute
|
||||
* keys use the underscore form, reserving the dotted xrpl.<domain>.<field>
|
||||
* form for resource attributes set on the OTel resource at startup.
|
||||
* Defined once here, aliased by domain-specific headers. These are
|
||||
* literal underscore-joined names, not dot-joined via `join()`, since
|
||||
* `join()` always inserts `.` between its arguments.
|
||||
*/
|
||||
inline constexpr auto txHash = makeStr("tx_hash");
|
||||
inline constexpr auto peerId = makeStr("peer_id");
|
||||
inline constexpr auto ledgerSeq = makeStr("ledger_seq");
|
||||
|
||||
/// Shared close-time attrs — bare names, reused by consensus and ledger.
|
||||
/**
|
||||
* Shared close-time attrs — bare names, reused by consensus and ledger.
|
||||
*/
|
||||
inline constexpr auto closeTime = makeStr("close_time");
|
||||
inline constexpr auto closeTimeCorrect = makeStr("close_time_correct");
|
||||
inline constexpr auto closeResolutionMs = makeStr("close_resolution_ms");
|
||||
/// Shared validation attrs — reused by the consensus and peer validation
|
||||
/// spans. Same concept, same key on every span; the span name tells them
|
||||
/// apart, so neither is emitter-prefixed. `ledgerHash` is a ledger-object
|
||||
/// property (bare, like ledgerSeq); `fullValidation` is the is-full-validation
|
||||
/// flag. Never the dotted xrpl. form (reserved for resource attrs).
|
||||
/**
|
||||
* Shared validation attrs — reused by the consensus and peer validation
|
||||
* spans. Same concept, same key on every span; the span name tells them
|
||||
* apart, so neither is emitter-prefixed. `ledgerHash` is a ledger-object
|
||||
* property (bare, like ledgerSeq); `fullValidation` is the is-full-validation
|
||||
* flag. Never the dotted xrpl. form (reserved for resource attrs).
|
||||
*/
|
||||
inline constexpr auto ledgerHash = makeStr("ledger_hash");
|
||||
inline constexpr auto fullValidation = makeStr("full_validation");
|
||||
} // namespace attr
|
||||
|
||||
@@ -1,90 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
/** Abstract interface for OpenTelemetry distributed tracing.
|
||||
|
||||
Provides the Telemetry base class that all components use to create trace
|
||||
spans. Three concrete implementations exist, selected at construction time
|
||||
by makeTelemetry():
|
||||
|
||||
- TelemetryImpl (Telemetry.cpp): real OTel SDK integration, compiled
|
||||
only when XRPL_ENABLE_TELEMETRY is defined and enabled at runtime.
|
||||
- NullTelemetry (NullTelemetry.cpp): no-op stub used when telemetry is
|
||||
disabled at compile time or runtime.
|
||||
- NullTelemetryOtel (Telemetry.cpp): no-op stub that still depends on
|
||||
the OTel API (used during transition or for testing).
|
||||
|
||||
Inheritance / dependency diagram:
|
||||
|
||||
+--------------------+
|
||||
| Telemetry | (abstract, this file)
|
||||
| <<interface>> |
|
||||
+---------+----------+
|
||||
|
|
||||
+---------+-----------+-------------------+
|
||||
| | |
|
||||
+---+------------+ +-----+---------+ +------+----------+
|
||||
| TelemetryImpl | | NullTelemetry | | NullTelemetryOtel|
|
||||
| (Telemetry.cpp)| |(NullTelemetry | | (Telemetry.cpp) |
|
||||
| OTel SDK | | .cpp) | | noop w/ OTel API |
|
||||
+----------------+ +---------------+ +------------------+
|
||||
|
||||
The Setup struct holds all configuration parsed from the [telemetry]
|
||||
section of xrpld.cfg. See TelemetryConfig.cpp for the parser and
|
||||
cfg/xrpld-example.cfg for the available options.
|
||||
|
||||
OTel SDK headers are conditionally included behind XRPL_ENABLE_TELEMETRY
|
||||
so that builds without telemetry have zero dependency on opentelemetry-cpp.
|
||||
|
||||
Usage examples:
|
||||
|
||||
1. Root span at a subsystem entry point (typical usage):
|
||||
@code
|
||||
#include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
using namespace xrpl::telemetry;
|
||||
|
||||
// In an RPC handler dispatch:
|
||||
auto guard = SpanGuard::span(
|
||||
TraceCategory::Rpc, rpc_span::prefix::command, commandName);
|
||||
guard.setAttribute(rpc_span::attr::command, commandName);
|
||||
// ... process request
|
||||
// guard destructor automatically ends the span on scope exit
|
||||
@endcode
|
||||
|
||||
2. Child span for a sub-operation (scoped child):
|
||||
@code
|
||||
auto parent = SpanGuard::span(
|
||||
TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
|
||||
{
|
||||
auto child = parent.childSpan(rpc_span::op::process);
|
||||
child.setAttribute(rpc_span::attr::version, apiVersion);
|
||||
// child ends here
|
||||
}
|
||||
@endcode
|
||||
|
||||
3. Unrelated span (cross-scope, same thread):
|
||||
@code
|
||||
// gRPC and RPC handlers can be active simultaneously
|
||||
auto grpcSpan = SpanGuard::span(
|
||||
TraceCategory::Rpc, grpc_span::prefix::grpc, grpc_span::attr::method);
|
||||
auto rpcSpan = SpanGuard::span(
|
||||
TraceCategory::Rpc, rpc_span::prefix::command, commandName);
|
||||
// both spans end on scope exit
|
||||
@endcode
|
||||
|
||||
4. Cross-thread context propagation:
|
||||
@code
|
||||
// Thread A: capture the active context while span is in scope
|
||||
auto ctx = parentGuard.captureContext();
|
||||
|
||||
// Thread B: create child span with explicit parent
|
||||
auto child = SpanGuard::childSpan(rpc_span::op::process, ctx);
|
||||
@endcode
|
||||
|
||||
@note Thread safety: The Telemetry interface is safe for concurrent reads
|
||||
(isEnabled, shouldTrace*, getTracer, startSpan) after start() completes.
|
||||
setServiceInstanceId() must be called before start() and is not thread-safe.
|
||||
The OTel SDK's TracerProvider and Tracer are internally thread-safe.
|
||||
*/
|
||||
/**
|
||||
* Abstract interface for OpenTelemetry distributed tracing.
|
||||
*
|
||||
* Provides the Telemetry base class that all components use to create trace
|
||||
* spans. Three concrete implementations exist, selected at construction time
|
||||
* by makeTelemetry():
|
||||
*
|
||||
* - TelemetryImpl (Telemetry.cpp): real OTel SDK integration, compiled
|
||||
* only when XRPL_ENABLE_TELEMETRY is defined and enabled at runtime.
|
||||
* - NullTelemetry (NullTelemetry.cpp): no-op stub used when telemetry is
|
||||
* disabled at compile time or runtime.
|
||||
* - NullTelemetryOtel (Telemetry.cpp): no-op stub that still depends on
|
||||
* the OTel API (used during transition or for testing).
|
||||
*
|
||||
* Inheritance / dependency diagram:
|
||||
*
|
||||
* +--------------------+
|
||||
* | Telemetry | (abstract, this file)
|
||||
* | <<interface>> |
|
||||
* +---------+----------+
|
||||
* |
|
||||
* +---------+-----------+-------------------+
|
||||
* | | |
|
||||
* +---+------------+ +-----+---------+ +------+----------+
|
||||
* | TelemetryImpl | | NullTelemetry | | NullTelemetryOtel|
|
||||
* | (Telemetry.cpp)| |(NullTelemetry | | (Telemetry.cpp) |
|
||||
* | OTel SDK | | .cpp) | | noop w/ OTel API |
|
||||
* +----------------+ +---------------+ +------------------+
|
||||
*
|
||||
* The Setup struct holds all configuration parsed from the [telemetry]
|
||||
* section of xrpld.cfg. See TelemetryConfig.cpp for the parser and
|
||||
* cfg/xrpld-example.cfg for the available options.
|
||||
*
|
||||
* OTel SDK headers are conditionally included behind XRPL_ENABLE_TELEMETRY
|
||||
* so that builds without telemetry have zero dependency on opentelemetry-cpp.
|
||||
*
|
||||
* Usage examples:
|
||||
*
|
||||
* 1. Root span at a subsystem entry point (typical usage):
|
||||
* @code
|
||||
* #include <xrpld/rpc/detail/RpcSpanNames.h>
|
||||
* using namespace xrpl::telemetry;
|
||||
*
|
||||
* // In an RPC handler dispatch:
|
||||
* auto guard = SpanGuard::span(
|
||||
* TraceCategory::Rpc, rpc_span::prefix::command, commandName);
|
||||
* guard.setAttribute(rpc_span::attr::command, commandName);
|
||||
* // ... process request
|
||||
* // guard destructor automatically ends the span on scope exit
|
||||
* @endcode
|
||||
*
|
||||
* 2. Child span for a sub-operation (scoped child):
|
||||
* @code
|
||||
* auto parent = SpanGuard::span(
|
||||
* TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
|
||||
* {
|
||||
* auto child = parent.childSpan(rpc_span::op::process);
|
||||
* child.setAttribute(rpc_span::attr::version, apiVersion);
|
||||
* // child ends here
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* 3. Unrelated span (cross-scope, same thread):
|
||||
* @code
|
||||
* // gRPC and RPC handlers can be active simultaneously
|
||||
* auto grpcSpan = SpanGuard::span(
|
||||
* TraceCategory::Rpc, grpc_span::prefix::grpc, grpc_span::attr::method);
|
||||
* auto rpcSpan = SpanGuard::span(
|
||||
* TraceCategory::Rpc, rpc_span::prefix::command, commandName);
|
||||
* // both spans end on scope exit
|
||||
* @endcode
|
||||
*
|
||||
* 4. Cross-thread context propagation:
|
||||
* @code
|
||||
* // Thread A: capture the active context while span is in scope
|
||||
* auto ctx = parentGuard.captureContext();
|
||||
*
|
||||
* // Thread B: create child span with explicit parent
|
||||
* auto child = SpanGuard::childSpan(rpc_span::op::process, ctx);
|
||||
* @endcode
|
||||
*
|
||||
* @note Thread safety: The Telemetry interface is safe for concurrent reads
|
||||
* (isEnabled, shouldTrace*, getTracer, startSpan) after start() completes.
|
||||
* setServiceInstanceId() must be called before start() and is not thread-safe.
|
||||
* The OTel SDK's TracerProvider and Tracer are internally thread-safe.
|
||||
*/
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
@@ -108,152 +109,203 @@
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
/** OTel instrumentation scope (tracer) name. Identifies this library as the
|
||||
source of spans; distinct from the `service.name` resource attribute
|
||||
(Setup::serviceName), which is config-overridable. */
|
||||
/**
|
||||
* OTel instrumentation scope (tracer) name. Identifies this library as the
|
||||
* source of spans; distinct from the `service.name` resource attribute
|
||||
* (Setup::serviceName), which is config-overridable.
|
||||
*/
|
||||
inline constexpr std::string_view kTracerName{"xrpld"};
|
||||
|
||||
/** OTel instrumentation scope (meter) name. Identifies this library as the
|
||||
source of metrics; symmetric with kTracerName for the tracing side. */
|
||||
/**
|
||||
* OTel instrumentation scope (meter) name. Identifies this library as the
|
||||
* source of metrics; symmetric with kTracerName for the tracing side.
|
||||
*/
|
||||
inline constexpr std::string_view kMeterName{"xrpld"};
|
||||
|
||||
/** OTel instrumentation scope version reported for the meter. */
|
||||
/**
|
||||
* OTel instrumentation scope version reported for the meter.
|
||||
*/
|
||||
inline constexpr std::string_view kMeterVersion{"1.0.0"};
|
||||
#endif
|
||||
|
||||
class Telemetry
|
||||
{
|
||||
/** Global singleton pointer, set by start()/stop() in the active
|
||||
implementation. Allows SpanGuard factory methods to access the
|
||||
Telemetry instance without callers passing it explicitly.
|
||||
|
||||
Atomic with acquire/release ordering: start()/stop() store on
|
||||
the initialization thread, factory methods load on worker threads.
|
||||
@see setInstance(), getInstance()
|
||||
*/
|
||||
/**
|
||||
* Global singleton pointer, set by start()/stop() in the active
|
||||
* implementation. Allows SpanGuard factory methods to access the
|
||||
* Telemetry instance without callers passing it explicitly.
|
||||
*
|
||||
* Atomic with acquire/release ordering: start()/stop() store on
|
||||
* the initialization thread, factory methods load on worker threads.
|
||||
* @see setInstance(), getInstance()
|
||||
*/
|
||||
inline static std::atomic<Telemetry*> instance{nullptr};
|
||||
|
||||
public:
|
||||
/** Get the global Telemetry instance.
|
||||
@return Pointer to the active instance, or nullptr if not started.
|
||||
*/
|
||||
/**
|
||||
* Get the global Telemetry instance.
|
||||
* @return Pointer to the active instance, or nullptr if not started.
|
||||
*/
|
||||
static Telemetry*
|
||||
getInstance()
|
||||
{
|
||||
return instance.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/** Set the global Telemetry instance.
|
||||
Called by start()/stop() in concrete implementations.
|
||||
Tests can call this with a mock to override the global instance.
|
||||
@param t Pointer to the Telemetry instance, or nullptr to clear.
|
||||
*/
|
||||
/**
|
||||
* Set the global Telemetry instance.
|
||||
* Called by start()/stop() in concrete implementations.
|
||||
* Tests can call this with a mock to override the global instance.
|
||||
* @param t Pointer to the Telemetry instance, or nullptr to clear.
|
||||
*/
|
||||
static void
|
||||
setInstance(Telemetry* t)
|
||||
{
|
||||
instance.store(t, std::memory_order_release);
|
||||
}
|
||||
|
||||
/** Configuration parsed from the [telemetry] section of xrpld.cfg.
|
||||
|
||||
All fields have sensible defaults so the section can be minimal
|
||||
or omitted entirely. See TelemetryConfig.cpp for the parser.
|
||||
*/
|
||||
/**
|
||||
* Configuration parsed from the [telemetry] section of xrpld.cfg.
|
||||
*
|
||||
* All fields have sensible defaults so the section can be minimal
|
||||
* or omitted entirely. See TelemetryConfig.cpp for the parser.
|
||||
*/
|
||||
struct Setup
|
||||
{
|
||||
/** Master switch: true to enable tracing at runtime. */
|
||||
/**
|
||||
* Master switch: true to enable tracing at runtime.
|
||||
*/
|
||||
bool enabled = false;
|
||||
|
||||
/** OTel resource attribute `service.name`. */
|
||||
/**
|
||||
* OTel resource attribute `service.name`.
|
||||
*/
|
||||
std::string serviceName = "xrpld";
|
||||
|
||||
/** OTel resource attribute `service.version` (set from BuildInfo). */
|
||||
/**
|
||||
* OTel resource attribute `service.version` (set from BuildInfo).
|
||||
*/
|
||||
std::string serviceVersion;
|
||||
|
||||
/** OTel resource attribute `service.instance.id` (defaults to node
|
||||
public key). */
|
||||
/**
|
||||
* OTel resource attribute `service.instance.id` (defaults to node
|
||||
* public key).
|
||||
*/
|
||||
std::string serviceInstanceId;
|
||||
|
||||
/** OTLP/HTTP endpoint URL where spans are sent. */
|
||||
/**
|
||||
* OTLP/HTTP endpoint URL where spans are sent.
|
||||
*/
|
||||
std::string exporterEndpoint = "http://localhost:4318/v1/traces";
|
||||
|
||||
/** Whether to use TLS for the exporter connection. */
|
||||
/**
|
||||
* Whether to use TLS for the exporter connection.
|
||||
*/
|
||||
bool useTls = false;
|
||||
|
||||
/** Path to a CA certificate bundle for TLS verification. */
|
||||
/**
|
||||
* Path to a CA certificate bundle for TLS verification.
|
||||
*/
|
||||
std::string tlsCertPath;
|
||||
|
||||
/** Path to this node's client certificate (PEM), presented to the
|
||||
collector for mutual TLS. Empty disables client-side auth, in
|
||||
which case only server (one-way) TLS is used. */
|
||||
/**
|
||||
* Path to this node's client certificate (PEM), presented to the
|
||||
* collector for mutual TLS. Empty disables client-side auth, in
|
||||
* which case only server (one-way) TLS is used.
|
||||
*/
|
||||
std::string tlsClientCertPath;
|
||||
|
||||
/** Path to the private key (PEM) for tlsClientCertPath. Required
|
||||
whenever tlsClientCertPath is set. */
|
||||
/**
|
||||
* Path to the private key (PEM) for tlsClientCertPath. Required
|
||||
* whenever tlsClientCertPath is set.
|
||||
*/
|
||||
std::string tlsClientKeyPath;
|
||||
|
||||
/** Head-based sampling ratio. Intentionally fixed at 1.0 (sample
|
||||
everything) and NOT read from config. A per-node ratio would let
|
||||
nodes make divergent keep/drop decisions for the same distributed
|
||||
trace, producing broken/partial traces. The ratio sampler is wrapped
|
||||
in a ParentBasedSampler (see Telemetry.cpp) so spans inheriting a
|
||||
remote parent honor the upstream sampled flag. Volume reduction is
|
||||
delegated to the collector's tail sampling; for node-local post-hoc
|
||||
dropping see SpanGuard::discard().
|
||||
*/
|
||||
/**
|
||||
* Head-based sampling ratio. Intentionally fixed at 1.0 (sample
|
||||
* everything) and NOT read from config. A per-node ratio would let
|
||||
* nodes make divergent keep/drop decisions for the same distributed
|
||||
* trace, producing broken/partial traces. The ratio sampler is wrapped
|
||||
* in a ParentBasedSampler (see Telemetry.cpp) so spans inheriting a
|
||||
* remote parent honor the upstream sampled flag. Volume reduction is
|
||||
* delegated to the collector's tail sampling; for node-local post-hoc
|
||||
* dropping see SpanGuard::discard().
|
||||
*/
|
||||
static constexpr double samplingRatio = 1.0;
|
||||
|
||||
/** Maximum number of spans per batch export. */
|
||||
/**
|
||||
* Maximum number of spans per batch export.
|
||||
*/
|
||||
std::uint32_t batchSize = 512;
|
||||
|
||||
/** Delay between batch exports. */
|
||||
/**
|
||||
* Delay between batch exports.
|
||||
*/
|
||||
std::chrono::milliseconds batchDelay = std::chrono::milliseconds{5000};
|
||||
|
||||
/** Maximum number of spans queued before dropping. */
|
||||
/**
|
||||
* Maximum number of spans queued before dropping.
|
||||
*/
|
||||
std::uint32_t maxQueueSize = 2048;
|
||||
|
||||
/** Network identifier, added as an OTel resource attribute. */
|
||||
/**
|
||||
* Network identifier, added as an OTel resource attribute.
|
||||
*/
|
||||
std::uint32_t networkId = 0;
|
||||
|
||||
/** Network type label (e.g. "mainnet", "testnet", "devnet"). */
|
||||
/**
|
||||
* Network type label (e.g. "mainnet", "testnet", "devnet").
|
||||
*/
|
||||
std::string networkType = "mainnet";
|
||||
|
||||
/** Enable tracing for transaction processing. */
|
||||
/**
|
||||
* Enable tracing for transaction processing.
|
||||
*/
|
||||
bool traceTransactions = true;
|
||||
|
||||
/** Enable tracing for consensus rounds. */
|
||||
/**
|
||||
* Enable tracing for consensus rounds.
|
||||
*/
|
||||
bool traceConsensus = true;
|
||||
|
||||
/** Enable tracing for RPC request handling. */
|
||||
/**
|
||||
* Enable tracing for RPC request handling.
|
||||
*/
|
||||
bool traceRpc = true;
|
||||
|
||||
/** Enable tracing for peer-to-peer messages (enabled by default;
|
||||
high volume). */
|
||||
/**
|
||||
* Enable tracing for peer-to-peer messages (enabled by default;
|
||||
* high volume).
|
||||
*/
|
||||
bool tracePeer = true;
|
||||
|
||||
/** Enable tracing for ledger close/accept. */
|
||||
/**
|
||||
* Enable tracing for ledger close/accept.
|
||||
*/
|
||||
bool traceLedger = true;
|
||||
|
||||
/** Strategy for cross-node consensus trace correlation.
|
||||
"deterministic" — derive trace_id from ledger hash so all
|
||||
validators in the same round share the same trace_id.
|
||||
"attribute" — random trace_id, correlate via ledger_id attribute.
|
||||
*/
|
||||
/**
|
||||
* Strategy for cross-node consensus trace correlation.
|
||||
* "deterministic" — derive trace_id from ledger hash so all
|
||||
* validators in the same round share the same trace_id.
|
||||
* "attribute" — random trace_id, correlate via ledger_id attribute.
|
||||
*/
|
||||
std::string consensusTraceStrategy = "deterministic";
|
||||
};
|
||||
|
||||
virtual ~Telemetry() = default;
|
||||
|
||||
/** Update the service instance ID (OTel resource attribute
|
||||
`service.instance.id`).
|
||||
|
||||
Must be called before start(). The node public key is not available
|
||||
when Telemetry is constructed (during the ApplicationImp member
|
||||
initializer list), so this setter allows Application::setup() to
|
||||
inject the identity once nodeIdentity_ is known.
|
||||
|
||||
@param id The node's base58-encoded public key or custom identifier.
|
||||
*/
|
||||
/**
|
||||
* Update the service instance ID (OTel resource attribute
|
||||
* `service.instance.id`).
|
||||
*
|
||||
* Must be called before start(). The node public key is not available
|
||||
* when Telemetry is constructed (during the ApplicationImp member
|
||||
* initializer list), so this setter allows Application::setup() to
|
||||
* inject the identity once nodeIdentity_ is known.
|
||||
*
|
||||
* @param id The node's base58-encoded public key or custom identifier.
|
||||
*/
|
||||
virtual void
|
||||
setServiceInstanceId(std::string const& id)
|
||||
{
|
||||
@@ -261,98 +313,118 @@ public:
|
||||
(void)id;
|
||||
}
|
||||
|
||||
/** Initialize the tracing pipeline (exporter, processor, provider).
|
||||
Call after construction.
|
||||
*/
|
||||
/**
|
||||
* Initialize the tracing pipeline (exporter, processor, provider).
|
||||
* Call after construction.
|
||||
*/
|
||||
virtual void
|
||||
start() = 0;
|
||||
|
||||
/** Flush pending spans and shut down the tracing pipeline.
|
||||
Call before destruction.
|
||||
*/
|
||||
/**
|
||||
* Flush pending spans and shut down the tracing pipeline.
|
||||
* Call before destruction.
|
||||
*/
|
||||
virtual void
|
||||
stop() = 0;
|
||||
|
||||
/** @return true if this instance is actively exporting spans. */
|
||||
/**
|
||||
* @return true if this instance is actively exporting spans.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isEnabled() const = 0;
|
||||
|
||||
/** @return true if transaction processing should be traced. */
|
||||
/**
|
||||
* @return true if transaction processing should be traced.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
shouldTraceTransactions() const = 0;
|
||||
|
||||
/** @return true if consensus rounds should be traced. */
|
||||
/**
|
||||
* @return true if consensus rounds should be traced.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
shouldTraceConsensus() const = 0;
|
||||
|
||||
/** @return true if RPC request handling should be traced. */
|
||||
/**
|
||||
* @return true if RPC request handling should be traced.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
shouldTraceRpc() const = 0;
|
||||
|
||||
/** @return true if peer-to-peer messages should be traced. */
|
||||
/**
|
||||
* @return true if peer-to-peer messages should be traced.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
shouldTracePeer() const = 0;
|
||||
|
||||
/** @return true if ledger close/accept should be traced. */
|
||||
/**
|
||||
* @return true if ledger close/accept should be traced.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
shouldTraceLedger() const = 0;
|
||||
|
||||
/** @return The configured consensus trace correlation strategy. */
|
||||
/**
|
||||
* @return The configured consensus trace correlation strategy.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string const&
|
||||
getConsensusTraceStrategy() const = 0;
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
/** Get or create a named tracer instance.
|
||||
|
||||
@param name Tracer name used to identify the instrumentation library.
|
||||
@return A shared pointer to the Tracer.
|
||||
*/
|
||||
/**
|
||||
* Get or create a named tracer instance.
|
||||
*
|
||||
* @param name Tracer name used to identify the instrumentation library.
|
||||
* @return A shared pointer to the Tracer.
|
||||
*/
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>
|
||||
getTracer(std::string_view name = kTracerName) = 0;
|
||||
|
||||
/** Get or create a named meter instance.
|
||||
|
||||
Returns the raw OTel Meter, giving developers direct access to the
|
||||
full metrics API. From the returned Meter any instrument type can be
|
||||
created: Counter, UpDownCounter, Gauge, and Histogram (synchronous),
|
||||
plus their observable/async variants (ObservableCounter,
|
||||
ObservableUpDownCounter, ObservableGauge).
|
||||
|
||||
@param name Meter name used to identify the instrumentation scope.
|
||||
@return A shared pointer to the Meter.
|
||||
*/
|
||||
/**
|
||||
* Get or create a named meter instance.
|
||||
*
|
||||
* Returns the raw OTel Meter, giving developers direct access to the
|
||||
* full metrics API. From the returned Meter any instrument type can be
|
||||
* created: Counter, UpDownCounter, Gauge, and Histogram (synchronous),
|
||||
* plus their observable/async variants (ObservableCounter,
|
||||
* ObservableUpDownCounter, ObservableGauge).
|
||||
*
|
||||
* @param name Meter name used to identify the instrumentation scope.
|
||||
* @return A shared pointer to the Meter.
|
||||
*/
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::metrics::Meter>
|
||||
getMeter(std::string_view name = kMeterName) = 0;
|
||||
|
||||
/** Start a new span on the current thread's context.
|
||||
|
||||
The span becomes a child of the current active span (if any) via
|
||||
OpenTelemetry's context propagation.
|
||||
|
||||
@param name Span name (typically "rpc.command.<cmd>").
|
||||
@param kind The span kind (defaults to kInternal). Possible values:
|
||||
- kInternal: default, in-process operation
|
||||
- kServer: incoming synchronous request (e.g. RPC)
|
||||
- kClient: outgoing synchronous request
|
||||
- kProducer: async message send (e.g. peer broadcast)
|
||||
- kConsumer: async message receive
|
||||
@return A shared pointer to the new Span.
|
||||
*/
|
||||
/**
|
||||
* Start a new span on the current thread's context.
|
||||
*
|
||||
* The span becomes a child of the current active span (if any) via
|
||||
* OpenTelemetry's context propagation.
|
||||
*
|
||||
* @param name Span name (typically "rpc.command.<cmd>").
|
||||
* @param kind The span kind (defaults to kInternal). Possible values:
|
||||
* - kInternal: default, in-process operation
|
||||
* - kServer: incoming synchronous request (e.g. RPC)
|
||||
* - kClient: outgoing synchronous request
|
||||
* - kProducer: async message send (e.g. peer broadcast)
|
||||
* - kConsumer: async message receive
|
||||
* @return A shared pointer to the new Span.
|
||||
*/
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
|
||||
startSpan(
|
||||
std::string_view name,
|
||||
opentelemetry::trace::SpanKind kind = opentelemetry::trace::SpanKind::kInternal) = 0;
|
||||
|
||||
/** Start a new span with an explicit parent context.
|
||||
|
||||
Use this overload when the parent span is not on the current
|
||||
thread's context stack (e.g. cross-thread trace propagation).
|
||||
|
||||
@param name Span name.
|
||||
@param parentContext The parent span's context.
|
||||
@param kind The span kind (defaults to kInternal).
|
||||
@return A shared pointer to the new Span.
|
||||
*/
|
||||
/**
|
||||
* Start a new span with an explicit parent context.
|
||||
*
|
||||
* Use this overload when the parent span is not on the current
|
||||
* thread's context stack (e.g. cross-thread trace propagation).
|
||||
*
|
||||
* @param name Span name.
|
||||
* @param parentContext The parent span's context.
|
||||
* @param kind The span kind (defaults to kInternal).
|
||||
* @return A shared pointer to the new Span.
|
||||
*/
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
|
||||
startSpan(
|
||||
std::string_view name,
|
||||
@@ -361,26 +433,28 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
/** Create a Telemetry instance.
|
||||
|
||||
Returns a TelemetryImpl when setup.enabled is true, or a
|
||||
NullTelemetry no-op stub otherwise.
|
||||
|
||||
@param setup Configuration from the [telemetry] config section.
|
||||
@param journal Journal for log output during initialization.
|
||||
*/
|
||||
/**
|
||||
* Create a Telemetry instance.
|
||||
*
|
||||
* Returns a TelemetryImpl when setup.enabled is true, or a
|
||||
* NullTelemetry no-op stub otherwise.
|
||||
*
|
||||
* @param setup Configuration from the [telemetry] config section.
|
||||
* @param journal Journal for log output during initialization.
|
||||
*/
|
||||
std::unique_ptr<Telemetry>
|
||||
makeTelemetry(Telemetry::Setup const& setup, beast::Journal journal);
|
||||
|
||||
/** Parse the [telemetry] config section into a Setup struct.
|
||||
|
||||
@param section The [telemetry] config section.
|
||||
@param nodePublicKey Node public key, used as default instance ID.
|
||||
@param version Build version string.
|
||||
@param networkId Network identifier from [network_id] config
|
||||
(0 = mainnet, 1 = testnet, 2 = devnet).
|
||||
@return A populated Setup struct with defaults for missing values.
|
||||
*/
|
||||
/**
|
||||
* Parse the [telemetry] config section into a Setup struct.
|
||||
*
|
||||
* @param section The [telemetry] config section.
|
||||
* @param nodePublicKey Node public key, used as default instance ID.
|
||||
* @param version Build version string.
|
||||
* @param networkId Network identifier from [network_id] config
|
||||
* (0 = mainnet, 1 = testnet, 2 = devnet).
|
||||
* @return A populated Setup struct with defaults for missing values.
|
||||
*/
|
||||
Telemetry::Setup
|
||||
makeTelemetrySetup(
|
||||
Section const& section,
|
||||
@@ -388,14 +462,15 @@ makeTelemetrySetup(
|
||||
std::string const& version,
|
||||
std::uint32_t networkId);
|
||||
|
||||
/** Derive a human-readable network type label from the numeric network ID.
|
||||
|
||||
Shared by the trace and metric export paths so both stamp the same
|
||||
`xrpl.network.type` resource attribute value.
|
||||
|
||||
@param networkId The network identifier from [network_id] config.
|
||||
@return "mainnet" (0), "testnet" (1), "devnet" (2), or "unknown".
|
||||
*/
|
||||
/**
|
||||
* Derive a human-readable network type label from the numeric network ID.
|
||||
*
|
||||
* Shared by the trace and metric export paths so both stamp the same
|
||||
* `xrpl.network.type` resource attribute value.
|
||||
*
|
||||
* @param networkId The network identifier from [network_id] config.
|
||||
* @return "mainnet" (0), "testnet" (1), "devnet" (2), or "unknown".
|
||||
*/
|
||||
std::string
|
||||
networkTypeFromId(std::uint32_t networkId);
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
/** Utilities for trace context propagation across nodes.
|
||||
|
||||
Provides serialization/deserialization of OTel trace context to/from
|
||||
Protocol Buffer TraceContext messages (P2P cross-node propagation).
|
||||
Wired into the P2P message flow via PropagationHelpers.h for
|
||||
TMTransaction, TMProposeSet, and TMValidation messages.
|
||||
|
||||
Only compiled when XRPL_ENABLE_TELEMETRY is defined.
|
||||
|
||||
@see PropagationHelpers.h (high-level inject helpers),
|
||||
TxTracing.h (transaction receive-side extraction),
|
||||
ConsensusReceiveTracing.h (proposal/validation receive-side).
|
||||
*/
|
||||
/**
|
||||
* Utilities for trace context propagation across nodes.
|
||||
*
|
||||
* Provides serialization/deserialization of OTel trace context to/from
|
||||
* Protocol Buffer TraceContext messages (P2P cross-node propagation).
|
||||
* Wired into the P2P message flow via PropagationHelpers.h for
|
||||
* TMTransaction, TMProposeSet, and TMValidation messages.
|
||||
*
|
||||
* Only compiled when XRPL_ENABLE_TELEMETRY is defined.
|
||||
*
|
||||
* @see PropagationHelpers.h (high-level inject helpers),
|
||||
* TxTracing.h (transaction receive-side extraction),
|
||||
* ConsensusReceiveTracing.h (proposal/validation receive-side).
|
||||
*/
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
|
||||
@@ -35,12 +36,13 @@
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** Extract OTel context from a protobuf TraceContext message.
|
||||
|
||||
@param proto The protobuf TraceContext received from a peer.
|
||||
@return An OTel Context with the extracted parent span, or an empty
|
||||
context if the protobuf fields are missing or invalid.
|
||||
*/
|
||||
/**
|
||||
* Extract OTel context from a protobuf TraceContext message.
|
||||
*
|
||||
* @param proto The protobuf TraceContext received from a peer.
|
||||
* @return An OTel Context with the extracted parent span, or an empty
|
||||
* context if the protobuf fields are missing or invalid.
|
||||
*/
|
||||
inline opentelemetry::context::Context
|
||||
extractFromProtobuf(protocol::TraceContext const& proto)
|
||||
{
|
||||
@@ -69,11 +71,12 @@ extractFromProtobuf(protocol::TraceContext const& proto)
|
||||
opentelemetry::nostd::shared_ptr<trace::Span>(new trace::DefaultSpan(spanCtx)));
|
||||
}
|
||||
|
||||
/** Inject the current span's trace context into a protobuf TraceContext.
|
||||
|
||||
@param ctx The OTel context containing the span to propagate.
|
||||
@param proto The protobuf TraceContext to populate.
|
||||
*/
|
||||
/**
|
||||
* Inject the current span's trace context into a protobuf TraceContext.
|
||||
*
|
||||
* @param ctx The OTel context containing the span to propagate.
|
||||
* @param proto The protobuf TraceContext to populate.
|
||||
*/
|
||||
inline void
|
||||
injectToProtobuf(opentelemetry::context::Context const& ctx, protocol::TraceContext& proto)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Validation predicates for peer-supplied trace context.
|
||||
/**
|
||||
* Validation predicates for peer-supplied trace context.
|
||||
*
|
||||
* A protobuf TraceContext arrives inside untrusted peer messages
|
||||
* (TMTransaction, TMProposeSet, TMValidation). Before a receiving node
|
||||
@@ -28,7 +29,7 @@
|
||||
* no OpenTelemetry headers, so receive sites that use SpanGuard keep
|
||||
* SpanGuard's encapsulation of OTel types.
|
||||
*
|
||||
* @note Thread-safe: all functions are pure and operate only on their
|
||||
* @note Thread-safe: all functions are pure and operate only on their
|
||||
* arguments. No shared state.
|
||||
*
|
||||
* Usage:
|
||||
@@ -50,10 +51,11 @@
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** True if the bytes are a valid OTel trace_id: 16 bytes, not all-zero.
|
||||
/**
|
||||
* True if the bytes are a valid OTel trace_id: 16 bytes, not all-zero.
|
||||
*
|
||||
* @param traceId The raw trace_id bytes from a protobuf TraceContext.
|
||||
* @return true if usable as a trace identifier, false otherwise.
|
||||
* @param traceId The raw trace_id bytes from a protobuf TraceContext.
|
||||
* @return true if usable as a trace identifier, false otherwise.
|
||||
*/
|
||||
inline bool
|
||||
isValidTraceId(std::string const& traceId)
|
||||
@@ -61,10 +63,11 @@ isValidTraceId(std::string const& traceId)
|
||||
return traceId.size() == 16 && std::ranges::any_of(traceId, [](char c) { return c != 0; });
|
||||
}
|
||||
|
||||
/** True if the bytes are a valid OTel span_id: 8 bytes, not all-zero.
|
||||
/**
|
||||
* True if the bytes are a valid OTel span_id: 8 bytes, not all-zero.
|
||||
*
|
||||
* @param spanId The raw span_id bytes from a protobuf TraceContext.
|
||||
* @return true if usable as a span identifier, false otherwise.
|
||||
* @param spanId The raw span_id bytes from a protobuf TraceContext.
|
||||
* @return true if usable as a span identifier, false otherwise.
|
||||
*/
|
||||
inline bool
|
||||
isValidSpanId(std::string const& spanId)
|
||||
@@ -72,15 +75,16 @@ isValidSpanId(std::string const& spanId)
|
||||
return spanId.size() == 8 && std::ranges::any_of(spanId, [](char c) { return c != 0; });
|
||||
}
|
||||
|
||||
/** True if the context carries a usable parent: a valid trace_id and a
|
||||
/**
|
||||
* True if the context carries a usable parent: a valid trace_id and a
|
||||
* valid span_id together.
|
||||
*
|
||||
* Use this where both ids are taken from the peer (consensus receive,
|
||||
* generic extraction). The transaction path derives its trace_id
|
||||
* locally from the txID, so it checks isValidSpanId() alone instead.
|
||||
*
|
||||
* @param tc The protobuf TraceContext received from a peer.
|
||||
* @return true if both ids are present and valid, false otherwise.
|
||||
* @param tc The protobuf TraceContext received from a peer.
|
||||
* @return true if both ids are present and valid, false otherwise.
|
||||
*/
|
||||
inline bool
|
||||
isValidTraceContext(protocol::TraceContext const& tc)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for the transaction apply pipeline.
|
||||
/**
|
||||
* Compile-time span name constants for the transaction apply pipeline.
|
||||
*
|
||||
* Defines the span names and attribute keys used by the three apply-pipeline
|
||||
* stages — preflight, preclaim, and transactor (apply) — that run inside the
|
||||
@@ -67,47 +68,73 @@ namespace xrpl::telemetry::tx_apply_span {
|
||||
// ===== Span operation suffixes =============================================
|
||||
|
||||
namespace op {
|
||||
/// "preflight" — stateless transaction checks (suffix form).
|
||||
/**
|
||||
* "preflight" — stateless transaction checks (suffix form).
|
||||
*/
|
||||
inline constexpr auto preflight = makeStr("preflight");
|
||||
/// "preclaim" — ledger-aware checks before fee claim (suffix form).
|
||||
/**
|
||||
* "preclaim" — ledger-aware checks before fee claim (suffix form).
|
||||
*/
|
||||
inline constexpr auto preclaim = makeStr("preclaim");
|
||||
/// "transactor" — the apply stage (suffix form, used with span()).
|
||||
/**
|
||||
* "transactor" — the apply stage (suffix form, used with span()).
|
||||
*/
|
||||
inline constexpr auto transactor = makeStr("transactor");
|
||||
} // namespace op
|
||||
|
||||
// ===== Full span names (tx.<op>) ===========================================
|
||||
|
||||
/// "tx.preflight" — full name for hashSpan() at the preflight stage.
|
||||
/**
|
||||
* "tx.preflight" — full name for hashSpan() at the preflight stage.
|
||||
*/
|
||||
inline constexpr auto preflight = join(seg::tx, op::preflight);
|
||||
/// "tx.preclaim" — full name for hashSpan() at the preclaim stage.
|
||||
/**
|
||||
* "tx.preclaim" — full name for hashSpan() at the preclaim stage.
|
||||
*/
|
||||
inline constexpr auto preclaim = join(seg::tx, op::preclaim);
|
||||
/// "tx.transactor" — full name for hashSpan() at the apply stage. Shares the
|
||||
/// txID-derived trace_id so it co-traces with tx.preflight and tx.preclaim.
|
||||
/**
|
||||
* "tx.transactor" — full name for hashSpan() at the apply stage. Shares the
|
||||
* txID-derived trace_id so it co-traces with tx.preflight and tx.preclaim.
|
||||
*/
|
||||
inline constexpr auto transactor = join(seg::tx, op::transactor);
|
||||
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/// "stage" — which apply-pipeline stage this span represents. Drives the
|
||||
/// collector spanmetrics `stage` dimension for per-stage RED metrics.
|
||||
/**
|
||||
* "stage" — which apply-pipeline stage this span represents. Drives the
|
||||
* collector spanmetrics `stage` dimension for per-stage RED metrics.
|
||||
*/
|
||||
inline constexpr auto stage = makeStr("stage");
|
||||
/// "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
/// Matches tx_span::attr::txType so both share the spanmetrics dimension.
|
||||
/**
|
||||
* "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
* Matches tx_span::attr::txType so both share the spanmetrics dimension.
|
||||
*/
|
||||
inline constexpr auto txType = makeStr("tx_type");
|
||||
/// "ter_result" — engine result code after the stage (e.g., "tesSUCCESS").
|
||||
/**
|
||||
* "ter_result" — engine result code after the stage (e.g., "tesSUCCESS").
|
||||
*/
|
||||
inline constexpr auto terResult = makeStr("ter_result");
|
||||
/// "applied" — whether the transaction was applied to the ledger (apply only).
|
||||
/**
|
||||
* "applied" — whether the transaction was applied to the ledger (apply only).
|
||||
*/
|
||||
inline constexpr auto applied = makeStr("applied");
|
||||
} // namespace attr
|
||||
|
||||
// ===== Attribute values (stage names) ======================================
|
||||
|
||||
namespace val {
|
||||
/// "preflight" — value of the stage attribute on tx.preflight.
|
||||
/**
|
||||
* "preflight" — value of the stage attribute on tx.preflight.
|
||||
*/
|
||||
inline constexpr auto preflight = makeStr("preflight");
|
||||
/// "preclaim" — value of the stage attribute on tx.preclaim.
|
||||
/**
|
||||
* "preclaim" — value of the stage attribute on tx.preclaim.
|
||||
*/
|
||||
inline constexpr auto preclaim = makeStr("preclaim");
|
||||
/// "apply" — value of the stage attribute on tx.transactor.
|
||||
/**
|
||||
* "apply" — value of the stage attribute on tx.transactor.
|
||||
*/
|
||||
inline constexpr auto apply = makeStr("apply");
|
||||
} // namespace val
|
||||
|
||||
|
||||
@@ -107,10 +107,14 @@ public:
|
||||
callHandler();
|
||||
|
||||
private:
|
||||
/** Owning collector. Prevents collector destruction while hook alive. */
|
||||
/**
|
||||
* Owning collector. Prevents collector destruction while hook alive.
|
||||
*/
|
||||
std::shared_ptr<OTelCollectorImp> impl_;
|
||||
|
||||
/** User-supplied handler called at each collection interval. */
|
||||
/**
|
||||
* User-supplied handler called at each collection interval.
|
||||
*/
|
||||
HandlerType handler_;
|
||||
};
|
||||
|
||||
@@ -151,7 +155,9 @@ public:
|
||||
increment(value_type amount) override;
|
||||
|
||||
private:
|
||||
/** OTel synchronous counter instrument. */
|
||||
/**
|
||||
* OTel synchronous counter instrument.
|
||||
*/
|
||||
opentelemetry::nostd::unique_ptr<metrics_api::Counter<uint64_t>> counter_;
|
||||
};
|
||||
|
||||
@@ -193,7 +199,9 @@ public:
|
||||
notify(value_type const& value) override;
|
||||
|
||||
private:
|
||||
/** OTel histogram instrument for recording durations. */
|
||||
/**
|
||||
* OTel histogram instrument for recording durations.
|
||||
*/
|
||||
opentelemetry::nostd::unique_ptr<metrics_api::Histogram<double>> histogram_;
|
||||
};
|
||||
|
||||
@@ -259,18 +267,26 @@ public:
|
||||
OTelGaugeImpl&
|
||||
operator=(OTelGaugeImpl const&) = delete;
|
||||
|
||||
/** Static callback registered with the OTel SDK observable gauge. */
|
||||
/**
|
||||
* Static callback registered with the OTel SDK observable gauge.
|
||||
*/
|
||||
static void
|
||||
gaugeCallback(opentelemetry::metrics::ObserverResult result, void* state);
|
||||
|
||||
private:
|
||||
/** Current gauge value, updated atomically by set()/increment(). */
|
||||
/**
|
||||
* Current gauge value, updated atomically by set()/increment().
|
||||
*/
|
||||
std::atomic<int64_t> value_{0};
|
||||
|
||||
/** OTel observable gauge handle (prevents deregistration). */
|
||||
/**
|
||||
* OTel observable gauge handle (prevents deregistration).
|
||||
*/
|
||||
opentelemetry::nostd::shared_ptr<metrics_api::ObservableInstrument> gauge_;
|
||||
|
||||
/** Owning collector, used to invoke hooks before reading gauge values. */
|
||||
/**
|
||||
* Owning collector, used to invoke hooks before reading gauge values.
|
||||
*/
|
||||
std::shared_ptr<OTelCollectorImp> collector_;
|
||||
};
|
||||
|
||||
@@ -315,7 +331,9 @@ public:
|
||||
increment(value_type amount) override;
|
||||
|
||||
private:
|
||||
/** OTel synchronous counter instrument (unsigned). */
|
||||
/**
|
||||
* OTel synchronous counter instrument (unsigned).
|
||||
*/
|
||||
opentelemetry::nostd::unique_ptr<metrics_api::Counter<uint64_t>> counter_;
|
||||
};
|
||||
|
||||
@@ -410,7 +428,9 @@ public:
|
||||
*/
|
||||
~OTelCollectorImp() override;
|
||||
|
||||
/** @name Collector interface implementation */
|
||||
/**
|
||||
* @name Collector interface implementation
|
||||
*/
|
||||
/** @{ */
|
||||
Hook
|
||||
makeHook(HookImpl::HandlerType const& handler) override;
|
||||
@@ -428,7 +448,9 @@ public:
|
||||
makeMeter(std::string const& name) override;
|
||||
/** @} */
|
||||
|
||||
/** @name Hook management for observable callbacks */
|
||||
/**
|
||||
* @name Hook management for observable callbacks
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/**
|
||||
@@ -455,7 +477,9 @@ public:
|
||||
callHooks();
|
||||
/** @} */
|
||||
|
||||
/** @name Gauge registration for observable callbacks */
|
||||
/**
|
||||
* @name Gauge registration for observable callbacks
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/**
|
||||
@@ -487,22 +511,34 @@ public:
|
||||
formatName(std::string const& name);
|
||||
|
||||
private:
|
||||
/** Journal for log output. */
|
||||
/**
|
||||
* Journal for log output.
|
||||
*/
|
||||
Journal journal_;
|
||||
|
||||
/** Prefix for all metric names (e.g., "xrpld"). */
|
||||
/**
|
||||
* Prefix for all metric names (e.g., "xrpld").
|
||||
*/
|
||||
std::string prefix_;
|
||||
|
||||
/** OTel Meter used to create all instruments. */
|
||||
/**
|
||||
* OTel Meter used to create all instruments.
|
||||
*/
|
||||
opentelemetry::nostd::shared_ptr<metrics_api::Meter> otelMeter_;
|
||||
|
||||
/** Mutex protecting hook and gauge registration lists. */
|
||||
/**
|
||||
* Mutex protecting hook and gauge registration lists.
|
||||
*/
|
||||
std::mutex mutex_;
|
||||
|
||||
/** Registered hooks called during observable callbacks. */
|
||||
/**
|
||||
* Registered hooks called during observable callbacks.
|
||||
*/
|
||||
std::vector<OTelHookImpl*> hooks_;
|
||||
|
||||
/** Registered gauges read during observable callbacks. */
|
||||
/**
|
||||
* Registered gauges read during observable callbacks.
|
||||
*/
|
||||
std::vector<OTelGaugeImpl*> gauges_;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/** No-op implementation of the Telemetry interface.
|
||||
|
||||
Always compiled (regardless of XRPL_ENABLE_TELEMETRY). Provides the
|
||||
makeTelemetry() factory when telemetry is compiled out (#ifndef), which
|
||||
unconditionally returns a NullTelemetry that does nothing.
|
||||
|
||||
When XRPL_ENABLE_TELEMETRY IS defined, the OTel virtual methods
|
||||
(getTracer, startSpan) return noop tracers/spans. The makeTelemetry()
|
||||
factory in this file is not used in that case -- Telemetry.cpp provides
|
||||
its own factory that can return the real TelemetryImpl.
|
||||
*/
|
||||
/**
|
||||
* No-op implementation of the Telemetry interface.
|
||||
*
|
||||
* Always compiled (regardless of XRPL_ENABLE_TELEMETRY). Provides the
|
||||
* makeTelemetry() factory when telemetry is compiled out (#ifndef), which
|
||||
* unconditionally returns a NullTelemetry that does nothing.
|
||||
*
|
||||
* When XRPL_ENABLE_TELEMETRY IS defined, the OTel virtual methods
|
||||
* (getTracer, startSpan) return noop tracers/spans. The makeTelemetry()
|
||||
* factory in this file is not used in that case -- Telemetry.cpp provides
|
||||
* its own factory that can return the real TelemetryImpl.
|
||||
*/
|
||||
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
|
||||
@@ -31,14 +32,17 @@ namespace xrpl::telemetry {
|
||||
|
||||
namespace {
|
||||
|
||||
/** No-op Telemetry that returns immediately from every method.
|
||||
|
||||
Used as the sole implementation when XRPL_ENABLE_TELEMETRY is not
|
||||
defined, or as a fallback when it is defined but enabled=0.
|
||||
*/
|
||||
/**
|
||||
* No-op Telemetry that returns immediately from every method.
|
||||
*
|
||||
* Used as the sole implementation when XRPL_ENABLE_TELEMETRY is not
|
||||
* defined, or as a fallback when it is defined but enabled=0.
|
||||
*/
|
||||
class NullTelemetry : public Telemetry
|
||||
{
|
||||
/** Retained configuration (unused, kept for diagnostic access). */
|
||||
/**
|
||||
* Retained configuration (unused, kept for diagnostic access).
|
||||
*/
|
||||
Setup const setup_;
|
||||
|
||||
public:
|
||||
@@ -130,9 +134,10 @@ public:
|
||||
|
||||
} // namespace
|
||||
|
||||
/** Factory used when XRPL_ENABLE_TELEMETRY is not defined.
|
||||
Unconditionally returns a NullTelemetry instance.
|
||||
*/
|
||||
/**
|
||||
* Factory used when XRPL_ENABLE_TELEMETRY is not defined.
|
||||
* Unconditionally returns a NullTelemetry instance.
|
||||
*/
|
||||
#ifndef XRPL_ENABLE_TELEMETRY
|
||||
std::unique_ptr<Telemetry>
|
||||
makeTelemetry(Telemetry::Setup const& setup, beast::Journal)
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/** OpenTelemetry SDK implementation of the Telemetry interface.
|
||||
|
||||
Compiled only when XRPL_ENABLE_TELEMETRY is defined (via CMake
|
||||
telemetry=ON). Contains:
|
||||
|
||||
- FilteringSpanProcessor: decorator that drops spans marked with
|
||||
kDiscardedAttr before they enter the batch export queue.
|
||||
- TelemetryImpl: configures the OTel SDK with an OTLP/HTTP exporter,
|
||||
FilteringSpanProcessor wrapping a batch span processor,
|
||||
trace-ID-ratio sampler, and resource attributes.
|
||||
- NullTelemetryOtel: no-op fallback used when telemetry is compiled in
|
||||
but disabled at runtime (enabled=0 in config).
|
||||
- makeTelemetry(): factory that selects the appropriate implementation.
|
||||
*/
|
||||
/**
|
||||
* OpenTelemetry SDK implementation of the Telemetry interface.
|
||||
*
|
||||
* Compiled only when XRPL_ENABLE_TELEMETRY is defined (via CMake
|
||||
* telemetry=ON). Contains:
|
||||
*
|
||||
* - FilteringSpanProcessor: decorator that drops spans marked with
|
||||
* kDiscardedAttr before they enter the batch export queue.
|
||||
* - TelemetryImpl: configures the OTel SDK with an OTLP/HTTP exporter,
|
||||
* FilteringSpanProcessor wrapping a batch span processor,
|
||||
* trace-ID-ratio sampler, and resource attributes.
|
||||
* - NullTelemetryOtel: no-op fallback used when telemetry is compiled in
|
||||
* but disabled at runtime (enabled=0 in config).
|
||||
* - makeTelemetry(): factory that selects the appropriate implementation.
|
||||
*/
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
|
||||
@@ -79,39 +80,40 @@ namespace metrics_sdk = opentelemetry::sdk::metrics;
|
||||
namespace otlp_http = opentelemetry::exporter::otlp;
|
||||
namespace resource = opentelemetry::sdk::resource;
|
||||
|
||||
/** SpanProcessor decorator that drops discarded spans.
|
||||
|
||||
Wraps a delegate processor (typically BatchSpanProcessor). In OnEnd(),
|
||||
calls DiscardScope::isActive(). If the calling thread is inside a
|
||||
DiscardScope (entered by SpanGuard::discard()), the span is silently
|
||||
dropped — never entering the batch queue, never sent over the network,
|
||||
never stored.
|
||||
|
||||
Uses a thread-local flag rather than inspecting Recordable attributes
|
||||
because the Recordable type varies by exporter (SpanData for simple
|
||||
exporters, OtlpRecordable for OTLP) and none expose a uniform getter.
|
||||
The flag is safe because Span::End() calls OnEnd() synchronously on
|
||||
the same thread.
|
||||
|
||||
All other methods delegate directly to the wrapped processor.
|
||||
|
||||
Dependency diagram:
|
||||
|
||||
+---------------------------+
|
||||
| FilteringSpanProcessor |
|
||||
+---------------------------+
|
||||
| - delegate_ : unique_ptr |
|
||||
| <SpanProcessor> |
|
||||
+---------------------------+
|
||||
| wraps
|
||||
+---------+-----------+
|
||||
| BatchSpanProcessor |
|
||||
+---------------------+
|
||||
|
||||
@note Thread safety: OnEnd() may be called concurrently from multiple
|
||||
threads. The discard flag behind DiscardScope is thread-local, so each
|
||||
thread's discard state is independent — no synchronization needed.
|
||||
*/
|
||||
/**
|
||||
* SpanProcessor decorator that drops discarded spans.
|
||||
*
|
||||
* Wraps a delegate processor (typically BatchSpanProcessor). In OnEnd(),
|
||||
* calls DiscardScope::isActive(). If the calling thread is inside a
|
||||
* DiscardScope (entered by SpanGuard::discard()), the span is silently
|
||||
* dropped — never entering the batch queue, never sent over the network,
|
||||
* never stored.
|
||||
*
|
||||
* Uses a thread-local flag rather than inspecting Recordable attributes
|
||||
* because the Recordable type varies by exporter (SpanData for simple
|
||||
* exporters, OtlpRecordable for OTLP) and none expose a uniform getter.
|
||||
* The flag is safe because Span::End() calls OnEnd() synchronously on
|
||||
* the same thread.
|
||||
*
|
||||
* All other methods delegate directly to the wrapped processor.
|
||||
*
|
||||
* Dependency diagram:
|
||||
*
|
||||
* +---------------------------+
|
||||
* | FilteringSpanProcessor |
|
||||
* +---------------------------+
|
||||
* | - delegate_ : unique_ptr |
|
||||
* | <SpanProcessor> |
|
||||
* +---------------------------+
|
||||
* | wraps
|
||||
* +---------+-----------+
|
||||
* | BatchSpanProcessor |
|
||||
* +---------------------+
|
||||
*
|
||||
* @note Thread safety: OnEnd() may be called concurrently from multiple
|
||||
* threads. The discard flag behind DiscardScope is thread-local, so each
|
||||
* thread's discard state is independent — no synchronization needed.
|
||||
*/
|
||||
class FilteringSpanProcessor : public trace_sdk::SpanProcessor
|
||||
{
|
||||
std::unique_ptr<trace_sdk::SpanProcessor> delegate_;
|
||||
@@ -163,15 +165,18 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** No-op implementation used when XRPL_ENABLE_TELEMETRY is defined but
|
||||
setup.enabled is false at runtime.
|
||||
|
||||
Lives in the anonymous namespace so there is no ODR conflict with the
|
||||
NullTelemetry in NullTelemetry.cpp.
|
||||
*/
|
||||
/**
|
||||
* No-op implementation used when XRPL_ENABLE_TELEMETRY is defined but
|
||||
* setup.enabled is false at runtime.
|
||||
*
|
||||
* Lives in the anonymous namespace so there is no ODR conflict with the
|
||||
* NullTelemetry in NullTelemetry.cpp.
|
||||
*/
|
||||
class NullTelemetryOtel : public Telemetry
|
||||
{
|
||||
/** Retained configuration (unused, kept for diagnostic access). */
|
||||
/**
|
||||
* Retained configuration (unused, kept for diagnostic access).
|
||||
*/
|
||||
Setup const setup_;
|
||||
|
||||
public:
|
||||
@@ -265,35 +270,41 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** Full OTel SDK implementation that exports trace spans via OTLP/HTTP.
|
||||
|
||||
Configures an OTLP/HTTP exporter, batch span processor,
|
||||
TraceIdRatioBasedSampler, and resource attributes on start().
|
||||
*/
|
||||
/**
|
||||
* Full OTel SDK implementation that exports trace spans via OTLP/HTTP.
|
||||
*
|
||||
* Configures an OTLP/HTTP exporter, batch span processor,
|
||||
* TraceIdRatioBasedSampler, and resource attributes on start().
|
||||
*/
|
||||
class TelemetryImpl : public Telemetry
|
||||
{
|
||||
/** Configuration from the [telemetry] config section.
|
||||
Non-const so setServiceInstanceId() can update the instance ID
|
||||
before start() creates the OTel resource.
|
||||
*/
|
||||
/**
|
||||
* Configuration from the [telemetry] config section.
|
||||
* Non-const so setServiceInstanceId() can update the instance ID
|
||||
* before start() creates the OTel resource.
|
||||
*/
|
||||
Setup setup_;
|
||||
|
||||
/** Journal used for log output during start/stop. */
|
||||
/**
|
||||
* Journal used for log output during start/stop.
|
||||
*/
|
||||
beast::Journal const journal_;
|
||||
|
||||
/** The SDK TracerProvider that owns the export pipeline.
|
||||
|
||||
Held as std::shared_ptr so we can call ForceFlush() on shutdown.
|
||||
Wrapped in a nostd::shared_ptr when registered as the global provider.
|
||||
*/
|
||||
/**
|
||||
* The SDK TracerProvider that owns the export pipeline.
|
||||
*
|
||||
* Held as std::shared_ptr so we can call ForceFlush() on shutdown.
|
||||
* Wrapped in a nostd::shared_ptr when registered as the global provider.
|
||||
*/
|
||||
std::shared_ptr<trace_sdk::TracerProvider> sdkProvider_;
|
||||
|
||||
/** The SDK MeterProvider that owns the metric export pipeline.
|
||||
|
||||
Symmetric with sdkProvider_ on the tracing side. Held as
|
||||
std::shared_ptr so we can ForceFlush() on shutdown; wrapped in a
|
||||
nostd::shared_ptr when registered as the global meter provider.
|
||||
*/
|
||||
/**
|
||||
* The SDK MeterProvider that owns the metric export pipeline.
|
||||
*
|
||||
* Symmetric with sdkProvider_ on the tracing side. Held as
|
||||
* std::shared_ptr so we can ForceFlush() on shutdown; wrapped in a
|
||||
* nostd::shared_ptr when registered as the global meter provider.
|
||||
*/
|
||||
std::shared_ptr<metrics_sdk::MeterProvider> meterProvider_;
|
||||
|
||||
public:
|
||||
@@ -394,15 +405,16 @@ public:
|
||||
JLOG(journal_.info()) << "Telemetry started successfully";
|
||||
}
|
||||
|
||||
/** Build and publish the metrics pipeline (MeterProvider + periodic
|
||||
reader + OTLP exporter + histogram view).
|
||||
|
||||
Called from the constructor, NOT start(), so the global MeterProvider
|
||||
exists before subsystems construct their beast::insight instruments
|
||||
during ApplicationImp's member-init list. The metrics resource uses
|
||||
setup_.serviceInstanceId from config; it is immutable once the provider
|
||||
is built, so a later node-key setServiceInstanceId() does not affect it.
|
||||
*/
|
||||
/**
|
||||
* Build and publish the metrics pipeline (MeterProvider + periodic
|
||||
* reader + OTLP exporter + histogram view).
|
||||
*
|
||||
* Called from the constructor, NOT start(), so the global MeterProvider
|
||||
* exists before subsystems construct their beast::insight instruments
|
||||
* during ApplicationImp's member-init list. The metrics resource uses
|
||||
* setup_.serviceInstanceId from config; it is immutable once the provider
|
||||
* is built, so a later node-key setServiceInstanceId() does not affect it.
|
||||
*/
|
||||
void
|
||||
initMetrics()
|
||||
{
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/** Parser for the [telemetry] section of xrpld.cfg.
|
||||
|
||||
Reads configuration values from the config file and populates a
|
||||
Telemetry::Setup struct. All options have sensible defaults so the
|
||||
section can be minimal or omitted entirely.
|
||||
|
||||
See cfg/xrpld-example.cfg for the full list of available options.
|
||||
*/
|
||||
/**
|
||||
* Parser for the [telemetry] section of xrpld.cfg.
|
||||
*
|
||||
* Reads configuration values from the config file and populates a
|
||||
* Telemetry::Setup struct. All options have sensible defaults so the
|
||||
* section can be minimal or omitted entirely.
|
||||
*
|
||||
* See cfg/xrpld-example.cfg for the full list of available options.
|
||||
*/
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
@@ -20,13 +21,14 @@ namespace xrpl::telemetry {
|
||||
|
||||
namespace {
|
||||
|
||||
/** Config key names for the [telemetry] section.
|
||||
|
||||
Each must match the corresponding option documented in
|
||||
cfg/xrpld-example.cfg verbatim. Defined as `char const*` so they
|
||||
pass to Section::valueOr() (which takes `std::string const&`)
|
||||
without an explicit conversion, exactly as a literal would.
|
||||
*/
|
||||
/**
|
||||
* Config key names for the [telemetry] section.
|
||||
*
|
||||
* Each must match the corresponding option documented in
|
||||
* cfg/xrpld-example.cfg verbatim. Defined as `char const*` so they
|
||||
* pass to Section::valueOr() (which takes `std::string const&`)
|
||||
* without an explicit conversion, exactly as a literal would.
|
||||
*/
|
||||
namespace key {
|
||||
constexpr char const* enabled = "enabled";
|
||||
constexpr char const* serviceName = "service_name";
|
||||
@@ -46,13 +48,14 @@ constexpr char const* tracePeer = "trace_peer";
|
||||
constexpr char const* traceLedger = "trace_ledger";
|
||||
} // namespace key
|
||||
|
||||
/** Default values applied when a key is absent from the config.
|
||||
|
||||
@note serviceName mirrors SystemParameters' systemName() ("xrpld") but
|
||||
is duplicated here as a literal: the telemetry module deliberately does
|
||||
not link xrpl.libxrpl.protocol, so including SystemParameters.h would
|
||||
introduce an undeclared cross-module dependency.
|
||||
*/
|
||||
/**
|
||||
* Default values applied when a key is absent from the config.
|
||||
*
|
||||
* @note serviceName mirrors SystemParameters' systemName() ("xrpld") but
|
||||
* is duplicated here as a literal: the telemetry module deliberately does
|
||||
* not link xrpl.libxrpl.protocol, so including SystemParameters.h would
|
||||
* introduce an undeclared cross-module dependency.
|
||||
*/
|
||||
namespace dflt {
|
||||
constexpr char const* serviceName = "xrpld";
|
||||
constexpr char const* endpoint = "http://localhost:4318/v1/traces";
|
||||
@@ -63,8 +66,15 @@ constexpr std::uint32_t maxQueueSize = 2048u;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Declared in Telemetry.h; shared by the trace and metric export paths so
|
||||
// both stamp the same xrpl.network.type resource attribute value.
|
||||
/**
|
||||
* Derive a human-readable network type label from the numeric network ID.
|
||||
*
|
||||
* Declared in Telemetry.h; shared by the trace and metric export paths so
|
||||
* both stamp the same xrpl.network.type resource attribute value.
|
||||
*
|
||||
* @param networkId The network identifier from [network_id] config.
|
||||
* @return "mainnet", "testnet", "devnet", or "unknown" for other values.
|
||||
*/
|
||||
std::string
|
||||
networkTypeFromId(std::uint32_t networkId)
|
||||
{
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/** @file GetMeter.cpp
|
||||
Unit tests for the direct OTel metrics API surface:
|
||||
- xrpl::telemetry::Telemetry::getMeter() on the disabled (noop) path.
|
||||
- The global MeterProvider contract that beast's OTelCollector shim
|
||||
relies on (GetMeterProvider()->GetMeter(kMeterName, kMeterVersion)).
|
||||
|
||||
These tests deliberately exercise the global-provider level rather than a
|
||||
fully started TelemetryImpl: TelemetryImpl::start() spins up an OTLP HTTP
|
||||
exporter with background export threads and network connect attempts, which
|
||||
is unsuitable for a hermetic unit test. The design spec permits testing the
|
||||
metrics unlock at the provider level, which is the exact path the beast shim
|
||||
and getMeter() delegate to.
|
||||
|
||||
Compiled only when XRPL_ENABLE_TELEMETRY is defined; the metrics API and SDK
|
||||
headers exist only in that build.
|
||||
*/
|
||||
/**
|
||||
* @file GetMeter.cpp
|
||||
* Unit tests for the direct OTel metrics API surface:
|
||||
* - xrpl::telemetry::Telemetry::getMeter() on the disabled (noop) path.
|
||||
* - The global MeterProvider contract that beast's OTelCollector shim
|
||||
* relies on (GetMeterProvider()->GetMeter(kMeterName, kMeterVersion)).
|
||||
*
|
||||
* These tests deliberately exercise the global-provider level rather than a
|
||||
* fully started TelemetryImpl: TelemetryImpl::start() spins up an OTLP HTTP
|
||||
* exporter with background export threads and network connect attempts, which
|
||||
* is unsuitable for a hermetic unit test. The design spec permits testing the
|
||||
* metrics unlock at the provider level, which is the exact path the beast shim
|
||||
* and getMeter() delegate to.
|
||||
*
|
||||
* Compiled only when XRPL_ENABLE_TELEMETRY is defined; the metrics API and SDK
|
||||
* headers exist only in that build.
|
||||
*/
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/** GTest unit tests for MetricsRegistry (no-op / telemetry-disabled path).
|
||||
/**
|
||||
* GTest unit tests for MetricsRegistry (no-op / telemetry-disabled path).
|
||||
*
|
||||
* Tests cover:
|
||||
* - Construction with telemetry disabled (no-op behavior).
|
||||
@@ -36,7 +37,8 @@ using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
|
||||
/** Minimal mock ServiceRegistry for MetricsRegistry testing.
|
||||
/**
|
||||
* Minimal mock ServiceRegistry for MetricsRegistry testing.
|
||||
*
|
||||
* Only the getMetricsRegistry() call is used in the tests; other methods
|
||||
* are not invoked because the registry is disabled (enabled=false) so no
|
||||
@@ -297,7 +299,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Test fixture that provides a MockServiceRegistry and null Journal.
|
||||
/**
|
||||
* Test fixture that provides a MockServiceRegistry and null Journal.
|
||||
*/
|
||||
class MetricsRegistryTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
/** Contract tests for the transaction apply-pipeline span constants.
|
||||
/**
|
||||
* Contract tests for the transaction apply-pipeline span constants.
|
||||
*
|
||||
* The span names and attribute keys in TxApplySpanNames.h are a cross-component
|
||||
* contract: the collector spanmetrics connector aggregates on these exact
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @file ValidationTracker.cpp
|
||||
Unit tests for xrpl::telemetry::ValidationTracker.
|
||||
*/
|
||||
/**
|
||||
* @file ValidationTracker.cpp
|
||||
* Unit tests for xrpl::telemetry::ValidationTracker.
|
||||
*/
|
||||
|
||||
#include <xrpld/telemetry/ValidationTracker.h>
|
||||
|
||||
@@ -17,14 +18,18 @@
|
||||
using namespace xrpl;
|
||||
using namespace xrpl::telemetry;
|
||||
|
||||
/// Helper to create a unique uint256 from an integer seed.
|
||||
/**
|
||||
* Helper to create a unique uint256 from an integer seed.
|
||||
*/
|
||||
static uint256
|
||||
makeHash(std::uint64_t n)
|
||||
{
|
||||
return uint256(n);
|
||||
}
|
||||
|
||||
/// Test fixture providing a fresh ValidationTracker per test.
|
||||
/**
|
||||
* Test fixture providing a fresh ValidationTracker per test.
|
||||
*/
|
||||
class ValidationTrackerTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -196,14 +196,16 @@ private:
|
||||
bool receiveDispatched_{false};
|
||||
std::unique_ptr<PeerSet> peerSet_;
|
||||
|
||||
/// Spans the acquire lifecycle: started in init(), finalized in done()
|
||||
/// with the outcome (complete/failed), timeout count, and peer count.
|
||||
/// Gives operators visibility into back-fill / fork-recovery cost, which
|
||||
/// previously emitted no span or metric.
|
||||
/// Stored detached: emplaced by the acquiring thread, reset on a
|
||||
/// JtLedgerData worker. detached() strips the thread-local Scope so the
|
||||
/// guard can be destroyed on the worker without corrupting the origin
|
||||
/// thread's context stack.
|
||||
/**
|
||||
* Spans the acquire lifecycle: started in init(), finalized in done()
|
||||
* with the outcome (complete/failed), timeout count, and peer count.
|
||||
* Gives operators visibility into back-fill / fork-recovery cost, which
|
||||
* previously emitted no span or metric.
|
||||
* Stored detached: emplaced by the acquiring thread, reset on a
|
||||
* JtLedgerData worker. detached() strips the thread-local Scope so the
|
||||
* guard can be destroyed on the worker without corrupting the origin
|
||||
* thread's context stack.
|
||||
*/
|
||||
std::optional<telemetry::SpanGuard> acquireSpan_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for ledger tracing.
|
||||
/**
|
||||
* Compile-time span name constants for ledger tracing.
|
||||
*
|
||||
* Used by BuildLedger and LedgerMaster for ledger lifecycle spans.
|
||||
* Built on StaticStr/join() from SpanNames.h.
|
||||
@@ -31,19 +32,25 @@ inline constexpr auto acquire = makeStr("acquire");
|
||||
// ===== Attribute keys ========================================================
|
||||
|
||||
namespace attr {
|
||||
/// Canonical shared constants (defined in SpanNames.h).
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h).
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::closeResolutionMs;
|
||||
using ::xrpl::telemetry::attr::closeTime;
|
||||
using ::xrpl::telemetry::attr::closeTimeCorrect;
|
||||
using ::xrpl::telemetry::attr::ledgerHash;
|
||||
using ::xrpl::telemetry::attr::ledgerSeq;
|
||||
|
||||
/// Domain-owned bare attrs.
|
||||
/**
|
||||
* Domain-owned bare attrs.
|
||||
*/
|
||||
inline constexpr auto txCount = makeStr("tx_count");
|
||||
inline constexpr auto txFailed = makeStr("tx_failed");
|
||||
inline constexpr auto validations = makeStr("validations");
|
||||
|
||||
/// ledger.acquire attrs (InboundLedger fetch lifecycle).
|
||||
/**
|
||||
* ledger.acquire attrs (InboundLedger fetch lifecycle).
|
||||
*/
|
||||
inline constexpr auto acquireReason = makeStr("acquire_reason");
|
||||
inline constexpr auto timeouts = makeStr("timeouts");
|
||||
inline constexpr auto peerCount = makeStr("peer_count");
|
||||
@@ -53,10 +60,14 @@ inline constexpr auto outcome = makeStr("outcome");
|
||||
// ===== Attribute values ======================================================
|
||||
|
||||
namespace val {
|
||||
/// ledger.acquire outcome values.
|
||||
/**
|
||||
* ledger.acquire outcome values.
|
||||
*/
|
||||
inline constexpr auto complete = makeStr("complete");
|
||||
inline constexpr auto failed = makeStr("failed");
|
||||
/// ledger.acquire reason values (mirror InboundLedger::Reason).
|
||||
/**
|
||||
* ledger.acquire reason values (mirror InboundLedger::Reason).
|
||||
*/
|
||||
inline constexpr auto history = makeStr("history");
|
||||
inline constexpr auto consensus = makeStr("consensus");
|
||||
inline constexpr auto generic = makeStr("generic");
|
||||
|
||||
@@ -221,8 +221,10 @@ public:
|
||||
beast::Journal journal_;
|
||||
std::unique_ptr<perf::PerfLog> perfLog_;
|
||||
std::unique_ptr<telemetry::Telemetry> telemetry_;
|
||||
/// OTel metrics registry for gap-fill metrics (counters, histograms,
|
||||
/// observable gauges). Created after telemetry_ during setup().
|
||||
/**
|
||||
* OTel metrics registry for gap-fill metrics (counters, histograms,
|
||||
* observable gauges). Created after telemetry_ during setup().
|
||||
*/
|
||||
std::unique_ptr<telemetry::MetricsRegistry> metricsRegistry_;
|
||||
Application::MutexType masterMutex_;
|
||||
|
||||
|
||||
@@ -25,15 +25,16 @@ public:
|
||||
group(std::string const& name) = 0;
|
||||
};
|
||||
|
||||
/** Construct the collector manager.
|
||||
|
||||
@param params The [insight] config section.
|
||||
@param serviceName service.name resource attribute for OTel metrics
|
||||
(empty -> the collector defaults it to "xrpld").
|
||||
@param networkType xrpl.network.type resource attribute for OTel
|
||||
metrics (e.g. "mainnet"), derived from [network_id].
|
||||
@param journal Journal for logging.
|
||||
*/
|
||||
/**
|
||||
* Construct the collector manager.
|
||||
*
|
||||
* @param params The [insight] config section.
|
||||
* @param serviceName service.name resource attribute for OTel metrics
|
||||
* (empty -> the collector defaults it to "xrpld").
|
||||
* @param networkType xrpl.network.type resource attribute for OTel
|
||||
* metrics (e.g. "mainnet"), derived from [network_id].
|
||||
* @param journal Journal for logging.
|
||||
*/
|
||||
std::unique_ptr<CollectorManager>
|
||||
makeCollectorManager(
|
||||
Section const& params,
|
||||
|
||||
@@ -197,7 +197,9 @@ private:
|
||||
|
||||
std::vector<boost::asio::ip::address> const& secureGatewayIPs_;
|
||||
|
||||
/// Human-readable name for telemetry spans (e.g. "GetLedger").
|
||||
/**
|
||||
* Human-readable name for telemetry spans (e.g. "GetLedger").
|
||||
*/
|
||||
std::string_view name_;
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for the gRPC subsystem.
|
||||
/**
|
||||
* Compile-time span name constants for the gRPC subsystem.
|
||||
*
|
||||
* All span prefixes, operation names, and attribute keys used by gRPC
|
||||
* tracing call sites are defined here. Built on the StaticStr/join()
|
||||
@@ -28,19 +29,27 @@ namespace xrpl::telemetry::grpc_span {
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "grpc" — root prefix for gRPC transport spans. The full span name is
|
||||
/// formed at the call site as `grpc.<MethodName>` (see GRPCServer.cpp).
|
||||
/**
|
||||
* "grpc" — root prefix for gRPC transport spans. The full span name is
|
||||
* formed at the call site as `grpc.<MethodName>` (see GRPCServer.cpp).
|
||||
*/
|
||||
inline constexpr auto grpc = makeStr("grpc");
|
||||
} // namespace prefix
|
||||
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/// "method" — gRPC method name (e.g. GetLedger).
|
||||
/**
|
||||
* "method" — gRPC method name (e.g. GetLedger).
|
||||
*/
|
||||
inline constexpr auto method = makeStr("method");
|
||||
/// "grpc_role" — Domain-qualified: collides with rpc_role.
|
||||
/**
|
||||
* "grpc_role" — Domain-qualified: collides with rpc_role.
|
||||
*/
|
||||
inline constexpr auto grpcRole = makeStr("grpc_role");
|
||||
/// "grpc_status" — Domain-qualified: avoids OTel reserved span status.
|
||||
/**
|
||||
* "grpc_status" — Domain-qualified: avoids OTel reserved span status.
|
||||
*/
|
||||
inline constexpr auto grpcStatus = makeStr("grpc_status");
|
||||
} // namespace attr
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for Transaction Queue tracing.
|
||||
/**
|
||||
* Compile-time span name constants for Transaction Queue tracing.
|
||||
*
|
||||
* Covers the TxQ lifecycle: enqueue decisions, direct apply, batch
|
||||
* clear, ledger-close accept loop, per-tx apply, and cleanup.
|
||||
@@ -55,7 +56,9 @@ namespace xrpl::telemetry::txq_span {
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "txq" — root prefix for transaction queue spans.
|
||||
/**
|
||||
* "txq" — root prefix for transaction queue spans.
|
||||
*/
|
||||
inline constexpr auto txq = makeStr("txq");
|
||||
} // namespace prefix
|
||||
|
||||
@@ -73,29 +76,51 @@ inline constexpr auto cleanup = makeStr("cleanup");
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/// Canonical shared constants (defined in SpanNames.h).
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h).
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::ledgerSeq;
|
||||
using ::xrpl::telemetry::attr::txHash;
|
||||
|
||||
/// "txq_status" — domain-qualified (collides with tx_status, rpc_status).
|
||||
/**
|
||||
* "txq_status" — domain-qualified (collides with tx_status, rpc_status).
|
||||
*/
|
||||
inline constexpr auto txqStatus = makeStr("txq_status");
|
||||
/// "fee_level_paid" — fee level paid by queued tx.
|
||||
/**
|
||||
* "fee_level_paid" — fee level paid by queued tx.
|
||||
*/
|
||||
inline constexpr auto feeLevelPaid = makeStr("fee_level_paid");
|
||||
/// "required_fee_level" — minimum fee level for inclusion.
|
||||
/**
|
||||
* "required_fee_level" — minimum fee level for inclusion.
|
||||
*/
|
||||
inline constexpr auto requiredFeeLevel = makeStr("required_fee_level");
|
||||
/// "queue_size" — current TxQ depth.
|
||||
/**
|
||||
* "queue_size" — current TxQ depth.
|
||||
*/
|
||||
inline constexpr auto queueSize = makeStr("queue_size");
|
||||
/// "ledger_changed" — whether ledger changed since last attempt.
|
||||
/**
|
||||
* "ledger_changed" — whether ledger changed since last attempt.
|
||||
*/
|
||||
inline constexpr auto ledgerChanged = makeStr("ledger_changed");
|
||||
/// "expired_count" — number of expired entries cleared.
|
||||
/**
|
||||
* "expired_count" — number of expired entries cleared.
|
||||
*/
|
||||
inline constexpr auto expiredCount = makeStr("expired_count");
|
||||
/// "ter_code" — transaction engine result code.
|
||||
/**
|
||||
* "ter_code" — transaction engine result code.
|
||||
*/
|
||||
inline constexpr auto terCode = makeStr("ter_code");
|
||||
/// "retries_remaining" — retries left before discard.
|
||||
/**
|
||||
* "retries_remaining" — retries left before discard.
|
||||
*/
|
||||
inline constexpr auto retriesRemaining = makeStr("retries_remaining");
|
||||
/// "num_cleared" — entries cleared in batch.
|
||||
/**
|
||||
* "num_cleared" — entries cleared in batch.
|
||||
*/
|
||||
inline constexpr auto numCleared = makeStr("num_cleared");
|
||||
/// "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
/**
|
||||
* "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
*/
|
||||
inline constexpr auto txType = makeStr("tx_type");
|
||||
} // namespace attr
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for consensus tracing.
|
||||
/**
|
||||
* Compile-time span name constants for consensus tracing.
|
||||
*
|
||||
* Used by RCLConsensus (app), Consensus.h (template), and PeerImp
|
||||
* (overlay) for consensus lifecycle spans.
|
||||
@@ -125,9 +126,11 @@ inline constexpr auto phaseOpen = join(seg::consensus, op::phaseOpen);
|
||||
// ===== Attribute keys ========================================================
|
||||
|
||||
namespace attr {
|
||||
/// Canonical shared constants (defined in SpanNames.h). `ledgerHash` and
|
||||
/// `fullValidation` are shared with the peer.validation.receive span — same
|
||||
/// concept, same key, distinguished by span name (not an emitter prefix).
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h). `ledgerHash` and
|
||||
* `fullValidation` are shared with the peer.validation.receive span — same
|
||||
* concept, same key, distinguished by span name (not an emitter prefix).
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::closeResolutionMs;
|
||||
using ::xrpl::telemetry::attr::closeTime;
|
||||
using ::xrpl::telemetry::attr::closeTimeCorrect;
|
||||
@@ -135,43 +138,67 @@ using ::xrpl::telemetry::attr::fullValidation;
|
||||
using ::xrpl::telemetry::attr::ledgerHash;
|
||||
using ::xrpl::telemetry::attr::ledgerSeq;
|
||||
|
||||
/// Domain-qualified attrs (rule 5 — bare name ambiguous across domains).
|
||||
/// Use `<domain>_<field>` underscore form for TraceQL ergonomics.
|
||||
/**
|
||||
* Domain-qualified attrs (rule 5 — bare name ambiguous across domains).
|
||||
* Use `<domain>_<field>` underscore form for TraceQL ergonomics.
|
||||
*/
|
||||
inline constexpr auto ledgerId = makeStr("consensus_ledger_id");
|
||||
inline constexpr auto mode = makeStr("consensus_mode");
|
||||
inline constexpr auto round = makeStr("consensus_round");
|
||||
inline constexpr auto roundId = makeStr("consensus_round_id");
|
||||
/// Current phase name attached to consensus.round; updated on each
|
||||
/// phase transition event (open/establish/accepted).
|
||||
/**
|
||||
* Current phase name attached to consensus.round; updated on each
|
||||
* phase transition event (open/establish/accepted).
|
||||
*/
|
||||
inline constexpr auto consensusPhase = makeStr("consensus_phase");
|
||||
/// Boolean flag set on consensus.check when checkConsensus reports stalled.
|
||||
/**
|
||||
* Boolean flag set on consensus.check when checkConsensus reports stalled.
|
||||
*/
|
||||
inline constexpr auto consensusStalled = makeStr("consensus_stalled");
|
||||
|
||||
/// Domain-owned bare attrs.
|
||||
/**
|
||||
* Domain-owned bare attrs.
|
||||
*/
|
||||
inline constexpr auto proposers = makeStr("proposers");
|
||||
inline constexpr auto roundTimeMs = makeStr("round_time_ms");
|
||||
inline constexpr auto proposing = makeStr("proposing");
|
||||
/// Round continuity / context attrs (set on consensus.round at round start).
|
||||
/**
|
||||
* Round continuity / context attrs (set on consensus.round at round start).
|
||||
*/
|
||||
inline constexpr auto previousProposers = makeStr("previous_proposers");
|
||||
inline constexpr auto previousRoundTimeMs = makeStr("previous_round_time_ms");
|
||||
inline constexpr auto previousLedgerSeq = makeStr("previous_ledger_seq");
|
||||
inline constexpr auto closeTimeResolutionMs = makeStr("close_time_resolution_ms");
|
||||
/// Open-phase end metadata (set on consensus.phase.open before reset).
|
||||
/**
|
||||
* Open-phase end metadata (set on consensus.phase.open before reset).
|
||||
*/
|
||||
inline constexpr auto openDurationMs = makeStr("open_duration_ms");
|
||||
inline constexpr auto peerPositionsAtClose = makeStr("peer_positions_at_close");
|
||||
/// Ledger-close inputs.
|
||||
/**
|
||||
* Ledger-close inputs.
|
||||
*/
|
||||
inline constexpr auto txCountOpen = makeStr("tx_count_open");
|
||||
/// Establish/check additional state.
|
||||
/**
|
||||
* Establish/check additional state.
|
||||
*/
|
||||
inline constexpr auto proposersFinished = makeStr("proposers_finished");
|
||||
/// Accept/apply enrichment.
|
||||
/**
|
||||
* Accept/apply enrichment.
|
||||
*/
|
||||
inline constexpr auto disputesResolvedCount = makeStr("disputes_resolved_count");
|
||||
/// Validation send/receive enrichment. (`full_validation` is shared — see the
|
||||
/// `using` re-export above.)
|
||||
/**
|
||||
* Validation send/receive enrichment. (`full_validation` is shared — see the
|
||||
* `using` re-export above.)
|
||||
*/
|
||||
inline constexpr auto validationSignTime = makeStr("validation_sign_time");
|
||||
/// Receive-side hash prefixes for cross-peer correlation.
|
||||
/**
|
||||
* Receive-side hash prefixes for cross-peer correlation.
|
||||
*/
|
||||
inline constexpr auto prevLedgerPrefix = makeStr("prev_ledger_prefix");
|
||||
inline constexpr auto positionHashPrefix = makeStr("position_hash_prefix");
|
||||
/// "consensus_state" — domain-qualified (collides with other domains' state).
|
||||
/**
|
||||
* "consensus_state" — domain-qualified (collides with other domains' state).
|
||||
*/
|
||||
inline constexpr auto consensusState = makeStr("consensus_state");
|
||||
inline constexpr auto parentCloseTime = makeStr("parent_close_time");
|
||||
inline constexpr auto closeTimeSelf = makeStr("close_time_self");
|
||||
@@ -185,27 +212,35 @@ inline constexpr auto haveCloseTimeConsensus = makeStr("have_close_time_consensu
|
||||
inline constexpr auto agreeCount = makeStr("agree_count");
|
||||
inline constexpr auto disagreeCount = makeStr("disagree_count");
|
||||
inline constexpr auto thresholdPercent = makeStr("threshold_percent");
|
||||
/// "consensus_result" — domain-qualified (collides with generic result).
|
||||
/**
|
||||
* "consensus_result" — domain-qualified (collides with generic result).
|
||||
*/
|
||||
inline constexpr auto consensusResult = makeStr("consensus_result");
|
||||
inline constexpr auto quorum = makeStr("quorum");
|
||||
inline constexpr auto traceStrategy = makeStr("trace_strategy");
|
||||
inline constexpr auto modeOld = makeStr("mode_old");
|
||||
inline constexpr auto modeNew = makeStr("mode_new");
|
||||
|
||||
/// "is_bow_out" — whether this proposal is a bow-out (resigning from round).
|
||||
/**
|
||||
* "is_bow_out" — whether this proposal is a bow-out (resigning from round).
|
||||
*/
|
||||
inline constexpr auto isBowOut = makeStr("is_bow_out");
|
||||
|
||||
/// Transaction/dispute attrs used in consensus accept spans.
|
||||
/**
|
||||
* Transaction/dispute attrs used in consensus accept spans.
|
||||
*/
|
||||
inline constexpr auto txId = makeStr("tx_id");
|
||||
inline constexpr auto disputeOurVote = makeStr("dispute_our_vote");
|
||||
inline constexpr auto disputeYays = makeStr("dispute_yays");
|
||||
inline constexpr auto disputeNays = makeStr("dispute_nays");
|
||||
inline constexpr auto txCount = makeStr("tx_count");
|
||||
inline constexpr auto disputesCount = makeStr("disputes_count");
|
||||
/// Trust flag (is the message origin a trusted UNL validator). Qualified by
|
||||
/// message type, shared with the peer.{proposal,validation}.receive spans:
|
||||
/// consensus.proposal.receive uses `proposal_trusted`, consensus.validation.
|
||||
/// receive uses `validation_trusted`. Same concept on both emitters → same key.
|
||||
/**
|
||||
* Trust flag (is the message origin a trusted UNL validator). Qualified by
|
||||
* message type, shared with the peer.{proposal,validation}.receive spans:
|
||||
* consensus.proposal.receive uses `proposal_trusted`, consensus.validation.
|
||||
* receive uses `validation_trusted`. Same concept on both emitters → same key.
|
||||
*/
|
||||
inline constexpr auto proposalTrusted = makeStr("proposal_trusted");
|
||||
inline constexpr auto validationTrusted = makeStr("validation_trusted");
|
||||
} // namespace attr
|
||||
@@ -213,21 +248,29 @@ inline constexpr auto validationTrusted = makeStr("validation_trusted");
|
||||
// ===== Event names ===========================================================
|
||||
|
||||
namespace event {
|
||||
/// "dispute.resolve"
|
||||
/**
|
||||
* "dispute.resolve"
|
||||
*/
|
||||
inline constexpr auto disputeResolve = join(makeStr("dispute"), makeStr("resolve"));
|
||||
/// "tx.included"
|
||||
/**
|
||||
* "tx.included"
|
||||
*/
|
||||
inline constexpr auto txIncluded = join(makeStr("tx"), makeStr("included"));
|
||||
|
||||
/// Phase transition events — fired on consensus.round at each transition
|
||||
/// so the round-level span carries a complete timeline of phase changes,
|
||||
/// including the handleWrongLedger recovery edge that re-enters Open.
|
||||
/**
|
||||
* Phase transition events — fired on consensus.round at each transition
|
||||
* so the round-level span carries a complete timeline of phase changes,
|
||||
* including the handleWrongLedger recovery edge that re-enters Open.
|
||||
*/
|
||||
inline constexpr auto phaseOpen = join(makeStr("phase"), makeStr("open"));
|
||||
inline constexpr auto phaseEstablish = join(makeStr("phase"), makeStr("establish"));
|
||||
inline constexpr auto phaseAccepted = join(makeStr("phase"), makeStr("accepted"));
|
||||
inline constexpr auto phaseRecovery = join(makeStr("phase"), makeStr("recovery"));
|
||||
|
||||
/// Outcome events — fired on consensus.round at the establish→accepted
|
||||
/// transition so the path that drove acceptance is queryable.
|
||||
/**
|
||||
* Outcome events — fired on consensus.round at the establish→accepted
|
||||
* transition so the path that drove acceptance is queryable.
|
||||
*/
|
||||
inline constexpr auto outcomeYes = join(makeStr("outcome"), makeStr("yes"));
|
||||
inline constexpr auto outcomeMovedOn = join(makeStr("outcome"), makeStr("moved_on"));
|
||||
inline constexpr auto outcomeExpired = join(makeStr("outcome"), makeStr("expired"));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for peer overlay tracing.
|
||||
/**
|
||||
* Compile-time span name constants for peer overlay tracing.
|
||||
*
|
||||
* Used by PeerImp for peer message handling spans (proposals,
|
||||
* validations). Built on StaticStr/join() from SpanNames.h.
|
||||
@@ -25,14 +26,18 @@ inline constexpr auto validationReceive = makeStr("validation.receive");
|
||||
// ===== Attribute keys ========================================================
|
||||
|
||||
namespace attr {
|
||||
/// Canonical shared constants (defined in SpanNames.h). `ledgerHash` and
|
||||
/// `fullValidation` are shared with the consensus validation spans — same
|
||||
/// concept, same key, told apart by span name.
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h). `ledgerHash` and
|
||||
* `fullValidation` are shared with the consensus validation spans — same
|
||||
* concept, same key, told apart by span name.
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::fullValidation;
|
||||
using ::xrpl::telemetry::attr::ledgerHash;
|
||||
using ::xrpl::telemetry::attr::peerId;
|
||||
|
||||
/// Trust flag qualified by message type, shared with consensus.*.receive.
|
||||
/**
|
||||
* Trust flag qualified by message type, shared with consensus.*.receive.
|
||||
*/
|
||||
inline constexpr auto proposalTrusted = makeStr("proposal_trusted");
|
||||
inline constexpr auto validationTrusted = makeStr("validation_trusted");
|
||||
} // namespace attr
|
||||
|
||||
@@ -192,10 +192,11 @@ private:
|
||||
void
|
||||
processSession(std::shared_ptr<Session> const&, std::shared_ptr<JobQueue::Coro> coro);
|
||||
|
||||
/** Process an RPC request and write the reply to `output`.
|
||||
@return false if the request resulted in an error response, true
|
||||
otherwise. Lets the caller's enclosing span reflect the outcome.
|
||||
*/
|
||||
/**
|
||||
* Process an RPC request and write the reply to `output`.
|
||||
* @return false if the request resulted in an error response, true
|
||||
* otherwise. Lets the caller's enclosing span reflect the outcome.
|
||||
*/
|
||||
bool
|
||||
processRequest(
|
||||
Port const& port,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for PathFind tracing.
|
||||
/**
|
||||
* Compile-time span name constants for PathFind tracing.
|
||||
*
|
||||
* Covers the path_find and ripple_path_find RPC handlers, the
|
||||
* PathRequest computation engine, and the Pathfinder graph exploration.
|
||||
@@ -48,7 +49,9 @@ namespace xrpl::telemetry::pathfind_span {
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "pathfind" — root prefix for path finding spans.
|
||||
/**
|
||||
* "pathfind" — root prefix for path finding spans.
|
||||
*/
|
||||
inline constexpr auto pathfind = makeStr("pathfind");
|
||||
} // namespace prefix
|
||||
|
||||
@@ -68,25 +71,43 @@ inline constexpr auto discover = makeStr("discover");
|
||||
// `fast` or `num_paths` that other subsystems may introduce.
|
||||
|
||||
namespace attr {
|
||||
/// "pathfind_source_account" — originating account for path search.
|
||||
/**
|
||||
* "pathfind_source_account" — originating account for path search.
|
||||
*/
|
||||
inline constexpr auto sourceAccount = makeStr("pathfind_source_account");
|
||||
/// "pathfind_dest_account" — destination account.
|
||||
/**
|
||||
* "pathfind_dest_account" — destination account.
|
||||
*/
|
||||
inline constexpr auto destAccount = makeStr("pathfind_dest_account");
|
||||
/// "pathfind_fast" — whether fast pathfinding mode enabled.
|
||||
/**
|
||||
* "pathfind_fast" — whether fast pathfinding mode enabled.
|
||||
*/
|
||||
inline constexpr auto fast = makeStr("pathfind_fast");
|
||||
/// "pathfind_search_level" — depth of graph exploration.
|
||||
/**
|
||||
* "pathfind_search_level" — depth of graph exploration.
|
||||
*/
|
||||
inline constexpr auto searchLevel = makeStr("pathfind_search_level");
|
||||
/// "pathfind_num_paths" — total paths produced across the per-source-asset
|
||||
/// loop in PathRequest::findPaths (sum of getBestPaths().size() per asset).
|
||||
/**
|
||||
* "pathfind_num_paths" — total paths produced across the per-source-asset
|
||||
* loop in PathRequest::findPaths (sum of getBestPaths().size() per asset).
|
||||
*/
|
||||
inline constexpr auto numPaths = makeStr("pathfind_num_paths");
|
||||
/// "pathfind_num_requests" — snapshot size of requests_ at update_all start
|
||||
/// (may include weak_ptrs that subsequently expire during processing).
|
||||
/**
|
||||
* "pathfind_num_requests" — snapshot size of requests_ at update_all start
|
||||
* (may include weak_ptrs that subsequently expire during processing).
|
||||
*/
|
||||
inline constexpr auto numRequests = makeStr("pathfind_num_requests");
|
||||
/// "pathfind_ledger_index" — pathfind target ledger index.
|
||||
/**
|
||||
* "pathfind_ledger_index" — pathfind target ledger index.
|
||||
*/
|
||||
inline constexpr auto ledgerIndex = makeStr("pathfind_ledger_index");
|
||||
/// "pathfind_dest_currency" — destination currency code.
|
||||
/**
|
||||
* "pathfind_dest_currency" — destination currency code.
|
||||
*/
|
||||
inline constexpr auto destCurrency = makeStr("pathfind_dest_currency");
|
||||
/// "pathfind_num_source_assets" — candidate source assets count.
|
||||
/**
|
||||
* "pathfind_num_source_assets" — candidate source assets count.
|
||||
*/
|
||||
inline constexpr auto numSourceAssets = makeStr("pathfind_num_source_assets");
|
||||
} // namespace attr
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for the RPC subsystem.
|
||||
/**
|
||||
* Compile-time span name constants for the RPC subsystem.
|
||||
*
|
||||
* All span prefixes, operation names, and attribute keys used by RPC
|
||||
* tracing call sites are defined here. Built on the StaticStr/join()
|
||||
@@ -116,9 +117,13 @@ namespace xrpl::telemetry::rpc_span {
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "rpc" — root prefix for transport-level spans.
|
||||
/**
|
||||
* "rpc" — root prefix for transport-level spans.
|
||||
*/
|
||||
inline constexpr auto rpc = seg::rpc;
|
||||
/// "rpc.command" — prefix for individual RPC command spans.
|
||||
/**
|
||||
* "rpc.command" — prefix for individual RPC command spans.
|
||||
*/
|
||||
inline constexpr auto command = join(seg::rpc, makeStr("command"));
|
||||
} // namespace prefix
|
||||
|
||||
@@ -134,21 +139,37 @@ inline constexpr auto process = makeStr("process");
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/// "command" — RPC method name.
|
||||
/**
|
||||
* "command" — RPC method name.
|
||||
*/
|
||||
inline constexpr auto command = makeStr("command");
|
||||
/// "version" — api_version per request.
|
||||
/**
|
||||
* "version" — api_version per request.
|
||||
*/
|
||||
inline constexpr auto version = makeStr("version");
|
||||
/// "rpc_role" — admin|user. Domain-qualified: collides with grpc_role.
|
||||
/**
|
||||
* "rpc_role" — admin|user. Domain-qualified: collides with grpc_role.
|
||||
*/
|
||||
inline constexpr auto rpcRole = makeStr("rpc_role");
|
||||
/// "rpc_status" — success|error. Domain-qualified: avoids OTel reserved span status.
|
||||
/**
|
||||
* "rpc_status" — success|error. Domain-qualified: avoids OTel reserved span status.
|
||||
*/
|
||||
inline constexpr auto rpcStatus = makeStr("rpc_status");
|
||||
/// "request_payload_size" — bytes of inbound request payload.
|
||||
/**
|
||||
* "request_payload_size" — bytes of inbound request payload.
|
||||
*/
|
||||
inline constexpr auto requestPayloadSize = makeStr("request_payload_size");
|
||||
/// "is_batch" — whether request is a JSON-RPC batch.
|
||||
/**
|
||||
* "is_batch" — whether request is a JSON-RPC batch.
|
||||
*/
|
||||
inline constexpr auto isBatch = makeStr("is_batch");
|
||||
/// "batch_size" — number of sub-requests in a batch.
|
||||
/**
|
||||
* "batch_size" — number of sub-requests in a batch.
|
||||
*/
|
||||
inline constexpr auto batchSize = makeStr("batch_size");
|
||||
/// "load_type" — resource cost category after execution.
|
||||
/**
|
||||
* "load_type" — resource cost category after execution.
|
||||
*/
|
||||
inline constexpr auto loadType = makeStr("load_type");
|
||||
} // namespace attr
|
||||
|
||||
@@ -160,7 +181,9 @@ using telemetry::attr_val::success;
|
||||
inline constexpr auto admin = makeStr("admin");
|
||||
inline constexpr auto user = makeStr("user");
|
||||
inline constexpr auto unknownCommand = makeStr("unknown");
|
||||
/// "invalid_json" — WS message parse failure or oversize.
|
||||
/**
|
||||
* "invalid_json" — WS message parse failure or oversize.
|
||||
*/
|
||||
inline constexpr auto invalidJson = makeStr("invalid_json");
|
||||
} // namespace val
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Helpers for injecting trace context into protobuf messages.
|
||||
/**
|
||||
* Helpers for injecting trace context into protobuf messages.
|
||||
*
|
||||
* Bridges the gap between SpanGuard (which hides OTel types) and the
|
||||
* protobuf TraceContext message used for cross-node propagation.
|
||||
@@ -13,7 +14,7 @@
|
||||
* | |
|
||||
* injectSpanContext(span, proto)
|
||||
*
|
||||
* @note When XRPL_ENABLE_TELEMETRY is disabled, getTraceBytes() returns
|
||||
* @note When XRPL_ENABLE_TELEMETRY is disabled, getTraceBytes() returns
|
||||
* {.valid=false}, so injectSpanContext becomes a no-op with zero overhead.
|
||||
*
|
||||
* Usage:
|
||||
@@ -25,8 +26,9 @@
|
||||
* overlay.relay(txID, tx, toSkip);
|
||||
* @endcode
|
||||
*
|
||||
* @see ConsensusReceiveTracing.h for receive-side extraction helpers.
|
||||
* @see TraceContextPropagator.h for low-level OTel context serialization.
|
||||
* @see ConsensusReceiveTracing.h and TxTracing.h for receive-side
|
||||
* extraction helpers.
|
||||
* @see TraceContextPropagator.h for low-level OTel context serialization.
|
||||
*/
|
||||
|
||||
#include <xrpl/proto/xrpl.pb.h>
|
||||
@@ -34,7 +36,8 @@
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** Inject trace context from an active SpanGuard into a protobuf
|
||||
/**
|
||||
* Inject trace context from an active SpanGuard into a protobuf
|
||||
* TraceContext message for cross-node propagation.
|
||||
*
|
||||
* Reads the span's trace_id, span_id, and trace_flags via
|
||||
@@ -42,8 +45,8 @@ namespace xrpl::telemetry {
|
||||
* Safe to call from any thread that holds a reference to the span.
|
||||
* No-op if the span is null or inactive.
|
||||
*
|
||||
* @param span The active SpanGuard whose context to propagate.
|
||||
* @param proto The protobuf TraceContext to populate.
|
||||
* @param span The active SpanGuard whose context to propagate.
|
||||
* @param proto The protobuf TraceContext to populate.
|
||||
*/
|
||||
inline void
|
||||
injectSpanContext(SpanGuard const& span, protocol::TraceContext& proto)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Compile-time span name constants for transaction tracing.
|
||||
/**
|
||||
* Compile-time span name constants for transaction tracing.
|
||||
*
|
||||
* Used by PeerImp (overlay) and NetworkOPs (app) for transaction
|
||||
* lifecycle spans. Built on StaticStr/join() from SpanNames.h.
|
||||
@@ -22,7 +23,9 @@ namespace xrpl::telemetry::tx_span {
|
||||
// ===== Span prefixes =======================================================
|
||||
|
||||
namespace prefix {
|
||||
/// "tx" — root prefix for transaction lifecycle spans.
|
||||
/**
|
||||
* "tx" — root prefix for transaction lifecycle spans.
|
||||
*/
|
||||
inline constexpr auto tx = seg::tx;
|
||||
} // namespace prefix
|
||||
|
||||
@@ -41,29 +44,51 @@ inline constexpr auto process = join(prefix::tx, op::process);
|
||||
// ===== Attribute keys ======================================================
|
||||
|
||||
namespace attr {
|
||||
/// Canonical shared constants (defined in SpanNames.h).
|
||||
/**
|
||||
* Canonical shared constants (defined in SpanNames.h).
|
||||
*/
|
||||
using ::xrpl::telemetry::attr::peerId;
|
||||
using ::xrpl::telemetry::attr::txHash;
|
||||
|
||||
/// "local" — whether tx originated locally.
|
||||
/**
|
||||
* "local" — whether tx originated locally.
|
||||
*/
|
||||
inline constexpr auto local = makeStr("local");
|
||||
/// "path" — sync or async processing path.
|
||||
/**
|
||||
* "path" — sync or async processing path.
|
||||
*/
|
||||
inline constexpr auto path = makeStr("path");
|
||||
/// "suppressed" — whether tx was suppressed as duplicate.
|
||||
/**
|
||||
* "suppressed" — whether tx was suppressed as duplicate.
|
||||
*/
|
||||
inline constexpr auto suppressed = makeStr("suppressed");
|
||||
/// "tx_status" — domain-qualified (collides with rpc_status, txq_status).
|
||||
/**
|
||||
* "tx_status" — domain-qualified (collides with rpc_status, txq_status).
|
||||
*/
|
||||
inline constexpr auto txStatus = makeStr("tx_status");
|
||||
/// "peer_version" — version of peer that sent the tx.
|
||||
/**
|
||||
* "peer_version" — version of peer that sent the tx.
|
||||
*/
|
||||
inline constexpr auto peerVersion = makeStr("peer_version");
|
||||
/// "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
/**
|
||||
* "tx_type" — transaction type name (e.g., "Payment", "OfferCreate").
|
||||
*/
|
||||
inline constexpr auto txType = makeStr("tx_type");
|
||||
/// "fee" — transaction fee in drops.
|
||||
/**
|
||||
* "fee" — transaction fee in drops.
|
||||
*/
|
||||
inline constexpr auto fee = makeStr("fee");
|
||||
/// "sequence" — transaction sequence number.
|
||||
/**
|
||||
* "sequence" — transaction sequence number.
|
||||
*/
|
||||
inline constexpr auto sequence = makeStr("sequence");
|
||||
/// "ter_result" — engine result code after application.
|
||||
/**
|
||||
* "ter_result" — engine result code after application.
|
||||
*/
|
||||
inline constexpr auto terResult = makeStr("ter_result");
|
||||
/// "applied" — whether the transaction was applied to the ledger.
|
||||
/**
|
||||
* "applied" — whether the transaction was applied to the ledger.
|
||||
*/
|
||||
inline constexpr auto applied = makeStr("applied");
|
||||
} // namespace attr
|
||||
|
||||
@@ -73,16 +98,24 @@ namespace val {
|
||||
inline constexpr auto sync = makeStr("sync");
|
||||
inline constexpr auto async = makeStr("async");
|
||||
inline constexpr auto knownBad = makeStr("known_bad");
|
||||
/// Transaction was suppressed via HashRouter (duplicate, not flagged bad).
|
||||
/**
|
||||
* Transaction was suppressed via HashRouter (duplicate, not flagged bad).
|
||||
*/
|
||||
inline constexpr auto suppressed = makeStr("suppressed");
|
||||
/// Transaction was rejected because it carried tfInnerBatchTxn, which
|
||||
/// must never appear in network-relayed traffic.
|
||||
/**
|
||||
* Transaction was rejected because it carried tfInnerBatchTxn, which
|
||||
* must never appear in network-relayed traffic.
|
||||
*/
|
||||
inline constexpr auto rejectedInnerBatch = makeStr("rejected_inner_batch");
|
||||
/// Transaction was dropped because the validated ledger is too old to
|
||||
/// confidently apply new transactions (server is out of sync).
|
||||
/**
|
||||
* Transaction was dropped because the validated ledger is too old to
|
||||
* confidently apply new transactions (server is out of sync).
|
||||
*/
|
||||
inline constexpr auto droppedNoSync = makeStr("dropped_no_sync");
|
||||
/// Transaction was dropped because the local job queue for jtTRANSACTION
|
||||
/// is at MAX_TRANSACTIONS — backpressure on the receive side.
|
||||
/**
|
||||
* Transaction was dropped because the local job queue for jtTRANSACTION
|
||||
* is at MAX_TRANSACTIONS — backpressure on the receive side.
|
||||
*/
|
||||
inline constexpr auto droppedQueueFull = makeStr("dropped_queue_full");
|
||||
} // namespace val
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/** Helper functions for creating transaction trace spans.
|
||||
/**
|
||||
* Helper functions for creating transaction trace spans.
|
||||
*
|
||||
* Encapsulates the logic for creating SpanGuard instances with
|
||||
* hash-derived trace IDs and optional protobuf parent extraction.
|
||||
@@ -21,7 +22,8 @@
|
||||
|
||||
namespace xrpl::telemetry {
|
||||
|
||||
/** Create a "tx.receive" span for a transaction received from a peer.
|
||||
/**
|
||||
* Create a "tx.receive" span for a transaction received from a peer.
|
||||
* trace_id is derived from txID[0:16]. If the incoming message carries
|
||||
* a protobuf TraceContext with a valid span_id, it is used as the
|
||||
* parent to preserve relay ordering.
|
||||
@@ -53,7 +55,8 @@ txReceiveSpan(uint256 const& txID, [[maybe_unused]] protocol::TMTransaction cons
|
||||
TraceCategory::Transactions, tx_span::receive, txID.data(), txID.kBytes);
|
||||
}
|
||||
|
||||
/** Create a "tx.process" span for transaction processing in NetworkOPs.
|
||||
/**
|
||||
* Create a "tx.process" span for transaction processing in NetworkOPs.
|
||||
* trace_id is derived from txID[0:16].
|
||||
*/
|
||||
inline SpanGuard
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
/** @file ValidationTracker.h
|
||||
Standalone validation agreement tracker for telemetry.
|
||||
*/
|
||||
/**
|
||||
* @file ValidationTracker.h
|
||||
* Standalone validation agreement tracker for telemetry.
|
||||
*/
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -93,10 +94,14 @@ namespace xrpl::telemetry {
|
||||
class ValidationTracker
|
||||
{
|
||||
public:
|
||||
/// Monotonic clock used for all internal timestamps.
|
||||
/**
|
||||
* Monotonic clock used for all internal timestamps.
|
||||
*/
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
/// Time point type from the monotonic clock.
|
||||
/**
|
||||
* Time point type from the monotonic clock.
|
||||
*/
|
||||
using TimePoint = Clock::time_point;
|
||||
|
||||
/**
|
||||
@@ -124,94 +129,127 @@ public:
|
||||
void
|
||||
reconcile();
|
||||
|
||||
/** @name Rolling-window percentage getters */
|
||||
/**
|
||||
* @name Rolling-window percentage getters
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** Agreement percentage over the last 1 hour.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
/**
|
||||
* Agreement percentage over the last 1 hour.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
*/
|
||||
double
|
||||
agreementPct1h() const;
|
||||
|
||||
/** Agreement percentage over the last 24 hours.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
/**
|
||||
* Agreement percentage over the last 24 hours.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
*/
|
||||
double
|
||||
agreementPct24h() const;
|
||||
|
||||
/** Agreement percentage over the last 7 days.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
/**
|
||||
* Agreement percentage over the last 7 days.
|
||||
* @return Percentage [0.0, 100.0], or 0.0 if no data.
|
||||
*/
|
||||
double
|
||||
agreementPct7d() const;
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name Rolling-window count getters */
|
||||
/**
|
||||
* @name Rolling-window count getters
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** Number of agreements in the 1-hour window. */
|
||||
/**
|
||||
* Number of agreements in the 1-hour window.
|
||||
*/
|
||||
uint64_t
|
||||
agreements1h() const;
|
||||
|
||||
/** Number of misses in the 1-hour window. */
|
||||
/**
|
||||
* Number of misses in the 1-hour window.
|
||||
*/
|
||||
uint64_t
|
||||
missed1h() const;
|
||||
|
||||
/** Number of agreements in the 24-hour window. */
|
||||
/**
|
||||
* Number of agreements in the 24-hour window.
|
||||
*/
|
||||
uint64_t
|
||||
agreements24h() const;
|
||||
|
||||
/** Number of misses in the 24-hour window. */
|
||||
/**
|
||||
* Number of misses in the 24-hour window.
|
||||
*/
|
||||
uint64_t
|
||||
missed24h() const;
|
||||
|
||||
/** Number of agreements in the 7-day window. */
|
||||
/**
|
||||
* Number of agreements in the 7-day window.
|
||||
*/
|
||||
uint64_t
|
||||
agreements7d() const;
|
||||
|
||||
/** Number of misses in the 7-day window. */
|
||||
/**
|
||||
* Number of misses in the 7-day window.
|
||||
*/
|
||||
uint64_t
|
||||
missed7d() const;
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @name Lifetime totals (atomic, lock-free reads) */
|
||||
/**
|
||||
* @name Lifetime totals (atomic, lock-free reads)
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/** Total agreements since process start. */
|
||||
/**
|
||||
* Total agreements since process start.
|
||||
*/
|
||||
uint64_t
|
||||
totalAgreements() const;
|
||||
|
||||
/** Total misses since process start. */
|
||||
/**
|
||||
* Total misses since process start.
|
||||
*/
|
||||
uint64_t
|
||||
totalMissed() const;
|
||||
|
||||
/** Lifetime agreements counted at first classification only.
|
||||
* @note Unlike totalAgreements(), this is strictly monotonic: it is
|
||||
* incremented only when a ledger is first reconciled as an agreement and
|
||||
* is never adjusted by a late repair. It backs the monotonic Prometheus
|
||||
* counter validation_agreements_total. See the counting-semantics
|
||||
* note in detail/ValidationTracker.cpp.
|
||||
/**
|
||||
* Lifetime agreements counted at first classification only.
|
||||
*
|
||||
* @note Unlike totalAgreements(), this is strictly monotonic: it is
|
||||
* incremented only when a ledger is first reconciled as an agreement and
|
||||
* is never adjusted by a late repair. It backs the monotonic Prometheus
|
||||
* counter validation_agreements_total. See the counting-semantics
|
||||
* note in detail/ValidationTracker.cpp.
|
||||
*/
|
||||
uint64_t
|
||||
totalAgreementsEver() const;
|
||||
|
||||
/** Lifetime misses counted at first classification only.
|
||||
* @note Unlike totalMissed(), this is strictly monotonic: it is
|
||||
* incremented only when a ledger is first reconciled as a miss and is
|
||||
* never decremented by a late repair. It backs the monotonic Prometheus
|
||||
* counter validation_missed_total. See the counting-semantics note
|
||||
* in detail/ValidationTracker.cpp.
|
||||
/**
|
||||
* Lifetime misses counted at first classification only.
|
||||
*
|
||||
* @note Unlike totalMissed(), this is strictly monotonic: it is
|
||||
* incremented only when a ledger is first reconciled as a miss and is
|
||||
* never decremented by a late repair. It backs the monotonic Prometheus
|
||||
* counter validation_missed_total. See the counting-semantics note
|
||||
* in detail/ValidationTracker.cpp.
|
||||
*/
|
||||
uint64_t
|
||||
totalMissedEver() const;
|
||||
|
||||
/** Total validations this node sent. */
|
||||
/**
|
||||
* Total validations this node sent.
|
||||
*/
|
||||
uint64_t
|
||||
totalValidationsSent() const;
|
||||
|
||||
/** Total network validations observed for comparison. */
|
||||
/**
|
||||
* Total network validations observed for comparison.
|
||||
*/
|
||||
uint64_t
|
||||
totalValidationsChecked() const;
|
||||
|
||||
@@ -242,70 +280,106 @@ private:
|
||||
bool agreed{false}; ///< Whether this was an agreement.
|
||||
};
|
||||
|
||||
/// Grace period before reconciling a ledger event.
|
||||
/**
|
||||
* Grace period before reconciling a ledger event.
|
||||
*/
|
||||
static constexpr auto kGracePeriod = std::chrono::seconds(8);
|
||||
|
||||
/// Window during which a missed event can be repaired.
|
||||
/**
|
||||
* Window during which a missed event can be repaired.
|
||||
*/
|
||||
static constexpr auto kLateRepairWindow = std::chrono::minutes(5);
|
||||
|
||||
/// Maximum number of pending (unreconciled + recently reconciled) events.
|
||||
/**
|
||||
* Maximum number of pending (unreconciled + recently reconciled) events.
|
||||
*/
|
||||
static constexpr std::size_t kMaxPendingEvents = 1000;
|
||||
|
||||
/// Duration of the short rolling window.
|
||||
/**
|
||||
* Duration of the short rolling window.
|
||||
*/
|
||||
static constexpr auto kWindow1h = std::chrono::hours(1);
|
||||
|
||||
/// Duration of the long rolling window.
|
||||
/**
|
||||
* Duration of the long rolling window.
|
||||
*/
|
||||
static constexpr auto kWindow24h = std::chrono::hours(24);
|
||||
|
||||
/// Duration of the extended rolling window (7 days).
|
||||
/**
|
||||
* Duration of the extended rolling window (7 days).
|
||||
*/
|
||||
static constexpr auto kWindow7d = std::chrono::hours(168);
|
||||
|
||||
/// Protects pending_, window1h_, window24h_, and window7d_.
|
||||
/**
|
||||
* Protects pending_, window1h_, window24h_, and window7d_.
|
||||
*/
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
/// Pending ledger events indexed by ledger hash.
|
||||
/**
|
||||
* Pending ledger events indexed by ledger hash.
|
||||
*/
|
||||
hash_map<uint256, LedgerEvent> pending_;
|
||||
|
||||
/// Sliding window of reconciled events (last 1 hour).
|
||||
/**
|
||||
* Sliding window of reconciled events (last 1 hour).
|
||||
*/
|
||||
std::deque<WindowEvent> window1h_;
|
||||
|
||||
/// Sliding window of reconciled events (last 24 hours).
|
||||
/**
|
||||
* Sliding window of reconciled events (last 24 hours).
|
||||
*/
|
||||
std::deque<WindowEvent> window24h_;
|
||||
|
||||
/// Sliding window of reconciled events (last 7 days).
|
||||
/**
|
||||
* Sliding window of reconciled events (last 7 days).
|
||||
*/
|
||||
std::deque<WindowEvent> window7d_;
|
||||
|
||||
/// Lifetime count of agreements (net: incremented on agree, also on
|
||||
/// repair). May be read via totalAgreements(); feeds the windowed gauge.
|
||||
/**
|
||||
* Lifetime count of agreements (net: incremented on agree, also on
|
||||
* repair). May be read via totalAgreements(); feeds the windowed gauge.
|
||||
*/
|
||||
std::atomic<uint64_t> totalAgreements_{0};
|
||||
|
||||
/// Lifetime count of misses (net: incremented on miss, decremented on
|
||||
/// repair). NON-monotonic. May be read via totalMissed().
|
||||
/**
|
||||
* Lifetime count of misses (net: incremented on miss, decremented on
|
||||
* repair). NON-monotonic. May be read via totalMissed().
|
||||
*/
|
||||
std::atomic<uint64_t> totalMissed_{0};
|
||||
|
||||
// Monotonic "gross" lifetime tallies for the Prometheus _total counters.
|
||||
//
|
||||
// Counting decision (initial-classification only): each reconciled ledger
|
||||
// is counted exactly once, at its first classification, into exactly one
|
||||
// of the two tallies below. A later late-repair (miss -> agreement) does
|
||||
// NOT move either tally. This keeps both strictly monotonic (a Prometheus
|
||||
// _total must never decrease) and additive:
|
||||
// totalAgreementsGross_ + totalMissedGross_ == ledgers reconciled.
|
||||
// The repaired/agreement view is still available from the windowed gauge
|
||||
// (validation_agreement) and the net totals above.
|
||||
/**
|
||||
* Monotonic "gross" lifetime tallies for the Prometheus _total counters.
|
||||
*
|
||||
* Counting decision (initial-classification only): each reconciled
|
||||
* ledger is counted exactly once, at its first classification, into
|
||||
* exactly one of the two tallies below. A later late-repair
|
||||
* (miss -> agreement) does NOT move either tally. This keeps both
|
||||
* strictly monotonic (a Prometheus _total must never decrease) and
|
||||
* additive: totalAgreementsGross_ + totalMissedGross_ == ledgers
|
||||
* reconciled. The repaired/agreement view is still available from the
|
||||
* windowed gauge (validation_agreement) and the net totals above.
|
||||
*/
|
||||
|
||||
/// Monotonic lifetime initial agreements; backs
|
||||
/// validation_agreements_total. Never adjusted on repair.
|
||||
/**
|
||||
* Monotonic lifetime initial agreements; backs
|
||||
* validation_agreements_total. Never adjusted on repair.
|
||||
*/
|
||||
std::atomic<uint64_t> totalAgreementsGross_{0};
|
||||
|
||||
/// Monotonic lifetime initial misses; backs validation_missed_total.
|
||||
/// Never decremented on repair.
|
||||
/**
|
||||
* Monotonic lifetime initial misses; backs validation_missed_total.
|
||||
* Never decremented on repair.
|
||||
*/
|
||||
std::atomic<uint64_t> totalMissedGross_{0};
|
||||
|
||||
/// Lifetime count of validations this node sent.
|
||||
/**
|
||||
* Lifetime count of validations this node sent.
|
||||
*/
|
||||
std::atomic<uint64_t> totalValidationsSent_{0};
|
||||
|
||||
/// Lifetime count of network validations observed.
|
||||
/**
|
||||
* Lifetime count of network validations observed.
|
||||
*/
|
||||
std::atomic<uint64_t> totalValidationsChecked_{0};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @file ValidationTracker.cpp
|
||||
Implementation of the ValidationTracker class.
|
||||
*/
|
||||
/**
|
||||
* @file ValidationTracker.cpp
|
||||
* Implementation of the ValidationTracker class.
|
||||
*/
|
||||
|
||||
#include <xrpld/telemetry/ValidationTracker.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user