use std::collections::{BTreeMap as Map, HashMap};
use famedly_rust_utils::LevelFilter;
use serde::Deserialize;
use url::Url;
const DEFAULT_ENDPOINT: &str = "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") }
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OtelConfig {
pub stdout: Option<StdoutLogsConfig>,
pub exporter: Option<ExporterConfig>,
}
impl OtelConfig {
#[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,
}
}
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, Deserialize)]
pub struct ExporterConfig {
#[serde(default)]
pub endpoint: OtelUrl,
#[serde(default)]
pub resource_metadata: Map<String, String>,
pub logs: Option<ProviderConfig>,
pub traces: Option<ProviderConfig>,
pub metrics: Option<ProviderConfig>,
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize)]
pub struct StdoutLogsConfig {
#[serde(default = "true_")]
pub enabled: bool,
#[serde(default = "default_level_filter")]
pub level: LevelFilter,
#[serde(default = "default_level_filter")]
pub general_level: LevelFilter,
#[serde(default)]
pub dependencies_levels: HashMap<String, LevelFilter>,
#[serde(default)]
pub json_output: bool,
}
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize)]
pub struct ProviderConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_level_filter")]
pub level: LevelFilter,
#[serde(default = "default_level_filter")]
pub general_level: LevelFilter,
#[serde(default)]
pub dependencies_levels: HashMap<String, LevelFilter>,
}
impl ProviderConfig {
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 {
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(),
dependencies_levels: HashMap::new(),
}
}
}
const fn default_level_filter() -> LevelFilter {
LevelFilter(tracing::level_filters::LevelFilter::INFO)
}
const fn true_() -> bool {
true
}
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
}