Skip to main content

subms_otel/
composite.rs

1//! Fan-out observer: wire two or more [`SubMsObserver`]s up to a single
2//! harness without modifying the harness API.
3
4use std::sync::Arc;
5
6use subms::{ObservationCtx, SubMsBenchSummary, SubMsObserver};
7
8/// Holds a list of inner observers and forwards every event to each. Lets a
9/// consumer combine, e.g., an [`crate::OtelObserver`] with a custom
10/// Prometheus impl and a logging observer at once.
11///
12/// ```ignore
13/// use std::sync::Arc;
14/// use subms_otel::{CompositeObserver, OtelObserver};
15///
16/// let composite = CompositeObserver::new(vec![
17///     Arc::new(OtelObserver::new(meter)) as Arc<dyn subms::SubMsObserver>,
18///     Arc::new(my_prom_observer),
19/// ]);
20/// let h = SubMsPerfHarness::new("workload", "rust").with_observer(Arc::new(composite));
21/// ```
22pub struct CompositeObserver {
23    inner: Vec<Arc<dyn SubMsObserver>>,
24}
25
26impl CompositeObserver {
27    /// Build from a ready-made vec of observers.
28    pub fn new(inner: Vec<Arc<dyn SubMsObserver>>) -> Self {
29        Self { inner }
30    }
31
32    /// Append another observer to the fan-out list. Chainable.
33    pub fn with(mut self, observer: Arc<dyn SubMsObserver>) -> Self {
34        self.inner.push(observer);
35        self
36    }
37
38    /// Read the inner observer list. Mostly for tests / introspection.
39    pub fn observers(&self) -> &[Arc<dyn SubMsObserver>] {
40        &self.inner
41    }
42}
43
44impl SubMsObserver for CompositeObserver {
45    fn on_record(&self, ctx: &ObservationCtx, ns: u64) {
46        for obs in &self.inner {
47            obs.on_record(ctx, ns);
48        }
49    }
50
51    fn on_summarize(&self, summary: &SubMsBenchSummary) {
52        for obs in &self.inner {
53            obs.on_summarize(summary);
54        }
55    }
56}