1pub use af_core_macros::logger_init as init;
8
9use super::*;
10use crate::{channel, thread};
11use dashmap::DashMap;
12use log::{Level, LevelFilter, Log, Metadata, Record, RecordBuilder};
13use parking_lot::RwLock;
14use std::cell::RefCell;
15use std::sync::atomic::{self, AtomicUsize};
16
17struct Logger {
19 dropped_messages: AtomicUsize,
20 max_level: RwLock<LevelFilter>,
21 max_level_of: DashMap<String, LevelFilter>,
22 output: (channel::Sender<String>, channel::Receiver<String>),
23}
24
25static LOGGER: Lazy<Logger> = Lazy::new(|| Logger {
27 dropped_messages: default(),
28 max_level: RwLock::new(LevelFilter::Warn),
29 max_level_of: default(),
30 output: channel::with_capacity(4096),
31});
32
33thread_local! {
34 static THREAD_BUFFER: RefCell<String> = default();
36}
37
38pub fn init() {
40 if log::set_logger(&*LOGGER).is_err() {
41 return;
42 }
43
44 log::set_max_level(LevelFilter::Trace);
45
46 set_level_of(
47 "af_core",
48 match cfg!(debug_assertions) {
49 true => Debug,
50 false => Info,
51 },
52 );
53
54 thread::start("af_runtime::logger", || thread::block_on(output_messages()));
55}
56
57pub fn set_level(level: impl Into<Option<Level>>) {
61 let level = level.into().map(|lv| lv.to_level_filter()).unwrap_or(LevelFilter::Off);
62 let mut max_level = LOGGER.max_level.write();
63
64 *max_level = level;
65}
66
67pub fn set_level_of(name: impl Into<String>, level: impl Into<Option<Level>>) {
71 let level = level.into().map(|lv| lv.to_level_filter()).unwrap_or(LevelFilter::Off);
72 let name = name.into();
73
74 LOGGER.max_level_of.insert(name, level);
75}
76
77async fn output_messages() {
79 let mut buffer = String::with_capacity(128);
80 let logger = &*LOGGER;
81 let messages = &logger.output.1;
82 let mut stderr = console::Term::stderr();
83
84 while let Ok(message) = messages.recv().await {
85 let dropped_messages = logger.dropped_messages.swap(0, atomic::Ordering::Relaxed);
88
89 if dropped_messages > 0 {
90 write_message(
91 Time::now(),
92 &RecordBuilder::new()
93 .level(Level::Error)
94 .target(module_path!())
95 .args(format_args!(
96 "Too many messages. {} {} dropped.",
97 dropped_messages,
98 match dropped_messages {
99 1 => "message",
100 _ => "messages",
101 }
102 ))
103 .build(),
104 &mut buffer,
105 )
106 .unwrap();
107
108 writeln!(stderr, "{}", buffer).unwrap();
109
110 buffer.clear();
111 }
112
113 writeln!(stderr, "{}", message).unwrap();
116 }
117}
118
119fn write_message(time: Time, record: &Record, f: &mut String) -> fmt::Result {
121 use console::style;
122
123 write!(f, "{} ", style(time.format("%F %T%.3f")).black().bright())?;
126
127 match record.level() {
130 Level::Trace => {
131 write!(f, "{} ", style("TRACE").black().bright())?;
132 }
133
134 Level::Debug => {
135 write!(f, "{} ", style("DEBUG").magenta())?;
136 }
137
138 Level::Info => {
139 write!(f, " {} ", style("INFO").blue())?;
140 }
141
142 Level::Warn => {
143 write!(f, " {} ", style("WARN").yellow())?;
144 }
145
146 Level::Error => {
147 write!(f, "{} ", style("ERROR").red())?;
148 }
149 }
150
151 if !record.target().is_empty() {
154 let mut name = style(fmt::surround("[", record.target(), "] "));
155
156 name = match record.level() {
157 Level::Trace => name.black().bright(),
158 _ => name.white(),
159 };
160
161 write!(f, "{}", name)?;
162 }
163
164 let message = style(record.args()).bright();
167
168 let styled = match record.level() {
169 Level::Trace => message.black(),
170 _ => message.white(),
171 };
172
173 write!(f, "{}", styled)
174}
175
176impl Log for Logger {
179 fn enabled(&self, metadata: &Metadata) -> bool {
180 let mut target = Some(metadata.target());
181
182 while let Some(t) = target {
183 match self.max_level_of.get(t) {
184 Some(filter) => return metadata.level() <= *filter,
185
186 None => {
187 let mut split = t.rsplitn(2, "::");
188
189 split.next();
190
191 target = split.next();
192 }
193 }
194 }
195
196 metadata.level() <= *LOGGER.max_level.read()
197 }
198
199 fn log(&self, record: &Record) {
200 if !self.enabled(record.metadata()) {
201 return;
202 }
203
204 let time = Time::now();
205
206 let message = THREAD_BUFFER.with(|buffer| {
207 let mut buffer = buffer.borrow_mut();
208
209 write_message(time, record, &mut buffer).unwrap();
210
211 buffer.split_off(0)
212 });
213
214 if self.output.0.try_send(message).is_err() {
218 LOGGER.dropped_messages.fetch_add(1, atomic::Ordering::Relaxed);
219 }
220 }
221
222 fn flush(&self) {}
223}