bothan_lib/metrics/websocket.rs
1//! Metrics collection for websocket operations.
2//!
3//! This module provides the [`Metrics`] struct and related types for tracking websocket connection and message statistics
4//! such as the number of activity messages, connection attempts, and their durations. It leverages OpenTelemetry for metrics
5//! instrumentation, supporting monitoring and observability.
6
7use opentelemetry::metrics::{Counter, Histogram};
8use opentelemetry::{KeyValue, global};
9use strum_macros::Display;
10
11/// Holds counters and histograms for websocket metrics.
12#[derive(Clone, Debug)]
13pub struct Metrics {
14 /// The worker identifier for labeling metrics.
15 worker: String,
16 /// Counter tracking total activity messages sent.
17 activity_messages_total: Counter<u64>,
18 /// Histogram recording connection durations in milliseconds.
19 connection_duration: Histogram<u64>,
20 /// Counter tracking total websocket connections established.
21 connections_total: Counter<u64>,
22}
23
24impl Metrics {
25 /// Creates a new [`Metrics`] instance configured for a specified source and worker.
26 ///
27 /// # Arguments
28 ///
29 /// * `source` - A static string identifying the source whose metrics are being recorded.
30 /// * `worker` - The worker identifier for labeling metrics.
31 ///
32 /// # Examples
33 ///
34 /// ```rust
35 /// use bothan_lib::metrics::websocket::{Metrics, MessageType, ConnectionResult};
36 ///
37 /// let metrics = Metrics::new("example_source", "worker-1".to_string());
38 /// metrics.increment_activity_messages_total(MessageType::Ping);
39 /// metrics.update_websocket_connection(200, ConnectionResult::Success);
40 /// ```
41 pub fn new(source: &'static str, worker: String) -> Self {
42 let meter = global::meter(source);
43
44 let activity_messages_total = meter
45 .u64_counter("websocket_activity_messages")
46 .with_description("Total number of messages sent by the source to indicate whether the source is active or not")
47 .build();
48 let connection_duration = meter
49 .u64_histogram("websocket_connection_duration_milliseconds")
50 .with_description(
51 "Time taken for worker to establish a websocket connection to the source",
52 )
53 .with_unit("milliseconds")
54 .build();
55 let connections_total = meter
56 .u64_counter("websocket_connection")
57 .with_description("Total number of connections established by a worker to the source")
58 .build();
59
60 Self {
61 worker,
62 activity_messages_total,
63 connection_duration,
64 connections_total,
65 }
66 }
67
68 /// Increments the activity message counter for a given message type.
69 ///
70 /// # Arguments
71 ///
72 /// * `message` - The type of activity message sent.
73 pub fn increment_activity_messages_total(&self, message: MessageType) {
74 self.activity_messages_total.add(
75 1,
76 &[
77 KeyValue::new("worker", self.worker.clone()),
78 KeyValue::new("message_type", message.to_string()),
79 ],
80 );
81 }
82
83 /// Records a websocket connection attempt and its duration.
84 ///
85 /// # Arguments
86 ///
87 /// * `elapsed_time` - Duration of the connection attempt in milliseconds.
88 /// * `status` - The result of the connection attempt.
89 pub fn update_websocket_connection(&self, elapsed_time: u128, status: ConnectionResult) {
90 let labels = &[
91 KeyValue::new("worker", self.worker.clone()),
92 KeyValue::new("status", status.to_string()),
93 ];
94 self.connections_total.add(1, labels);
95 // `elapsed_time` is u128, but it will never exceed u64::MAX in practice
96 self.connection_duration.record(elapsed_time as u64, labels);
97 }
98}
99
100/// Possible types of websocket activity messages.
101#[derive(Display)]
102#[strum(serialize_all = "snake_case")]
103pub enum MessageType {
104 /// Asset information message.
105 AssetInfo,
106 /// Ping message.
107 Ping,
108 /// Unused message type.
109 Unused,
110 /// Error message.
111 Error,
112}
113
114/// Possible results for a websocket connection attempt.
115#[derive(Display)]
116#[strum(serialize_all = "snake_case")]
117pub enum ConnectionResult {
118 /// The connection was successful.
119 Success,
120 /// The connection failed.
121 Failed,
122}