pub struct Observer { /* private fields */ }Expand description
Process-level structured logger and trace correlator.
Cloning this value is cheap. Emission uses try_send; output and JSON
serialization happen on the dedicated writer thread.
Implementations§
Source§impl Observer
impl Observer
Sourcepub fn record_admission_capacity(
&self,
output: Option<&EmergencyDiagnosticHandle>,
context: AdmissionEventContext<'_>,
facts: &AdmissionCapacityFacts,
construction: AdmissionConstruction,
) -> AdmissionCapacitySubmission
pub fn record_admission_capacity( &self, output: Option<&EmergencyDiagnosticHandle>, context: AdmissionEventContext<'_>, facts: &AdmissionCapacityFacts, construction: AdmissionConstruction, ) -> AdmissionCapacitySubmission
Emit original four-dimensional facts without synthetic Call/Event/root. Metrics use the existing fixed dimensions even when output is absent.
Source§impl Observer
impl Observer
pub fn record_execution_pressure( &self, output: Option<&EmergencyDiagnosticHandle>, facts: ExecutionPressureFacts, ) -> DiagnosticSubmission
Source§impl Observer
impl Observer
Sourcepub fn record_standard_rpc_shutdown(
&self,
output: Option<&EmergencyDiagnosticHandle>,
joined: bool,
rpc: &RpcStageSnapshot,
) -> DiagnosticSubmission
pub fn record_standard_rpc_shutdown( &self, output: Option<&EmergencyDiagnosticHandle>, joined: bool, rpc: &RpcStageSnapshot, ) -> DiagnosticSubmission
Original same-lock ledger sample after application driver join and physical pool reclaim. Values are not manufactured from join success.
Source§impl Observer
impl Observer
pub fn record_ingress_fifo( &self, output: Option<&EmergencyDiagnosticHandle>, request_id: Option<ContextIdentity>, queue_sequence: u64, transition: &'static str, reason: &'static str, ) -> DiagnosticSubmission
Source§impl Observer
impl Observer
Sourcepub fn record_ingress_fifo_attempt(
&self,
output: Option<&EmergencyDiagnosticHandle>,
request_id: Option<ContextIdentity>,
queue_sequence: u64,
decision_attempt: u64,
input_peak: usize,
outcome: &'static str,
) -> DiagnosticSubmission
pub fn record_ingress_fifo_attempt( &self, output: Option<&EmergencyDiagnosticHandle>, request_id: Option<ContextIdentity>, queue_sequence: u64, decision_attempt: u64, input_peak: usize, outcome: &'static str, ) -> DiagnosticSubmission
Attempt identity only. Resource amounts remain on the original same-lock capacity receipt; input_peak is the already recognized input demand.
Source§impl Observer
impl Observer
Sourcepub fn record_resource_quiescent(
&self,
output: Option<&EmergencyDiagnosticHandle>,
context: Option<RequestContextSnapshot>,
completion_sequence: u64,
resource: &ProcessSnapshot,
rpc: &RpcStageSnapshot,
) -> DiagnosticSubmission
pub fn record_resource_quiescent( &self, output: Option<&EmergencyDiagnosticHandle>, context: Option<RequestContextSnapshot>, completion_sequence: u64, resource: &ProcessSnapshot, rpc: &RpcStageSnapshot, ) -> DiagnosticSubmission
A post-join sample, not an admission decision or an atomic combined ledger receipt.
Source§impl Observer
impl Observer
Sourcepub fn start_external_call(
&self,
application: impl Into<ApplicationId>,
module: impl Into<ModuleId>,
service: impl Into<ServiceId>,
operation: impl Into<OperationId>,
inbound_trace_id: Option<&str>,
) -> (ActiveCall, InboundTrace)
pub fn start_external_call( &self, application: impl Into<ApplicationId>, module: impl Into<ModuleId>, service: impl Into<ServiceId>, operation: impl Into<OperationId>, inbound_trace_id: Option<&str>, ) -> (ActiveCall, InboundTrace)
Starts an external request using the legacy compatibility policy.
Invalid supplied identifiers are replaced for compatibility with 0.3.0
callers. Production entry points must use
Observer::start_external_call_checked so only a missing identifier
can cause generation.
Sourcepub fn start_external_call_checked(
&self,
application: impl Into<ApplicationId>,
module: impl Into<ModuleId>,
service: impl Into<ServiceId>,
operation: impl Into<OperationId>,
inbound_trace_id: Option<&str>,
) -> Result<(ActiveCall, InboundTrace), TraceIdError>
pub fn start_external_call_checked( &self, application: impl Into<ApplicationId>, module: impl Into<ModuleId>, service: impl Into<ServiceId>, operation: impl Into<OperationId>, inbound_trace_id: Option<&str>, ) -> Result<(ActiveCall, InboundTrace), TraceIdError>
Starts a production entry call. A supplied opaque protocol identifier is inherited byte-for-byte after bounded validation; only an absent identifier may cause generation.
Sourcepub fn start_external_call_with_rpc(
&self,
application: impl Into<ApplicationId>,
module: impl Into<ModuleId>,
service: impl Into<ServiceId>,
operation: impl Into<OperationId>,
inbound_trace_id: Option<&str>,
rpc: RpcCorrelationId,
) -> Result<(ActiveCall, InboundTrace), TraceIdError>
pub fn start_external_call_with_rpc( &self, application: impl Into<ApplicationId>, module: impl Into<ModuleId>, service: impl Into<ServiceId>, operation: impl Into<OperationId>, inbound_trace_id: Option<&str>, rpc: RpcCorrelationId, ) -> Result<(ActiveCall, InboundTrace), TraceIdError>
Entry supplied protocol RPC, already validated by its typed carrier. No child RPC is created for local stages or database transactions.
Examples found in repository?
12fn main() {
13 let observer = Observer::with_writer(ObserverConfig::default(), std::io::sink()).unwrap();
14 let (incoming, _) = observer
15 .start_external_call_with_rpc(
16 "shop",
17 "ingress",
18 "orders",
19 "query",
20 Some("gateway-opaque"),
21 RpcCorrelationId::new("0.3").unwrap(),
22 )
23 .unwrap();
24 let ingress_event = EventContext::new(
25 RequestIdentity::new("request-7").unwrap(),
26 RouteIdentity::new("/incoming").unwrap(),
27 1,
28 )
29 .unwrap();
30 let parent = RequestDiagnosticScope::output_unavailable(incoming.context(), &ingress_event)
31 .with_task(DiagnosticTaskId::from_runtime_id("42").unwrap())
32 .unwrap_or_else(|_| panic!("task"))
33 .with_zone(DiagnosticZone::from_validated_ingress("zone-a").unwrap());
34
35 // programming.rs generates the wire child; Boundary uses its trace/rpc plus
36 // request_id/function. The adapter below does NOT generate these identities.
37 let wire_trace = "gateway-opaque";
38 let wire_rpc = "0.3.1";
39 let request_id = "request-7";
40 let function = "remote.query";
41 let (root, _) = observer
42 .start_external_call_with_rpc(
43 "saddle",
44 "zone-a",
45 "profusecontract",
46 "invoke",
47 Some(wire_trace),
48 RpcCorrelationId::new(wire_rpc).unwrap(),
49 )
50 .unwrap();
51 let event = EventContext::new(
52 RequestIdentity::new(request_id).unwrap(),
53 RouteIdentity::new(function).unwrap(),
54 1,
55 )
56 .unwrap();
57 let child = parent
58 .derive_outbound_child(root.context(), &event)
59 .unwrap();
60 let failure = child.fail(
61 std::io::ErrorKind::ConnectionRefused,
62 DiagnosticCategory::UnexpectedError,
63 BoundedDiagnosticCause::new(
64 DiagnosticStage::RequestDecode,
65 DiagnosticCode::new("outbound.connect_failed").unwrap(),
66 ),
67 );
68 let id = failure.source_diagnostic().id();
69 let (retained, delivery) =
70 failure.finish_boundary_retained(None, &DiagnosticOutcomeAxes::default());
71 assert_eq!(retained.source_diagnostic().id(), id);
72 assert_eq!(*retained.error(), std::io::ErrorKind::ConnectionRefused);
73 assert_eq!(
74 delivery.source_submission(),
75 DiagnosticSubmission::OutputUnavailable
76 );
77 assert_eq!(
78 delivery.boundary_submission(),
79 DiagnosticSubmission::OutputUnavailable
80 );
81 println!("GO: Boundary-shaped child Call/Event consumed; absent output retains same failure");
82}Sourcepub fn start_child_call(
&self,
parent: &CallContext,
kind: CallKind,
module: impl Into<ModuleId>,
service: impl Into<ServiceId>,
operation: impl Into<OperationId>,
) -> ActiveCall
pub fn start_child_call( &self, parent: &CallContext, kind: CallKind, module: impl Into<ModuleId>, service: impl Into<ServiceId>, operation: impl Into<OperationId>, ) -> ActiveCall
Starts a Service, DB or transaction child call on the parent’s trace.
Sourcepub fn record_domain_event(&self, context: &CallContext, event: DomainEvent)
pub fn record_domain_event(&self, context: &CallContext, event: DomainEvent)
Records an intentional domain event without accepting arbitrary objects.
Source§impl Observer
impl Observer
Sourcepub fn record_transaction_terminal(
observation: TransactionTerminalObservation,
outcome: TransactionOutcome,
)
pub fn record_transaction_terminal( observation: TransactionTerminalObservation, outcome: TransactionOutcome, )
DB owning driver calls once before returning its result/physical receipt. Uses the carrier’s observer/context, with no caller-supplied scope or sink. Queue loss preserves the DB result; a missing log remains NOT_PROVEN.
use saddle_observability::{Observer, TransactionOutcome, TransactionTerminalObservation};
fn replay(observation: TransactionTerminalObservation) {
Observer::record_transaction_terminal(observation, TransactionOutcome::Committed);
Observer::record_transaction_terminal(observation, TransactionOutcome::Rejected);
}pub fn record_lifecycle_timeout( &self, application: &str, stage: LifecycleTimeoutStage, elapsed_ms: u64, )
pub fn start_stage( &self, parent: &CallContext, stage: Stage, event_context: EventContext, ) -> ActiveStage
pub fn record_capacity( &self, context: &CallContext, event_context: &EventContext, value: CapacityObservation, )
Sourcepub fn observe_database_disposition(&self, disposition: DatabaseDisposition)
pub fn observe_database_disposition(&self, disposition: DatabaseDisposition)
Update the existing fixed-label counter without allocating or emitting a second log. The DB owning terminal path calls this exactly once with its actual physical disposition, independently of operation outcome.
pub fn record_database_disposition( &self, context: &CallContext, event_context: &EventContext, disposition: DatabaseDisposition, )
pub fn record_resource_finalization( &self, context: &CallContext, event_context: &EventContext, disposition: DatabaseDisposition, elapsed_ms: u64, )
pub fn record_outbound( &self, context: &CallContext, event_context: &EventContext, observation: OutboundObservation, )
pub fn record_lifecycle( &self, context: &CallContext, event_context: &EventContext, state: LifecycleState, health: Health, )
pub fn record_logger_health( &self, context: &CallContext, event_context: &EventContext, dropped: u64, failure: Option<OutputStage>, )
Source§impl Observer
impl Observer
Sourcepub fn record_diagnostic(
&self,
diagnostic: &Diagnostic,
emergency: &EmergencyDiagnosticHandle,
context: Option<(&CallContext, &EventContext)>,
) -> DiagnosticSubmission
pub fn record_diagnostic( &self, diagnostic: &Diagnostic, emergency: &EmergencyDiagnosticHandle, context: Option<(&CallContext, &EventContext)>, ) -> DiagnosticSubmission
Mirrors one safe diagnostic into independent emergency output and the existing logger. The result acknowledges only emergency queue admission.
Source§impl Observer
impl Observer
Sourcepub fn with_writer(
config: ObserverConfig,
writer: impl Write + Send + 'static,
) -> Result<Self, InitError>
pub fn with_writer( config: ObserverConfig, writer: impl Write + Send + 'static, ) -> Result<Self, InitError>
Creates an observer with a framework-owned writer.
Application startup should normally use init. This constructor is
useful for embedding and deterministic tests.
Examples found in repository?
55fn main() {
56 let process = example_profile();
57 let observer = Observer::with_writer(ObserverConfig::default(), std::io::sink()).unwrap();
58 let app = ContextLabel::checked("app").unwrap();
59 let (permit, original) = match process.verified_profile().try_admit_observed() {
60 ProfuseGwLightweightObservedAdmissionOutcome::Ready(permit, original) => (permit, original),
61 _ => panic!("example first admission must succeed"),
62 };
63 let submission = consume_original(
64 original,
65 &observer,
66 None,
67 AdmissionEventContext::Unrooted {
68 application: &app,
69 lifecycle: RequestViewPhase::Admitted,
70 },
71 AdmissionConstruction::Failed(AdmissionConstructionFailure::Storage),
72 );
73 assert_eq!(submission.cpu, DiagnosticSubmission::OutputUnavailable);
74 assert_eq!(submission.memory, DiagnosticSubmission::OutputUnavailable);
75 assert_eq!(submission.database, DiagnosticSubmission::OutputUnavailable);
76 assert_eq!(
77 submission.profuse_contract,
78 DiagnosticSubmission::OutputUnavailable
79 );
80 // The output did not release or remake admission: original permit remains.
81 permit.into_execution().cancel();
82 process.finish().unwrap();
83 for (name, layout) in ["facts", "borrowed_context", "record", "submission"]
84 .into_iter()
85 .zip(admission_capacity_layouts())
86 {
87 println!(
88 "AE layout {name}: bytes={} align={}",
89 layout.size(),
90 layout.align()
91 );
92 }
93 let existing = root_diagnostic::request_logging_layouts();
94 println!(
95 "AE existing frame={} packet={} slots={} confirmation_owner={}",
96 existing.source_frame.size(),
97 existing.emergency_packet.size(),
98 existing.emergency_slots,
99 existing.confirmation_owner.size()
100 );
101 println!("AE consumer example GO; AF/Service production wiring NOT_RUN");
102}More examples
12fn main() {
13 let observer = Observer::with_writer(ObserverConfig::default(), std::io::sink()).unwrap();
14 let (incoming, _) = observer
15 .start_external_call_with_rpc(
16 "shop",
17 "ingress",
18 "orders",
19 "query",
20 Some("gateway-opaque"),
21 RpcCorrelationId::new("0.3").unwrap(),
22 )
23 .unwrap();
24 let ingress_event = EventContext::new(
25 RequestIdentity::new("request-7").unwrap(),
26 RouteIdentity::new("/incoming").unwrap(),
27 1,
28 )
29 .unwrap();
30 let parent = RequestDiagnosticScope::output_unavailable(incoming.context(), &ingress_event)
31 .with_task(DiagnosticTaskId::from_runtime_id("42").unwrap())
32 .unwrap_or_else(|_| panic!("task"))
33 .with_zone(DiagnosticZone::from_validated_ingress("zone-a").unwrap());
34
35 // programming.rs generates the wire child; Boundary uses its trace/rpc plus
36 // request_id/function. The adapter below does NOT generate these identities.
37 let wire_trace = "gateway-opaque";
38 let wire_rpc = "0.3.1";
39 let request_id = "request-7";
40 let function = "remote.query";
41 let (root, _) = observer
42 .start_external_call_with_rpc(
43 "saddle",
44 "zone-a",
45 "profusecontract",
46 "invoke",
47 Some(wire_trace),
48 RpcCorrelationId::new(wire_rpc).unwrap(),
49 )
50 .unwrap();
51 let event = EventContext::new(
52 RequestIdentity::new(request_id).unwrap(),
53 RouteIdentity::new(function).unwrap(),
54 1,
55 )
56 .unwrap();
57 let child = parent
58 .derive_outbound_child(root.context(), &event)
59 .unwrap();
60 let failure = child.fail(
61 std::io::ErrorKind::ConnectionRefused,
62 DiagnosticCategory::UnexpectedError,
63 BoundedDiagnosticCause::new(
64 DiagnosticStage::RequestDecode,
65 DiagnosticCode::new("outbound.connect_failed").unwrap(),
66 ),
67 );
68 let id = failure.source_diagnostic().id();
69 let (retained, delivery) =
70 failure.finish_boundary_retained(None, &DiagnosticOutcomeAxes::default());
71 assert_eq!(retained.source_diagnostic().id(), id);
72 assert_eq!(*retained.error(), std::io::ErrorKind::ConnectionRefused);
73 assert_eq!(
74 delivery.source_submission(),
75 DiagnosticSubmission::OutputUnavailable
76 );
77 assert_eq!(
78 delivery.boundary_submission(),
79 DiagnosticSubmission::OutputUnavailable
80 );
81 println!("GO: Boundary-shaped child Call/Event consumed; absent output retains same failure");
82}Sourcepub fn metrics_snapshot(&self) -> MetricsSnapshot
pub fn metrics_snapshot(&self) -> MetricsSnapshot
Returns a fixed-size, allocation-free copy of the current process metrics.
Sourcepub fn flush(&self) -> FlushFuture ⓘ
pub fn flush(&self) -> FlushFuture ⓘ
Flushes records accepted before the lifecycle coordinator reaches the writer. The returned future never blocks the async executor thread.
Sourcepub fn shutdown(&self) -> FlushFuture ⓘ
pub fn shutdown(&self) -> FlushFuture ⓘ
Stops admission, drains accepted records, flushes output and joins the writer without blocking the async executor thread.
pub fn dropped_events(&self) -> u64
Trait Implementations§
Source§impl ComponentLifecycle for Observer
impl ComponentLifecycle for Observer
fn name(&self) -> &'static str
fn start(&self) -> LifecycleFuture<'_>
fn shutdown(&self) -> LifecycleFuture<'_>
Source§fn shutdown_with_primary(
&self,
_primary: Option<DiagnosticOccurrence>,
) -> Pin<Box<dyn Future<Output = Result<(), SaddleError>> + Send + '_>>
fn shutdown_with_primary( &self, _primary: Option<DiagnosticOccurrence>, ) -> Pin<Box<dyn Future<Output = Result<(), SaddleError>> + Send + '_>>
Source§impl RecordedComponentLifecycle for Observer
The formal component boundary keeps the actual FlushError until the
controlled source file has acknowledged it. The Core lifecycle impl above
remains a legacy compatibility surface for callers migrating to this one.
impl RecordedComponentLifecycle for Observer
The formal component boundary keeps the actual FlushError until the controlled source file has acknowledged it. The Core lifecycle impl above remains a legacy compatibility surface for callers migrating to this one.