Skip to main content

camel_component_api/
runtime_observability.rs

1//! `RuntimeObservability` — narrow trait exposing `metrics()` and `health()`
2//! to Endpoint consumers/producers. Defined per ADR-0012 §Signal-replacement-API
3//! and the Phase A closure spec
4//! (`docs/superpowers/specs/2026-06-05-adr-0012-closure-design.md`).
5//!
6//! **Why a separate trait:** `ComponentContext` carries `resolve_component`,
7//! `resolve_language`, `platform_service` — far more authority than an
8//! emitter needs. Passing `Arc<dyn ComponentContext>` would be a
9//! service-locator anti-pattern inviting future misuse. This trait exposes
10//! exactly the two observability surfaces an ADR-0012 emitter calls.
11
12use std::sync::Arc;
13
14use camel_api::MetricsCollector;
15
16use crate::{ComponentContext, HealthCheckRegistry};
17
18/// Narrow observability surface available to Endpoint consumers and producers.
19///
20/// Implemented blanket for every `T: ComponentContext`, so
21/// `CamelContext` (the spec's "DefaultRuntime") and any test ctx
22/// automatically satisfy this trait. Endpoints receive
23/// `Arc<dyn RuntimeObservability>` at `create_consumer` / `create_producer`
24/// time (per Phase A closure spec).
25///
26/// `Send + Sync` is mandatory — Endpoints spawn across tokio tasks.
27pub trait RuntimeObservability: Send + Sync {
28    /// Active metrics collector. Used for `increment_errors(route_id, label)`
29    /// per ADR-0012 categories (b′) and (e).
30    fn metrics(&self) -> Arc<dyn MetricsCollector>;
31
32    /// Active health-check registry. Used for
33    /// `force_unhealthy_for_route(route_id, name, reason)` per ADR-0012
34    /// category (g).
35    fn health(&self) -> Arc<dyn HealthCheckRegistry>;
36}
37
38// Blanket impl: every ComponentContext is automatically a RuntimeObservability.
39// This covers `CamelContext` (production), `NoOpComponentContext` (tests),
40// and any future impl.
41//
42// No `?Sized` bound: ComponentContext itself requires Sized (the trait has
43// methods returning `Arc<dyn …>` which need a concrete owner type). If a
44// future dynamically-sized ctx is needed, add a separate explicit impl.
45impl<T: ComponentContext> RuntimeObservability for T {
46    fn metrics(&self) -> Arc<dyn MetricsCollector> {
47        <Self as ComponentContext>::metrics(self)
48    }
49
50    fn health(&self) -> Arc<dyn HealthCheckRegistry> {
51        <Self as ComponentContext>::health(self)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn runtime_observability_is_send_sync() {
61        fn assert_send_sync<T: Send + Sync + ?Sized>() {}
62        assert_send_sync::<dyn RuntimeObservability>();
63    }
64}