use std::{env, time::Duration};
use env_filter::Builder as FilterBuilder;
use log::LevelFilter;
use crate::{
error::Error,
logger::{GelfLogger, Target, TcpTarget, Writer},
record::flatten,
Map, Value,
};
const DEFAULT_FILTER_ENV: &str = "RUST_LOG";
#[derive(Debug)]
pub struct Builder {
filter: FilterBuilder,
target: Target,
null_character: bool,
type_suffix: bool,
additional_fields: Map<String, Value>,
raw_additional_fields: Map<String, Value>,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
pub fn from_default_env() -> Self {
Self::new().parse_default_env()
}
pub fn from_env(env: &str) -> Self {
Self::new().parse_env(env)
}
pub fn parse_default_env(self) -> Self {
self.parse_env(DEFAULT_FILTER_ENV)
}
pub fn parse_env(self, env: &str) -> Self {
let Ok(value) = env::var(env) else {
return self;
};
self.parse_filters(&value)
}
pub fn filter_module(mut self, module: &str, level: LevelFilter) -> Self {
self.filter.filter_module(module, level);
self
}
pub fn filter_level(mut self, level: LevelFilter) -> Self {
self.filter.filter_level(level);
self
}
pub fn filter(mut self, module: Option<&str>, level: LevelFilter) -> Self {
self.filter.filter(module, level);
self
}
pub fn parse_filters(mut self, filters: &str) -> Self {
self.filter.parse(filters);
self
}
pub fn target(mut self, target: Target) -> Self {
self.target = target;
self
}
pub fn stdout(mut self) -> Self {
self.target = Target::Stdout;
self
}
pub fn stderr(mut self) -> Self {
self.target = Target::Stderr;
self
}
pub fn tcp(mut self, config: Option<TcpTarget>) -> Self {
self.target = Target::Tcp(config.unwrap_or_default());
self
}
pub fn hostname(mut self, hostname: String) -> Self {
self.tcp_config_or_default().hostname = hostname;
self
}
pub fn port(mut self, port: u16) -> Self {
self.tcp_config_or_default().port = port;
self
}
pub fn tls(mut self, tls: bool) -> Self {
self.tcp_config_or_default().tls = tls;
self
}
pub fn connect_timeout(mut self, duration: Option<Duration>) -> Self {
self.tcp_config_or_default().connect_timeout = duration;
self
}
pub fn write_timeout(mut self, duration: Option<Duration>) -> Self {
self.tcp_config_or_default().write_timeout = duration;
self
}
pub fn buffer_size(mut self, n: usize) -> Self {
self.tcp_config_or_default().buffer_size = n;
self
}
pub fn background_error_handler(mut self, f: Option<fn(Error)>) -> Self {
self.tcp_config_or_default().background_error_handler = f;
self
}
fn tcp_config_or_default(&mut self) -> &mut TcpTarget {
match &mut self.target {
Target::Tcp(target) => target,
target => {
*target = Target::Tcp(TcpTarget::default());
match target {
Target::Tcp(target) => target,
_ => unreachable!(),
}
}
}
}
#[cfg(feature = "ovh-ldp")]
pub fn ovh_ldp(self, hostname: String, token: String) -> Self {
self.hostname(hostname)
.port(12202)
.tls(true)
.ovh_token(token)
.null_character(true)
.type_suffix(true)
}
pub fn null_character(mut self, enabled: bool) -> Self {
self.null_character = enabled;
self
}
pub fn type_suffix(mut self, enabled: bool) -> Self {
self.type_suffix = enabled;
self
}
pub fn extend_additional_fields<T: IntoIterator<Item = (String, Value)>>(
mut self,
fields: T,
) -> Self {
self.additional_fields.extend(fields);
self
}
pub fn extend_raw_additional_fields<T: IntoIterator<Item = (String, Value)>>(
mut self,
fields: T,
) -> Self {
self.raw_additional_fields.extend(fields);
self
}
#[cfg(feature = "ovh-ldp")]
pub fn ovh_token(mut self, token: String) -> Self {
_ = self
.raw_additional_fields
.insert("X-OVH-TOKEN".to_owned(), Value::String(token));
self
}
pub fn try_init(self) -> Result<(), Error> {
let logger = self.build()?;
let max_level = logger.filter();
log::set_boxed_logger(Box::new(logger))?;
log::set_max_level(max_level);
Ok(())
}
pub fn init(self) {
self.try_init().expect("logger initialization failure");
}
pub fn build(mut self) -> Result<GelfLogger, Error> {
Ok(GelfLogger {
filter: self.filter.build(),
writer: Writer::new(self.target)?,
null_character: self.null_character,
additional_fields: flatten(self.additional_fields, Some("_"), "_", self.type_suffix)
.into_iter()
.chain(self.raw_additional_fields)
.collect(),
})
}
}
impl Default for Builder {
fn default() -> Self {
Self {
filter: FilterBuilder::default(),
target: Target::Stderr,
null_character: false,
type_suffix: false,
additional_fields: Map::new(),
raw_additional_fields: Map::new(),
}
}
}