use crate::env::EnvConfig;
use crate::filter::Filter;
use crate::formatter::{self, Format, FormatterType};
use crate::logger::ProLogger;
use crate::sink::console::{ConsoleSink, ConsoleTarget};
use crate::sink::Sink;
use log::{LevelFilter, SetLoggerError};
#[cfg(feature = "color")]
use crate::color::ColorMode;
#[cfg(feature = "file")]
use crate::rotation::RotationConfig;
#[cfg(feature = "file")]
use crate::sink::file::FileSink;
pub struct ProLoggerBuilder {
level: LevelFilter,
module_filters: Vec<(String, LevelFilter)>,
sinks: Vec<Box<dyn Sink>>,
formatter_type: FormatterType,
custom_formatter: Option<Box<dyn Format>>,
#[cfg(feature = "color")]
color_mode: ColorMode,
#[cfg(not(feature = "color"))]
_use_color: bool,
#[cfg(feature = "async")]
async_capacity: Option<usize>,
}
impl ProLoggerBuilder {
pub fn new() -> Self {
Self {
level: LevelFilter::Info,
module_filters: Vec::new(),
sinks: Vec::new(),
formatter_type: FormatterType::Pretty,
custom_formatter: None,
#[cfg(feature = "color")]
color_mode: ColorMode::Auto,
#[cfg(not(feature = "color"))]
_use_color: false,
#[cfg(feature = "async")]
async_capacity: None,
}
}
pub fn with_level(mut self, level: LevelFilter) -> Self {
self.level = level;
self
}
pub fn with_module_filter(mut self, module: &str, level: LevelFilter) -> Self {
self.module_filters.push((module.to_string(), level));
self
}
pub fn with_env(self) -> Self {
self.with_env_var(EnvConfig::DEFAULT_ENV_VAR)
}
pub fn with_env_var(self, var_name: &str) -> Self {
match EnvConfig::from_env(var_name) {
Some(Ok(config)) => self.apply_env_config(config),
Some(Err(e)) => panic!("prologger: failed to parse {}: {}", var_name, e),
None => self, }
}
pub fn with_env_opt(self) -> Result<Self, crate::env::EnvParseError> {
self.with_env_var_opt(EnvConfig::DEFAULT_ENV_VAR)
}
pub fn with_env_var_opt(self, var_name: &str) -> Result<Self, crate::env::EnvParseError> {
match EnvConfig::from_env(var_name) {
Some(Ok(config)) => Ok(self.apply_env_config(config)),
Some(Err(e)) => Err(e),
None => Ok(self),
}
}
fn apply_env_config(mut self, config: EnvConfig) -> Self {
if let Some(level) = config.global_level() {
self.level = level;
}
for (module, level) in config.module_levels() {
self.module_filters.push((module.clone(), *level));
}
self
}
pub fn with_formatter(mut self, formatter_type: FormatterType) -> Self {
self.formatter_type = formatter_type;
self.custom_formatter = None;
self
}
pub fn with_custom_formatter(mut self, formatter: Box<dyn Format>) -> Self {
self.custom_formatter = Some(formatter);
self
}
#[cfg(feature = "color")]
pub fn with_color(mut self, mode: ColorMode) -> Self {
self.color_mode = mode;
self
}
pub fn with_console_default(self) -> Self {
self.with_console(ConsoleTarget::Mixed)
}
pub fn with_console(mut self, target: ConsoleTarget) -> Self {
self.sinks.push(Box::new(ConsoleSink::new(target)));
self
}
#[cfg(feature = "file")]
pub fn with_file(mut self, path: &str) -> Self {
match FileSink::new(path) {
Ok(sink) => self.sinks.push(Box::new(sink)),
Err(e) => eprintln!("[prologger] failed to open log file '{}': {}", path, e),
}
self
}
#[cfg(feature = "file")]
pub fn with_rotating_file(mut self, path: &str, config: RotationConfig) -> Self {
match FileSink::with_rotation(path, config) {
Ok(sink) => self.sinks.push(Box::new(sink)),
Err(e) => eprintln!("[prologger] failed to open log file '{}': {}", path, e),
}
self
}
pub fn with_sink(mut self, sink: Box<dyn Sink>) -> Self {
self.sinks.push(sink);
self
}
#[cfg(feature = "syslog")]
pub fn with_syslog(mut self, process_name: &str) -> Self {
match crate::sink::syslog::SyslogSink::new(process_name.to_string()) {
Ok(sink) => self.sinks.push(Box::new(sink)),
Err(e) => eprintln!("[prologger] failed to init syslog: {}", e),
}
self
}
#[cfg(feature = "x_grok")]
pub fn with_x_grok(mut self, api_key: impl Into<String>) -> Self {
self.sinks.push(Box::new(crate::sink::x_grok::XGrokSink::new(api_key)));
self
}
#[cfg(feature = "async")]
pub fn with_async(mut self, capacity: usize) -> Self {
self.async_capacity = Some(capacity);
self
}
pub fn build(mut self) -> ProLogger {
if self.sinks.is_empty() {
self.sinks.push(Box::new(ConsoleSink::default()));
}
#[cfg(feature = "color")]
let use_color = self.color_mode.should_color();
#[cfg(not(feature = "color"))]
let use_color = false;
let formatter = self
.custom_formatter
.unwrap_or_else(|| formatter::create_formatter(self.formatter_type, use_color));
let mut filter = Filter::new(self.level);
for (module, level) in self.module_filters {
filter = filter.with_module(&module, level);
}
#[cfg(feature = "async")]
let sinks: Vec<Box<dyn Sink>> = if let Some(capacity) = self.async_capacity {
vec![Box::new(crate::sink::async_sink::AsyncSink::new(self.sinks, capacity))]
} else {
self.sinks
};
#[cfg(not(feature = "async"))]
let sinks = self.sinks;
ProLogger {
filter,
formatter,
sinks,
}
}
pub fn init(self) -> Result<(), SetLoggerError> {
self.build().init()
}
}
impl Default for ProLoggerBuilder {
fn default() -> Self {
Self::new()
}
}