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}