Skip to main content

modkit/telemetry/
config.rs

1//! OpenTelemetry tracing and metrics configuration types
2//!
3//! These types define the configuration structure for OpenTelemetry distributed
4//! tracing and metrics.
5
6use serde::{Deserialize, Serialize};
7use std::collections::{BTreeMap, HashMap};
8
9/// Top-level OpenTelemetry configuration grouping resource identity,
10/// a shared default exporter, tracing settings and metrics settings.
11#[derive(Debug, Clone, Deserialize, Serialize, Default)]
12#[serde(deny_unknown_fields)]
13pub struct OpenTelemetryConfig {
14    #[serde(default)]
15    pub resource: OpenTelemetryResource,
16    /// Default exporter shared by tracing and metrics. Per-signal `exporter`
17    /// fields override this when present.
18    pub exporter: Option<Exporter>,
19    #[serde(default)]
20    pub tracing: TracingConfig,
21    #[serde(default)]
22    pub metrics: MetricsConfig,
23}
24
25impl OpenTelemetryConfig {
26    /// Resolve the effective exporter for tracing (per-signal or shared fallback).
27    #[must_use]
28    pub fn tracing_exporter(&self) -> Option<&Exporter> {
29        self.tracing.exporter.as_ref().or(self.exporter.as_ref())
30    }
31    /// Resolve the effective exporter for metrics (per-signal or shared fallback).
32    #[must_use]
33    pub fn metrics_exporter(&self) -> Option<&Exporter> {
34        self.metrics.exporter.as_ref().or(self.exporter.as_ref())
35    }
36}
37
38/// OpenTelemetry resource identity — attached to all traces and metrics.
39#[derive(Debug, Clone, Deserialize, Serialize)]
40#[serde(deny_unknown_fields)]
41pub struct OpenTelemetryResource {
42    /// Logical service name.
43    #[serde(default = "default_service_name")]
44    pub service_name: String,
45    /// Extra resource attributes added to every span and metric data point.
46    #[serde(default)]
47    pub attributes: BTreeMap<String, String>,
48}
49
50/// Return the default OpenTelemetry service name used when none is configured.
51fn default_service_name() -> String {
52    "cyberfabric".to_owned()
53}
54
55impl Default for OpenTelemetryResource {
56    fn default() -> Self {
57        Self {
58            service_name: default_service_name(),
59            attributes: BTreeMap::default(),
60        }
61    }
62}
63
64/// Tracing configuration for OpenTelemetry distributed tracing
65#[derive(Debug, Clone, Deserialize, Serialize, Default)]
66#[serde(deny_unknown_fields)]
67pub struct TracingConfig {
68    pub enabled: bool,
69    /// Per-signal exporter override. When `None`, the shared
70    /// [`OpenTelemetryConfig::exporter`] is used instead.
71    pub exporter: Option<Exporter>,
72    pub sampler: Option<Sampler>,
73    pub propagation: Option<Propagation>,
74    pub http: Option<HttpOpts>,
75    pub logs_correlation: Option<LogsCorrelation>,
76}
77
78/// Metrics configuration for OpenTelemetry metrics collection
79#[derive(Debug, Clone, Deserialize, Serialize, Default)]
80#[serde(deny_unknown_fields)]
81pub struct MetricsConfig {
82    pub enabled: bool,
83    /// Per-signal exporter override. When `None`, the shared
84    /// [`OpenTelemetryConfig::exporter`] is used instead.
85    pub exporter: Option<Exporter>,
86    /// Maximum number of distinct attribute combinations per instrument.
87    /// When the limit is reached, new combinations are folded into an
88    /// overflow data point.  `None` means the SDK default is used.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub cardinality_limit: Option<usize>,
91}
92
93#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, Copy)]
94#[serde(rename_all = "snake_case")]
95pub enum ExporterKind {
96    #[default]
97    OtlpGrpc,
98    OtlpHttp,
99}
100
101#[derive(Debug, Clone, Deserialize, Serialize)]
102pub struct Exporter {
103    pub kind: ExporterKind,
104    pub endpoint: Option<String>,
105    pub headers: Option<HashMap<String, String>>,
106    pub timeout_ms: Option<u64>,
107}
108
109#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
110#[serde(rename_all = "snake_case")]
111pub enum Sampler {
112    ParentBasedAlwaysOn {},
113    ParentBasedRatio {
114        #[serde(skip_serializing_if = "Option::is_none")]
115        ratio: Option<f64>,
116    },
117    AlwaysOn {},
118    AlwaysOff {},
119}
120
121#[derive(Debug, Clone, Deserialize, Serialize)]
122pub struct Propagation {
123    pub w3c_trace_context: Option<bool>,
124}
125
126#[derive(Debug, Clone, Deserialize, Serialize)]
127pub struct HttpOpts {
128    pub inject_request_id_header: Option<String>,
129    pub record_headers: Option<Vec<String>>,
130}
131
132#[derive(Debug, Clone, Deserialize, Serialize)]
133pub struct LogsCorrelation {
134    pub inject_trace_ids_into_logs: Option<bool>,
135}