flexi_logger/
flexi_logger.rs1use crate::{
2 filter::LogLineFilter,
3 primary_writer::PrimaryWriter,
4 util::{eprint_err, eprint_msg, ErrorCode},
5 writers::LogWriter,
6 DeferredNow, LogSpecification,
7};
8
9#[cfg(feature = "textfilter")]
10use regex::Regex;
11use std::collections::HashMap;
12use std::sync::{Arc, RwLock};
13
14pub(crate) struct FlexiLogger {
18 log_specification: Arc<RwLock<LogSpecification>>,
19 primary_writer: Arc<PrimaryWriter>,
20 other_writers: Arc<HashMap<String, Box<dyn LogWriter>>>,
21 filter: Option<Box<dyn LogLineFilter + Send + Sync>>,
22}
23
24impl FlexiLogger {
25 pub fn new(
26 log_specification: Arc<RwLock<LogSpecification>>,
27 primary_writer: Arc<PrimaryWriter>,
28 other_writers: Arc<HashMap<String, Box<dyn LogWriter>>>,
29 filter: Option<Box<dyn LogLineFilter + Send + Sync>>,
30 ) -> Self {
31 Self {
32 log_specification,
33 primary_writer,
34 other_writers,
35 filter,
36 }
37 }
38
39 fn primary_enabled(&self, level: log::Level, module: &str) -> bool {
40 self.log_specification
41 .read()
42 .map_err(|e| eprint_err(ErrorCode::Poison, "rwlock on log spec is poisoned", &e))
43 .unwrap()
44 .enabled(level, module)
45 }
46}
47
48impl log::Log for FlexiLogger {
49 fn enabled(&self, metadata: &log::Metadata) -> bool {
57 let target = metadata.target();
58 let level = metadata.level();
59
60 if !self.other_writers.is_empty() && target.starts_with('{') {
61 let targets: Vec<&str> = target[1..(target.len() - 1)].split(',').collect();
63 for t in targets {
64 if t != "_Default" {
65 match self.other_writers.get(t) {
66 None => {
67 eprint_msg(ErrorCode::WriterSpec, &format!("bad writer spec: {t}"));
68 }
69 Some(writer) => {
70 if level < writer.max_log_level() {
71 return true;
72 }
73 }
74 }
75 }
76 }
77 }
78
79 self.primary_enabled(level, target)
80 }
81
82 fn log(&self, record: &log::Record) {
83 let target = record.metadata().target();
84 let mut now = DeferredNow::new();
85 let special_target_is_used = target.starts_with('{');
86 if special_target_is_used {
87 let mut use_default = false;
88 let targets: Vec<&str> = target[1..(target.len() - 1)].split(',').collect();
89 for t in targets {
90 if t == "_Default" {
91 use_default = true;
92 } else {
93 match self.other_writers.get(t) {
94 None => {
95 eprint_msg(ErrorCode::WriterSpec, &format!("bad writer spec: {t}"));
96 }
97 Some(writer) => {
98 writer.write(&mut now, record).unwrap_or_else(|e| {
99 eprint_err(
100 ErrorCode::Write,
101 &format!("writing log line to custom writer \"{t}\" failed"),
102 &e,
103 );
104 });
105 }
106 }
107 }
108 }
109 if !use_default {
110 return;
111 }
112 }
113
114 let effective_target = if special_target_is_used {
115 record.module_path().unwrap_or_default()
116 } else {
117 target
118 };
119 if !self.primary_enabled(record.level(), effective_target) {
120 return;
121 }
122
123 #[cfg(feature = "textfilter")]
124 {
125 let check_text_filter = |text_filter: Option<&Regex>| {
127 text_filter.is_none_or(|filter| filter.is_match(&record.args().to_string()))
128 };
129
130 if !check_text_filter(
131 self.log_specification.read().as_ref().unwrap().text_filter(),
132 ) {
133 return;
134 }
135 }
136
137 if let Some(ref filter) = self.filter {
138 filter.write(&mut now, record, &(*self.primary_writer))
139 } else {
140 self.primary_writer.write(&mut now, record)
141 }
142 .unwrap_or_else(|e| {
143 eprint_err(ErrorCode::Write, "writing log line failed", &e);
144 });
145 }
146
147 fn flush(&self) {
148 self.primary_writer.flush().unwrap_or_else(|e| {
149 eprint_err(ErrorCode::Flush, "flushing primary writer failed", &e);
150 });
151 for writer in self.other_writers.values() {
152 writer.flush().unwrap_or_else(|e| {
153 eprint_err(ErrorCode::Flush, "flushing custom writer failed", &e);
154 });
155 }
156 }
157}