Self-metrics
Headrace exports its own telemetry over OTLP - the same protocol it processes - so you can watch a pipeline with the tools you already point at everything else. It is off by default.
Enabling
headrace --metrics otlp run pipeline.yaml # export over OTLP/gRPC
headrace --metrics otlp --otlp-endpoint http://collector:4317 run pipeline.yaml
headrace --metrics stdout run pipeline.yaml # print to stderr, for debuggingotlpexports to--otlp-endpoint, orOTEL_EXPORTER_OTLP_ENDPOINT, or the default.stdoutperiodically prints metrics to stderr. Convenience only: the dump interleaves with a stdout sink's data, so preferotlpfor clean output.
Instruments
Every metric is attributed by node (the node id) and kind (source, filter, window,
map, join, sink); records.dropped adds a reason. The two window instruments carry
node only.
| Metric | Type | Attributes | Meaning |
|---|---|---|---|
headrace.records.out | counter | node, kind | Records a node emitted or forwarded. |
headrace.records.dropped | counter | node, kind, reason | Records dropped, split by reason (below). |
headrace.window.flushes | counter | node | Window flush events. |
headrace.window.groups | histogram | node | Aggregate groups emitted per flush. |
headrace.node.errors | counter | node, kind | Node tasks that terminated with an error. |
headrace.wasm.memory.bytes | histogram | node, kind | A wasm module's linear-memory size, recorded when it changes. |
reason on headrace.records.dropped:
reason | Meaning |
|---|---|
filtered | A filter predicate rejected the record. |
invalid | A missing/non-numeric field or an inevaluable expression, under a skip policy. |
late | The record arrived after its window had already fired. |
incomplete | A join bucket was evicted before every input supplied a value. |
capped | The node was at its max_groups cap. |
Reading them
- A rising
records.dropped{reason=late}meansallowed_latenessis too small for the source's out-of-orderness - see Troubleshooting. records.dropped{reason=invalid}on awindowormapnode points at a missing or non-numeric field with anon_missing/on_invalidpolicy ofskip.headrace.window.groupsshows the cardinality of each flush - useful for spotting agroup_bythat fans out more than you expected.records.dropped{reason=capped}is nonzero only when a node hit itsmax_groupscap: thegroup_bycardinality outran the limit (a misconfigured key or an attack). Raise the cap or narrow thegroup_by.wasm.memory.bytesis the linear memory awasmmodule settled on; size itsmax_memoryabove the peak you see here, with headroom.