rust_telemetry/
config.rs

1//! OpenTelemetry configuration
2//!
3//! Module containing the configuration struct for the OpenTelemetry
4
5use std::collections::BTreeMap as Map;
6
7use famedly_rust_utils::LevelFilter;
8use serde::Deserialize;
9use url::Url;
10
11/// Default gRPC Otel endpoint
12const DEFAULT_ENDPOINT: &str = "http://localhost:4317";
13
14/// Wrapper over [`Url`] with [`Default`] implementation `http://localhost:4317`
15#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
16#[derive(Debug, Clone, Deserialize)]
17#[repr(transparent)]
18#[serde(transparent)]
19#[allow(missing_docs)]
20pub struct OtelUrl {
21	pub url: Url,
22}
23
24impl From<Url> for OtelUrl {
25	fn from(url: Url) -> Self {
26		Self { url }
27	}
28}
29
30#[allow(clippy::expect_used)]
31impl Default for OtelUrl {
32	fn default() -> Self {
33		Self { url: Url::parse(DEFAULT_ENDPOINT).expect("Error parsing default endpoint") }
34	}
35}
36
37/// OpenTelemetry configuration
38#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
39#[derive(Debug, Clone, Default, Deserialize)]
40pub struct OtelConfig {
41	/// Enables logs on stdout
42	pub stdout: Option<StdoutLogsConfig>,
43	/// Configurations for exporting traces, metrics and logs
44	pub exporter: Option<ExporterConfig>,
45}
46
47impl OtelConfig {
48	/// Helper constructor to get stdout-only config for use in tests.
49	#[must_use]
50	pub fn for_tests() -> Self {
51		OtelConfig {
52			stdout: Some(StdoutLogsConfig {
53				enabled: true,
54				level: tracing_subscriber::filter::LevelFilter::TRACE.into(),
55				general_level: tracing_subscriber::filter::LevelFilter::INFO.into(),
56				json_output: false,
57			}),
58			exporter: None,
59		}
60	}
61}
62
63/// Configuration for exporting OpenTelemetry data
64#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
65#[derive(Debug, Clone, Default, Deserialize)]
66pub struct ExporterConfig {
67	/// gRPC endpoint for exporting using OTELP
68	#[serde(default)]
69	pub endpoint: OtelUrl,
70	/// Key value mapping of the OTEL resource. See [Resource semantic conventions](https://opentelemetry.io/docs/specs/semconv/resource/) for what can be set here.
71	/// Only string values are supported now.
72	/// This crate sets `service.name` and `service.version` by default.
73	#[serde(default)]
74	pub resource_metadata: Map<String, String>,
75	/// Logs exporting config
76	pub logs: Option<ProviderConfig>,
77	/// Traces exporting config
78	pub traces: Option<ProviderConfig>,
79	/// Metrics exporting config
80	pub metrics: Option<ProviderConfig>,
81}
82
83/// Stdout logs configuration
84#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
85#[derive(Debug, Clone, Deserialize)]
86pub struct StdoutLogsConfig {
87	/// Enables the stdout logs
88	#[serde(default = "true_")]
89	pub enabled: bool,
90	/// Level for the crate
91	#[serde(default = "default_level_filter")]
92	pub level: LevelFilter,
93	/// Level for the dependencies
94	#[serde(default = "default_level_filter")]
95	pub general_level: LevelFilter,
96	/// Output structured JSON logs
97	#[serde(default)]
98	pub json_output: bool,
99}
100
101/// Provider configuration for OpenTelemetry export
102#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
103#[derive(Debug, Clone, Deserialize)]
104pub struct ProviderConfig {
105	/// Enables provider
106	#[serde(default)]
107	pub enabled: bool,
108	/// Level for the crate
109	#[serde(default = "default_level_filter")]
110	pub level: LevelFilter,
111	/// Level for the dependencies
112	#[serde(default = "default_level_filter")]
113	pub general_level: LevelFilter,
114}
115
116impl ProviderConfig {
117	/// Builds a trace filter
118	pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
119		format!("{},{}={}", self.general_level, crate_name, self.level)
120	}
121}
122
123impl StdoutLogsConfig {
124	/// Builds a trace filter
125	pub(crate) fn get_filter(&self, crate_name: &'static str) -> String {
126		format!("{},{}={}", self.general_level, crate_name, self.level)
127	}
128}
129
130impl Default for StdoutLogsConfig {
131	fn default() -> Self {
132		Self {
133			enabled: true,
134			level: default_level_filter(),
135			general_level: default_level_filter(),
136			json_output: false,
137		}
138	}
139}
140
141impl Default for ProviderConfig {
142	fn default() -> Self {
143		Self {
144			enabled: false,
145			level: default_level_filter(),
146			general_level: default_level_filter(),
147		}
148	}
149}
150
151/// Sets the default LevelFilter
152const fn default_level_filter() -> LevelFilter {
153	LevelFilter(tracing::level_filters::LevelFilter::INFO)
154}
155
156/// Workaround for [serde-rs/serde#368](https://github.com/serde-rs/serde/issues/368)
157const fn true_() -> bool {
158	true
159}