sc_command/observability.rs
1//! Canonical event injection without linking a logging runtime.
2//!
3//! Applications may implement this open receiver port and own their sinks and
4//! lifecycle. This temporary 1.5.0 contract is removed after upstream capability
5//! proof: <https://github.com/randlee/sc-runtime/issues/97> (ADR-RUN-0017).
6
7use sc_observability_types::{LogEvent, ServiceName, v2};
8
9use std::sync::Arc;
10
11/// A trusted synchronous canonical event receiver.
12///
13/// Implementations must return promptly, perform no sink I/O or capacity wait,
14/// and never panic. Libraries treat rejection as best effort.
15pub trait CanonicalLog: Send + Sync {
16 /// Returns the receiver's service identity.
17 fn service_name(&self) -> &ServiceName;
18
19 /// Attempts immediate event admission.
20 ///
21 /// # Errors
22 /// Returns an event error when the receiver rejects the event.
23 fn emit(&self, event: LogEvent) -> Result<(), v2::EventError>;
24}
25
26/// Shared ownership of an object-safe canonical event receiver.
27pub type SharedCanonicalLog = Arc<dyn CanonicalLog>;
28
29/// A receiver that retains a service name and discards all events.
30#[derive(Clone, Debug)]
31pub struct NoopCanonicalLog {
32 service: ServiceName,
33}
34
35impl NoopCanonicalLog {
36 /// Creates a discarding receiver with the supplied validated identity.
37 #[must_use]
38 pub fn new(service_name: ServiceName) -> Self {
39 Self {
40 service: service_name,
41 }
42 }
43
44 /// Uses a valid static service name, falling back to `noop` for invalid names.
45 ///
46 /// The fixed fallback is validated by the exact types 1.5.0 dependency.
47 /// ADR-RUN-0017 permits only this literal invariant; caller input is never
48 /// unwrapped. Upstream capability cleanup removes this temporary exception.
49 #[must_use]
50 pub fn named_or_default(service: &'static str) -> Self {
51 let service = ServiceName::new(service).unwrap_or_else(|_| {
52 // ADR-RUN-0017 fixed-literal invariant, bounded by exact types =1.5.0.
53 // Never unwrap caller/environment data. Remove with upstream cleanup:
54 // https://github.com/randlee/sc-runtime/issues/97
55 ServiceName::new("noop")
56 .expect("fixed noop identity is valid in pinned sc-observability-types 1.5.0")
57 });
58 Self::new(service)
59 }
60}
61
62impl CanonicalLog for NoopCanonicalLog {
63 fn service_name(&self) -> &ServiceName {
64 &self.service
65 }
66
67 fn emit(&self, _event: LogEvent) -> Result<(), v2::EventError> {
68 Ok(())
69 }
70}
71
72#[cfg(feature = "test-util")]
73mod test_util;
74
75#[cfg(feature = "test-util")]
76pub use test_util::{RecordingCanonicalLog, RejectingCanonicalLog};
77
78#[cfg(test)]
79mod tests;