stasis/application/telemetry/
operation.rs1use std::sync::Arc;
2use std::time::Instant;
3
4use crate::application::telemetry::keys::{
5 MEMORY_RECALL_DURATION_MS, MEMORY_RECALL_ERRORS_TOTAL, MEMORY_RECALL_TOTAL,
6 MEMORY_STORE_DURATION_MS, MEMORY_STORE_ERRORS_TOTAL, MEMORY_STORE_TOTAL,
7};
8use crate::application::telemetry::spans;
9use crate::ports::outbound::runtime::runtime_metrics::RuntimeMetrics;
10use crate::ports::outbound::runtime::runtime_tracing::{OtelAttribute, RuntimeTracing, SpanGuard};
11
12#[derive(Clone)]
14pub struct OperationTelemetry {
15 metrics: Arc<dyn RuntimeMetrics>,
16 tracing: Arc<dyn RuntimeTracing>,
17}
18
19impl OperationTelemetry {
20 pub fn new(metrics: Arc<dyn RuntimeMetrics>, tracing: Arc<dyn RuntimeTracing>) -> Self {
21 Self { metrics, tracing }
22 }
23
24 pub fn start_span(&self, name: &'static str, attributes: &[OtelAttribute]) -> SpanGuard {
25 self.tracing.start_span(name, attributes)
26 }
27
28 pub fn recall_span(&self, correlation_id: &str) -> SpanGuard {
29 self.start_span(
30 spans::MEMORY_RECALL,
31 &[OtelAttribute::string(
32 "stasis.memory.correlation_id",
33 correlation_id.to_string(),
34 )],
35 )
36 }
37
38 pub fn store_span(&self, correlation_id: &str) -> SpanGuard {
39 self.start_span(
40 spans::MEMORY_STORE,
41 &[OtelAttribute::string(
42 "stasis.memory.correlation_id",
43 correlation_id.to_string(),
44 )],
45 )
46 }
47
48 pub fn grapheme_span(&self, job_id: &str) -> SpanGuard {
49 self.start_span(
50 spans::GRAPHEME_EXECUTE,
51 &[OtelAttribute::string("stasis.job.id", job_id.to_string())],
52 )
53 }
54
55 pub fn outbox_publish_span(&self, event_type: &str, job_id: &str) -> SpanGuard {
56 self.start_span(
57 spans::OUTBOX_PUBLISH,
58 &[
59 OtelAttribute::string("stasis.outbox.event_type", event_type.to_string()),
60 OtelAttribute::string("stasis.outbox.job_id", job_id.to_string()),
61 ],
62 )
63 }
64
65 pub fn record_recall_started(&self) {
66 self.metrics.incr_counter(MEMORY_RECALL_TOTAL, 1);
67 }
68
69 pub fn record_recall_success(&self, started: Instant) {
70 self.metrics.observe_duration_ms(
71 MEMORY_RECALL_DURATION_MS,
72 started.elapsed().as_millis() as u64,
73 );
74 }
75
76 pub fn record_recall_error(&self, started: Instant) {
77 self.metrics.incr_counter(MEMORY_RECALL_ERRORS_TOTAL, 1);
78 self.metrics.observe_duration_ms(
79 MEMORY_RECALL_DURATION_MS,
80 started.elapsed().as_millis() as u64,
81 );
82 }
83
84 pub fn record_store_started(&self) {
85 self.metrics.incr_counter(MEMORY_STORE_TOTAL, 1);
86 }
87
88 pub fn record_store_success(&self, started: Instant) {
89 self.metrics.observe_duration_ms(
90 MEMORY_STORE_DURATION_MS,
91 started.elapsed().as_millis() as u64,
92 );
93 }
94
95 pub fn record_store_error(&self, started: Instant) {
96 self.metrics.incr_counter(MEMORY_STORE_ERRORS_TOTAL, 1);
97 self.metrics.observe_duration_ms(
98 MEMORY_STORE_DURATION_MS,
99 started.elapsed().as_millis() as u64,
100 );
101 }
102}
103
104pub fn runtime_event_type_name(event_type: &crate::domain::runtime::outbox::RuntimeEventType) -> &'static str {
105 match event_type {
106 crate::domain::runtime::outbox::RuntimeEventType::JobSucceeded => "job.succeeded",
107 crate::domain::runtime::outbox::RuntimeEventType::JobRetryScheduled => "job.retry_scheduled",
108 crate::domain::runtime::outbox::RuntimeEventType::JobDeadLettered => "job.dead_lettered",
109 }
110}