pub struct MetricRecorder { /* private fields */ }Expand description
Ergonomic plugin-side helper that wraps the FFI recorder.
Plugins embed a MetricRecorder in their state, the
define_plugin! macro wires it via the set_metric_recorder
vtable entry, and plugin code calls the typed counter / gauge /
histogram methods from anywhere with &self.
pub struct MyPlugin {
metrics: MetricRecorder,
// ...other state
}
impl MyPlugin {
fn process(&self, msg: &SpoeMessage) -> Result<ProcessingResult, _> {
self.metrics.counter("dispatches_total", &[("kind", "ok")], 1);
self.metrics.histogram("latency_seconds", &[], 0.012);
// ...
}
}When the recorder is not yet installed (between create and the
hub’s set_metric_recorder call) or the host is running a v1 hub
that never installs one, the methods are inexpensive no-ops — a
single uncontended read-lock acquire that observes None.
§Why RwLock<Option<…>> and not OnceLock<…>
Plugins are typically declared as pub(crate) static METRICS: MetricRecorder = MetricRecorder::new(); — a process-wide singleton
living in the .so’s BSS. The hub keeps the .so mapped across
plugin reloads via libloading::Library refcounting, so the static
survives every reload and accumulates state across plugin
instances. With an internal OnceLock, install() was silently
no-op on the second-and-later plugin loads: the NEW plugin’s hub
ctx pointer never replaced the OLD one. When the hub then dropped
the OLD plugin’s Box<MetricCtx> (after the OLD plugin’s last
in-flight process() call retired), the stale OLD pointer in
MetricRecorder became a use-after-free; the next counter() /
gauge() / histogram() from any plugin instance — old or new —
dereferenced freed memory and segfaulted the hub process. The
haptic conformance suite reproduces this on every conformance shard
run because its reconciliationDebounceInterval=10ms triggers
rapid back-to-back HAProxy general-storage writes, each of which
the hub’s file-watcher turns into a plugin reload.
RwLock lets install() actually REPLACE the inner on every call,
which is what plugin reload requires. Read paths (emit) take the
read lock for the duration of the recorder callback, holding the
pointer valid against a racing install() from a concurrent
reload. The hub’s lifetime contract — “ctx is valid until the
plugin instance that received it is destroyed” — combined with the
hub’s drop ordering (Plugin::drop calls destroy() BEFORE
dropping Box<MetricCtx>) means a NEW install always lands BEFORE
the corresponding OLD Box is freed, so once emit reads NEW
inner under its read lock, the ctx it observes is the NEW one and
stays valid for the call.
Implementations§
Source§impl MetricRecorder
impl MetricRecorder
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Construct an uninitialised recorder. Plugins embed this in their
state; the define_plugin! macro fills it in when the hub calls
set_metric_recorder.
Sourcepub fn counter(&self, name: &str, labels: &[(&str, &str)], increment: u64)
pub fn counter(&self, name: &str, labels: &[(&str, &str)], increment: u64)
Increment a counter. increment is typically 1. Names are
hub-namespaced — emitting "dispatches_total" from the mirror
plugin lands as plugin_mirror_dispatches_total in Prometheus.
Trait Implementations§
Source§impl Debug for MetricRecorder
impl Debug for MetricRecorder
Auto Trait Implementations§
impl !Freeze for MetricRecorder
impl RefUnwindSafe for MetricRecorder
impl Send for MetricRecorder
impl Sync for MetricRecorder
impl Unpin for MetricRecorder
impl UnsafeUnpin for MetricRecorder
impl UnwindSafe for MetricRecorder
Blanket Implementations§
Source§impl<T> AlignerFor<1> for T
impl<T> AlignerFor<1> for T
Source§impl<T> AlignerFor<2> for T
impl<T> AlignerFor<2> for T
Source§impl<T> AlignerFor<4> for T
impl<T> AlignerFor<4> for T
Source§impl<T> AlignerFor<8> for T
impl<T> AlignerFor<8> for T
Source§impl<T> AlignerFor<16> for T
impl<T> AlignerFor<16> for T
Source§impl<T> AlignerFor<32> for T
impl<T> AlignerFor<32> for T
Source§impl<T> AlignerFor<64> for T
impl<T> AlignerFor<64> for T
Source§impl<T> AlignerFor<128> for T
impl<T> AlignerFor<128> for T
Source§type Aligner = AlignTo128<T>
type Aligner = AlignTo128<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<256> for T
impl<T> AlignerFor<256> for T
Source§type Aligner = AlignTo256<T>
type Aligner = AlignTo256<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<512> for T
impl<T> AlignerFor<512> for T
Source§type Aligner = AlignTo512<T>
type Aligner = AlignTo512<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<1024> for T
impl<T> AlignerFor<1024> for T
Source§type Aligner = AlignTo1024<T>
type Aligner = AlignTo1024<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<2048> for T
impl<T> AlignerFor<2048> for T
Source§type Aligner = AlignTo2048<T>
type Aligner = AlignTo2048<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<4096> for T
impl<T> AlignerFor<4096> for T
Source§type Aligner = AlignTo4096<T>
type Aligner = AlignTo4096<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<8192> for T
impl<T> AlignerFor<8192> for T
Source§type Aligner = AlignTo8192<T>
type Aligner = AlignTo8192<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<16384> for T
impl<T> AlignerFor<16384> for T
Source§type Aligner = AlignTo16384<T>
type Aligner = AlignTo16384<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> AlignerFor<32768> for T
impl<T> AlignerFor<32768> for T
Source§type Aligner = AlignTo32768<T>
type Aligner = AlignTo32768<T>
AlignTo* type which aligns Self to ALIGNMENT.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<S> ROExtAcc for S
impl<S> ROExtAcc for S
Source§fn f_get<F>(&self, offset: FieldOffset<S, F, Aligned>) -> &F
fn f_get<F>(&self, offset: FieldOffset<S, F, Aligned>) -> &F
offset. Read moreSource§fn f_get_mut<F>(&mut self, offset: FieldOffset<S, F, Aligned>) -> &mut F
fn f_get_mut<F>(&mut self, offset: FieldOffset<S, F, Aligned>) -> &mut F
offset. Read moreSource§fn f_get_ptr<F, A>(&self, offset: FieldOffset<S, F, A>) -> *const F
fn f_get_ptr<F, A>(&self, offset: FieldOffset<S, F, A>) -> *const F
offset. Read moreSource§fn f_get_mut_ptr<F, A>(&mut self, offset: FieldOffset<S, F, A>) -> *mut F
fn f_get_mut_ptr<F, A>(&mut self, offset: FieldOffset<S, F, A>) -> *mut F
offset. Read moreSource§impl<S> ROExtOps<Aligned> for S
impl<S> ROExtOps<Aligned> for S
Source§fn f_replace<F>(&mut self, offset: FieldOffset<S, F, Aligned>, value: F) -> F
fn f_replace<F>(&mut self, offset: FieldOffset<S, F, Aligned>, value: F) -> F
offset) with value,
returning the previous value of the field. Read moreSource§fn f_get_copy<F>(&self, offset: FieldOffset<S, F, Aligned>) -> Fwhere
F: Copy,
fn f_get_copy<F>(&self, offset: FieldOffset<S, F, Aligned>) -> Fwhere
F: Copy,
Source§impl<S> ROExtOps<Unaligned> for S
impl<S> ROExtOps<Unaligned> for S
Source§fn f_replace<F>(&mut self, offset: FieldOffset<S, F, Unaligned>, value: F) -> F
fn f_replace<F>(&mut self, offset: FieldOffset<S, F, Unaligned>, value: F) -> F
offset) with value,
returning the previous value of the field. Read moreSource§fn f_get_copy<F>(&self, offset: FieldOffset<S, F, Unaligned>) -> Fwhere
F: Copy,
fn f_get_copy<F>(&self, offset: FieldOffset<S, F, Unaligned>) -> Fwhere
F: Copy,
Source§impl<T> SelfOps for Twhere
T: ?Sized,
impl<T> SelfOps for Twhere
T: ?Sized,
Source§fn piped<F, U>(self, f: F) -> U
fn piped<F, U>(self, f: F) -> U
Source§fn piped_ref<'a, F, U>(&'a self, f: F) -> Uwhere
F: FnOnce(&'a Self) -> U,
fn piped_ref<'a, F, U>(&'a self, f: F) -> Uwhere
F: FnOnce(&'a Self) -> U,
piped except that the function takes &Self
Useful for functions that take &Self instead of Self. Read moreSource§fn piped_mut<'a, F, U>(&'a mut self, f: F) -> Uwhere
F: FnOnce(&'a mut Self) -> U,
fn piped_mut<'a, F, U>(&'a mut self, f: F) -> Uwhere
F: FnOnce(&'a mut Self) -> U,
piped, except that the function takes &mut Self.
Useful for functions that take &mut Self instead of Self.Source§fn mutated<F>(self, f: F) -> Self
fn mutated<F>(self, f: F) -> Self
Source§fn observe<F>(self, f: F) -> Self
fn observe<F>(self, f: F) -> Self
Source§fn as_ref_<T>(&self) -> &T
fn as_ref_<T>(&self) -> &T
AsRef,
using the turbofish .as_ref_::<_>() syntax. Read more