1use crate::model::Event;
4
5#[derive(Debug, Default, Clone)]
6pub struct Metrics {
7 pub executions_started: u64,
8 pub executions_completed: u64,
9 pub executions_failed: u64,
10 pub executions_cancelled: u64,
11 pub executions_timed_out: u64,
12 pub tasks_dispatched: u64,
13 pub tasks_succeeded: u64,
14 pub task_attempts_failed: u64,
15 pub task_attempts_uncertain: u64,
16 pub retries_scheduled: u64,
17 pub steps_skipped: u64,
18 pub stale_results: u64,
19 pub compensations_started: u64,
20 pub compensations_failed: u64,
21 pub events_committed: u64,
22 pub executions_continued_as_new: u64,
23 pub schedules_fired: u64,
25 pub schedules_skipped_overlap: u64,
27 pub schedules_skipped_missed: u64,
29}
30
31impl Metrics {
32 pub fn observe(&mut self, e: &Event) {
33 self.events_committed += 1;
34 match e {
35 Event::WorkflowCreated { run: None, .. } => self.executions_started += 1,
37 Event::WorkflowContinuedAsNew { .. } => self.executions_continued_as_new += 1,
38 Event::WorkflowCompleted { .. } => self.executions_completed += 1,
39 Event::WorkflowFailed { .. } => self.executions_failed += 1,
40 Event::WorkflowCancelled { .. } => self.executions_cancelled += 1,
41 Event::WorkflowTimedOut { .. } => self.executions_timed_out += 1,
42 Event::StepDispatched { .. } => self.tasks_dispatched += 1,
43 Event::StepSucceeded { .. } => self.tasks_succeeded += 1,
44 Event::StepAttemptFailed { .. } => self.task_attempts_failed += 1,
45 Event::StepAttemptUncertain { .. } => self.task_attempts_uncertain += 1,
46 Event::RetryScheduled { .. } => self.retries_scheduled += 1,
47 Event::StepSkipped { .. } => self.steps_skipped += 1,
48 Event::StaleResultIgnored { .. } => self.stale_results += 1,
49 Event::CompensationStarted { .. } => self.compensations_started += 1,
50 Event::CompensationFailed { .. } => self.compensations_failed += 1,
51 _ => {}
52 }
53 }
54
55 pub fn render(&self, active: usize, sessions: usize) -> String {
56 let counters = [
57 (
58 "orbit_executions_started_total",
59 "Executions started",
60 self.executions_started,
61 ),
62 (
63 "orbit_executions_completed_total",
64 "Executions completed",
65 self.executions_completed,
66 ),
67 (
68 "orbit_executions_failed_total",
69 "Executions failed",
70 self.executions_failed,
71 ),
72 (
73 "orbit_executions_cancelled_total",
74 "Executions cancelled",
75 self.executions_cancelled,
76 ),
77 (
78 "orbit_executions_timed_out_total",
79 "Executions timed out",
80 self.executions_timed_out,
81 ),
82 (
83 "orbit_tasks_dispatched_total",
84 "Task attempts dispatched",
85 self.tasks_dispatched,
86 ),
87 (
88 "orbit_tasks_succeeded_total",
89 "Task attempts succeeded",
90 self.tasks_succeeded,
91 ),
92 (
93 "orbit_task_attempts_failed_total",
94 "Task attempts failed",
95 self.task_attempts_failed,
96 ),
97 (
98 "orbit_task_attempts_uncertain_total",
99 "Task attempts with unknown outcome",
100 self.task_attempts_uncertain,
101 ),
102 (
103 "orbit_retries_scheduled_total",
104 "Retries scheduled",
105 self.retries_scheduled,
106 ),
107 (
108 "orbit_steps_skipped_total",
109 "Steps skipped by conditions",
110 self.steps_skipped,
111 ),
112 (
113 "orbit_stale_results_total",
114 "Duplicate or late results ignored",
115 self.stale_results,
116 ),
117 (
118 "orbit_compensations_started_total",
119 "Compensations started",
120 self.compensations_started,
121 ),
122 (
123 "orbit_compensations_failed_total",
124 "Compensations failed",
125 self.compensations_failed,
126 ),
127 (
128 "orbit_events_committed_total",
129 "History events committed",
130 self.events_committed,
131 ),
132 (
133 "orbit_executions_continued_as_new_total",
134 "Runs ended by continue_as_new",
135 self.executions_continued_as_new,
136 ),
137 (
138 "orbit_schedules_fired_total",
139 "Schedule occurrences that started an execution",
140 self.schedules_fired,
141 ),
142 ];
143 let mut out = String::new();
144 for (name, help, value) in counters {
145 out.push_str(&format!(
146 "# HELP {name} {help}\n# TYPE {name} counter\n{name} {value}\n"
147 ));
148 }
149 out.push_str(&format!(
150 "# HELP orbit_schedules_skipped_total Schedule occurrences that started nothing\n# TYPE orbit_schedules_skipped_total counter\norbit_schedules_skipped_total{{reason=\"overlap\"}} {}\norbit_schedules_skipped_total{{reason=\"missed\"}} {}\n",
151 self.schedules_skipped_overlap, self.schedules_skipped_missed
152 ));
153 out.push_str(&format!(
154 "# HELP orbit_active_executions Executions not yet finished\n# TYPE orbit_active_executions gauge\norbit_active_executions {active}\n"
155 ));
156 out.push_str(&format!(
157 "# HELP orbit_worker_sessions Registered worker sessions\n# TYPE orbit_worker_sessions gauge\norbit_worker_sessions {sessions}\n"
158 ));
159 out
160 }
161}