-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconfig.rs
More file actions
206 lines (187 loc) · 5.98 KB
/
Copy pathconfig.rs
File metadata and controls
206 lines (187 loc) · 5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// SPDX-FileCopyrightText: 2025 Famedly GmbH (info@famedly.com)
//
// SPDX-License-Identifier: Apache-2.0
//! OpenTelemetry configuration
//!
//! Module containing the configuration struct for the OpenTelemetry
use std::collections::{BTreeMap as Map, HashMap};
use famedly_rust_utils::LevelFilter;
use serde::Deserialize;
use url::Url;
/// Default gRPC Otel endpoint
const DEFAULT_ENDPOINT: &str = "http://localhost:4317";
/// Wrapper over [`Url`] with [`Default`] implementation `http://localhost:4317`
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize)]
#[repr(transparent)]
#[serde(transparent)]
#[allow(missing_docs)]
pub struct OtelUrl {
pub url: Url,
}
impl From<Url> for OtelUrl {
fn from(url: Url) -> Self {
Self { url }
}
}
#[allow(clippy::expect_used)]
impl Default for OtelUrl {
fn default() -> Self {
Self { url: Url::parse(DEFAULT_ENDPOINT).expect("Error parsing default endpoint") }
}
}
/// OpenTelemetry configuration
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OtelConfig {
/// Enables logs on stdout
pub stdout: Option<StdoutLogsConfig>,
/// Configurations for exporting traces, metrics and logs
pub exporter: Option<ExporterConfig>,
}
impl OtelConfig {
/// Helper constructor to get stdout-only config for use in tests.
#[must_use]
pub fn for_tests() -> Self {
OtelConfig {
stdout: Some(StdoutLogsConfig {
enabled: true,
level: tracing_subscriber::filter::LevelFilter::TRACE.into(),
general_level: tracing_subscriber::filter::LevelFilter::INFO.into(),
dependencies_levels: HashMap::new(),
json_output: false,
}),
exporter: None,
}
}
}
/// Configuration for exporting OpenTelemetry data
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, Deserialize)]
pub struct ExporterConfig {
/// gRPC endpoint for exporting using OTELP
#[serde(default)]
pub endpoint: OtelUrl,
/// Key value mapping of the OTEL resource. See [Resource semantic conventions](https://opentelemetry.io/docs/specs/semconv/resource/) for what can be set here.
/// Only string values are supported now.
/// This crate sets `service.name` and `service.version` by default.
#[serde(default)]
pub resource_metadata: Map<String, String>,
/// Logs exporting config
pub logs: Option<ProviderConfig>,
/// Traces exporting config
pub traces: Option<ProviderConfig>,
/// Metrics exporting config
pub metrics: Option<ProviderConfig>,
}
/// Stdout logs configuration
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize)]
pub struct StdoutLogsConfig {
/// Enables the stdout logs
#[serde(default = "true_")]
pub enabled: bool,
/// Level for the crate
#[serde(default = "default_level_filter")]
pub level: LevelFilter,
/// General level
#[serde(default = "default_level_filter")]
pub general_level: LevelFilter,
/// Level for the dependencies
#[serde(default)]
pub dependencies_levels: HashMap<String, LevelFilter>,
/// Output structured JSON logs
#[serde(default)]
pub json_output: bool,
}
/// Provider configuration for OpenTelemetry export
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize)]
pub struct ProviderConfig {
/// Enables provider
#[serde(default)]
pub enabled: bool,
/// Level for the crate
#[serde(default = "default_level_filter")]
pub level: LevelFilter,
/// General level
#[serde(default = "default_level_filter")]
pub general_level: LevelFilter,
/// Levels for the dependencies
#[serde(default)]
pub dependencies_levels: HashMap<String, LevelFilter>,
}
impl ProviderConfig {
/// Builds a trace filter
pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
format!(
"{},{}{}={}",
self.general_level,
build_dependencies_level_string(&self.dependencies_levels),
crate_name,
self.level
)
}
}
impl StdoutLogsConfig {
/// Builds a trace filter
pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
format!(
"{},{}{}={}",
self.general_level,
build_dependencies_level_string(&self.dependencies_levels),
crate_name,
self.level
)
}
}
impl Default for StdoutLogsConfig {
fn default() -> Self {
Self {
enabled: true,
level: default_level_filter(),
general_level: default_level_filter(),
dependencies_levels: HashMap::new(),
json_output: false,
}
}
}
impl Default for ProviderConfig {
fn default() -> Self {
Self {
enabled: false,
level: default_level_filter(),
general_level: default_level_filter(),
// We have to ignore these specific crates to avoid them emitting traces
// after our OTLP collector has been turned off, causing an infinite loop.
//
// See the issue: https://github.com/open-telemetry/opentelemetry-rust/issues/2877
// See the source of the workaround:
// https://github.com/open-telemetry/opentelemetry-rust/blob/v0.31.0/opentelemetry-otlp/examples/basic-otlp/src/main.rs#L69-L86
dependencies_levels: HashMap::from([
("hyper".to_owned(), LevelFilter(tracing::level_filters::LevelFilter::OFF)),
("tonic".to_owned(), LevelFilter(tracing::level_filters::LevelFilter::OFF)),
("h2".to_owned(), LevelFilter(tracing::level_filters::LevelFilter::OFF)),
("reqwest".to_owned(), LevelFilter(tracing::level_filters::LevelFilter::OFF)),
]),
}
}
}
/// Sets the default LevelFilter
const fn default_level_filter() -> LevelFilter {
LevelFilter(tracing::level_filters::LevelFilter::INFO)
}
/// Workaround for [serde-rs/serde#368](https://github.com/serde-rs/serde/issues/368)
const fn true_() -> bool {
true
}
/// Builds a string that configures the filter level of each dependency on the
/// map
fn build_dependencies_level_string(dependencies_levels: &HashMap<String, LevelFilter>) -> String {
let mut dependencies_levels =
dependencies_levels.iter().map(|(k, v)| format!("{k}={v}")).collect::<Vec<_>>().join(",");
if !dependencies_levels.is_empty() {
dependencies_levels.push(',');
}
dependencies_levels
}