1use std::collections::{BTreeMap as Map, HashMap};
10
11use famedly_rust_utils::LevelFilter;
12use serde::Deserialize;
13use url::Url;
14
15const DEFAULT_ENDPOINT: &str = "http://localhost:4317";
17
18#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
20#[derive(Debug, Clone, Deserialize)]
21#[repr(transparent)]
22#[serde(transparent)]
23#[allow(missing_docs)]
24pub struct OtelUrl {
25 pub url: Url,
26}
27
28impl From<Url> for OtelUrl {
29 fn from(url: Url) -> Self {
30 Self { url }
31 }
32}
33
34#[allow(clippy::expect_used)]
35impl Default for OtelUrl {
36 fn default() -> Self {
37 Self { url: Url::parse(DEFAULT_ENDPOINT).expect("Error parsing default endpoint") }
38 }
39}
40
41#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
43#[derive(Debug, Clone, Default, Deserialize)]
44pub struct OtelConfig {
45 pub stdout: Option<StdoutLogsConfig>,
47 pub exporter: Option<ExporterConfig>,
49}
50
51impl OtelConfig {
52 #[must_use]
54 pub fn for_tests() -> Self {
55 OtelConfig {
56 stdout: Some(StdoutLogsConfig {
57 enabled: true,
58 level: tracing_subscriber::filter::LevelFilter::TRACE.into(),
59 general_level: tracing_subscriber::filter::LevelFilter::INFO.into(),
60 dependencies_levels: HashMap::new(),
61 json_output: false,
62 }),
63 exporter: None,
64 }
65 }
66}
67
68#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
70#[derive(Debug, Clone, Default, Deserialize)]
71pub struct ExporterConfig {
72 #[serde(default)]
74 pub endpoint: OtelUrl,
75 #[serde(default)]
79 pub resource_metadata: Map<String, String>,
80 pub logs: Option<ProviderConfig>,
82 pub traces: Option<ProviderConfig>,
84 pub metrics: Option<ProviderConfig>,
86}
87
88#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
90#[derive(Debug, Clone, Deserialize)]
91pub struct StdoutLogsConfig {
92 #[serde(default = "true_")]
94 pub enabled: bool,
95 #[serde(default = "default_level_filter")]
97 pub level: LevelFilter,
98 #[serde(default = "default_level_filter")]
100 pub general_level: LevelFilter,
101 #[serde(default)]
103 pub dependencies_levels: HashMap<String, LevelFilter>,
104 #[serde(default)]
106 pub json_output: bool,
107}
108
109#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
111#[derive(Debug, Clone, Deserialize)]
112pub struct ProviderConfig {
113 #[serde(default)]
115 pub enabled: bool,
116 #[serde(default = "default_level_filter")]
118 pub level: LevelFilter,
119 #[serde(default = "default_level_filter")]
121 pub general_level: LevelFilter,
122 #[serde(default)]
124 pub dependencies_levels: HashMap<String, LevelFilter>,
125}
126
127impl ProviderConfig {
128 pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
130 format!(
131 "{},{}{}={}",
132 self.general_level,
133 build_dependencies_level_string(&self.dependencies_levels),
134 crate_name,
135 self.level
136 )
137 }
138}
139
140impl StdoutLogsConfig {
141 pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
143 format!(
144 "{},{}{}={}",
145 self.general_level,
146 build_dependencies_level_string(&self.dependencies_levels),
147 crate_name,
148 self.level
149 )
150 }
151}
152
153impl Default for StdoutLogsConfig {
154 fn default() -> Self {
155 Self {
156 enabled: true,
157 level: default_level_filter(),
158 general_level: default_level_filter(),
159 dependencies_levels: HashMap::new(),
160 json_output: false,
161 }
162 }
163}
164
165impl Default for ProviderConfig {
166 fn default() -> Self {
167 Self {
168 enabled: false,
169 level: default_level_filter(),
170 general_level: default_level_filter(),
171 dependencies_levels: HashMap::new(),
172 }
173 }
174}
175
176const fn default_level_filter() -> LevelFilter {
178 LevelFilter(tracing::level_filters::LevelFilter::INFO)
179}
180
181const fn true_() -> bool {
183 true
184}
185
186fn build_dependencies_level_string(dependencies_levels: &HashMap<String, LevelFilter>) -> String {
189 let mut dependencies_levels =
190 dependencies_levels.iter().map(|(k, v)| format!("{k}={v}")).collect::<Vec<_>>().join(",");
191 if !dependencies_levels.is_empty() {
192 dependencies_levels.push(',');
193 }
194 dependencies_levels
195}