ferron_common/
observability.rs

1use std::collections::HashSet;
2use std::error::Error;
3use std::sync::Arc;
4
5use async_channel::{Receiver, Sender};
6
7use crate::config::ServerConfiguration;
8use crate::logging::LogMessage;
9
10/// A trait that defines an observability backend loader
11pub trait ObservabilityBackendLoader {
12  /// Loads an observability backend according to specific configuration
13  fn load_observability_backend(
14    &mut self,
15    config: &ServerConfiguration,
16    global_config: Option<&ServerConfiguration>,
17    secondary_runtime: &tokio::runtime::Runtime,
18  ) -> Result<Arc<dyn ObservabilityBackend + Send + Sync>, Box<dyn Error + Send + Sync>>;
19
20  /// Determines configuration properties required to load an observability backend
21  fn get_requirements(&self) -> Vec<&'static str> {
22    vec![]
23  }
24
25  /// Validates the server configuration
26  #[allow(unused_variables)]
27  fn validate_configuration(
28    &self,
29    config: &ServerConfiguration,
30    used_properties: &mut HashSet<String>,
31  ) -> Result<(), Box<dyn Error + Send + Sync>> {
32    Ok(())
33  }
34}
35
36/// A trait that defines an observability backend
37pub trait ObservabilityBackend {
38  /// Obtains the channel for logging
39  fn get_log_channel(&self) -> Option<Sender<LogMessage>> {
40    None
41  }
42
43  /// Obtains the channel for metrics
44  fn get_metric_channel(&self) -> Option<Sender<Metric>> {
45    None
46  }
47
48  /// Obtains the channel for traces
49  fn get_trace_channel(&self) -> Option<(Sender<()>, Receiver<Sender<TraceSignal>>)> {
50    None
51  }
52}
53
54/// Observability backend channels inside configurations
55#[derive(Clone)]
56pub struct ObservabilityBackendChannels {
57  /// Log channels
58  pub log_channels: Vec<Sender<LogMessage>>,
59  /// Metric channels
60  pub metric_channels: Vec<Sender<Metric>>,
61  /// Trace channels
62  pub trace_channels: Vec<(Sender<()>, Receiver<Sender<TraceSignal>>)>,
63}
64
65impl Default for ObservabilityBackendChannels {
66  fn default() -> Self {
67    Self::new()
68  }
69}
70
71impl ObservabilityBackendChannels {
72  /// Creates an empty instance of `ObservabilityBackendChannels`
73  pub fn new() -> Self {
74    Self {
75      log_channels: Vec::new(),
76      metric_channels: Vec::new(),
77      trace_channels: Vec::new(),
78    }
79  }
80
81  /// Adds a log channel to the observability backend channels
82  pub fn add_log_channel(&mut self, channel: Sender<LogMessage>) {
83    self.log_channels.push(channel);
84  }
85
86  /// Adds a metric channel to the observability backend channels
87  pub fn add_metric_channel(&mut self, channel: Sender<Metric>) {
88    self.metric_channels.push(channel);
89  }
90
91  /// Adds a trace channel to the observability backend channels
92  pub fn add_trace_channel(&mut self, channel: (Sender<()>, Receiver<Sender<TraceSignal>>)) {
93    self.trace_channels.push(channel);
94  }
95}
96
97/// Represents a metric with its name, attributes, and value.
98#[derive(Clone)]
99pub struct Metric {
100  /// Name of the metric
101  pub name: &'static str,
102  /// Attributes of the metric
103  pub attributes: Vec<(&'static str, MetricAttributeValue)>,
104  /// Type of the metric
105  pub ty: MetricType,
106  /// Value of the metric
107  pub value: MetricValue,
108  /// Optional unit of the metric
109  pub unit: Option<&'static str>,
110  /// Optional description of the metric
111  pub description: Option<&'static str>,
112}
113
114impl Metric {
115  /// Creates a new instance of `Metric`
116  pub fn new(
117    name: &'static str,
118    attributes: Vec<(&'static str, MetricAttributeValue)>,
119    ty: MetricType,
120    value: MetricValue,
121    unit: Option<&'static str>,
122    description: Option<&'static str>,
123  ) -> Self {
124    Self {
125      name,
126      attributes,
127      ty,
128      value,
129      unit,
130      description,
131    }
132  }
133}
134
135/// Represents a type of metric.
136#[derive(Clone, Debug, PartialEq)]
137pub enum MetricType {
138  /// Increasing counter
139  Counter,
140
141  /// Gauge
142  Gauge,
143
144  /// Increasing or decreasing counter
145  UpDownCounter,
146
147  /// Histogram with optional buckets
148  Histogram(Option<Vec<f64>>),
149}
150
151/// Represents a value for a metric.
152#[derive(Clone, Copy, Debug, PartialEq)]
153#[non_exhaustive]
154pub enum MetricValue {
155  F64(f64),
156  U64(u64),
157  I64(i64),
158}
159
160/// Represents an attribute value for a metric.
161#[derive(Clone, Debug, PartialEq)]
162pub enum MetricAttributeValue {
163  /// String value
164  String(String),
165
166  /// Boolean value
167  Bool(bool),
168
169  /// Integer value
170  I64(i64),
171
172  /// Floating-point value
173  F64(f64),
174}
175
176/// Facilitates logging of error messages through a provided sender.
177pub struct MetricsMultiSender {
178  senders: Vec<Sender<Metric>>,
179}
180
181impl MetricsMultiSender {
182  /// Creates a new `MetricsMultiSender` instance.
183  ///
184  /// # Parameters
185  ///
186  /// - `sender`: A `Sender<Metric>` used for sending metric data.
187  ///
188  /// # Returns
189  ///
190  /// A new `MetricsMultiSender` instance associated with the provided sender.
191  pub fn new(sender: Sender<Metric>) -> Self {
192    Self { senders: vec![sender] }
193  }
194
195  /// Creates a new `MetricsMultiSender` instance with multiple senders.
196  ///
197  /// # Parameters
198  ///
199  /// - `senders`: A vector of `Sender<Metric>` used for sending metric data.
200  ///
201  /// # Returns
202  ///
203  /// A new `MetricsMultiSender` instance associated with multiple provided senders.
204  pub fn new_multiple(senders: Vec<Sender<Metric>>) -> Self {
205    Self { senders }
206  }
207
208  /// Creates a new `MetricsMultiSender` instance without any underlying sender.
209  ///
210  /// # Returns
211  ///
212  /// A new `MetricsMultiSender` instance not associated with any sender.
213  pub fn without_sender() -> Self {
214    Self { senders: vec![] }
215  }
216
217  /// Sends metric data asynchronously.
218  ///
219  /// # Parameters
220  ///
221  /// - `metric_data`: A `Metric` containing the metric data to be sent.
222  ///
223  pub async fn send(&self, metric_data: Metric) {
224    for sender in &self.senders {
225      sender.send(metric_data.clone()).await.unwrap_or_default();
226    }
227  }
228}
229
230impl Clone for MetricsMultiSender {
231  /// Clone a `MetricsMultiSender`.
232  ///
233  /// # Returns
234  ///
235  /// A cloned `MetricsMultiSender` instance
236  fn clone(&self) -> Self {
237    Self {
238      senders: self.senders.clone(),
239    }
240  }
241}
242
243/// Represents a trace signal with a Ferron module name and attributes.
244#[derive(Clone)]
245#[non_exhaustive]
246pub enum TraceSignal {
247  /// Start a new span with the given module name.
248  StartSpan(String),
249  /// End the span with the given module name and optional error description.
250  EndSpan(String, Option<String>),
251}