systemprompt_models/profile/
observability.rs1use std::collections::BTreeMap;
19
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
23#[serde(deny_unknown_fields)]
24pub struct ObservabilityConfig {
25 #[serde(default)]
26 pub otlp: Option<OtlpExportConfig>,
27}
28
29impl ObservabilityConfig {
30 #[must_use]
31 pub const fn otlp(&self) -> Option<&OtlpExportConfig> {
32 self.otlp.as_ref()
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
38#[serde(deny_unknown_fields)]
39pub struct OtlpExportConfig {
40 pub endpoint: String,
41
42 #[serde(default)]
43 pub protocol: OtlpProtocol,
44
45 #[serde(default)]
46 pub headers: BTreeMap<String, String>,
47
48 #[serde(default = "default_signals")]
49 pub signals: Vec<OtlpSignal>,
50
51 #[serde(default = "default_batch_seconds")]
52 pub batch_seconds: u64,
53}
54
55impl OtlpExportConfig {
56 pub const DEFAULT_BATCH_SECONDS: u64 = 15;
57 pub const MIN_BATCH_SECONDS: u64 = 1;
58 pub const MAX_BATCH_SECONDS: u64 = 3600;
59
60 #[must_use]
61 pub fn exports(&self, signal: OtlpSignal) -> bool {
62 self.signals.contains(&signal)
63 }
64
65 #[must_use]
69 pub fn signal_url(&self, signal: OtlpSignal) -> String {
70 let base = self.endpoint.trim_end_matches('/');
71 let path = signal.http_path();
72 if base.ends_with(path) {
73 base.to_owned()
74 } else {
75 format!("{base}{path}")
76 }
77 }
78}
79
80#[derive(
81 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, schemars::JsonSchema,
82)]
83#[serde(rename_all = "lowercase")]
84pub enum OtlpProtocol {
85 #[default]
86 Http,
87 Grpc,
88}
89
90impl OtlpProtocol {
91 #[must_use]
92 pub const fn label(self) -> &'static str {
93 match self {
94 Self::Http => "http",
95 Self::Grpc => "grpc",
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
101#[serde(rename_all = "lowercase")]
102pub enum OtlpSignal {
103 Traces,
104 Logs,
105}
106
107impl OtlpSignal {
108 pub const ALL: [Self; 2] = [Self::Traces, Self::Logs];
109
110 #[must_use]
111 pub const fn label(self) -> &'static str {
112 match self {
113 Self::Traces => "traces",
114 Self::Logs => "logs",
115 }
116 }
117
118 #[must_use]
119 pub const fn http_path(self) -> &'static str {
120 match self {
121 Self::Traces => "/v1/traces",
122 Self::Logs => "/v1/logs",
123 }
124 }
125
126 #[must_use]
127 pub fn parse(label: &str) -> Option<Self> {
128 Self::ALL.into_iter().find(|s| s.label() == label)
129 }
130}
131
132impl std::fmt::Display for OtlpSignal {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 f.write_str(self.label())
135 }
136}
137
138fn default_signals() -> Vec<OtlpSignal> {
139 OtlpSignal::ALL.to_vec()
140}
141
142const fn default_batch_seconds() -> u64 {
143 OtlpExportConfig::DEFAULT_BATCH_SECONDS
144}