1use super::GlobalConfig;
2use crate::config::{
3 autotune::AutotuneLogLevel, compilation::CompilationLogLevel, memory::MemoryLogLevel,
4 profiling::ProfilingLogLevel, streaming::StreamingLogLevel,
5};
6use alloc::{string::ToString, sync::Arc, vec::Vec};
7use core::fmt::Display;
8use hashbrown::HashMap;
9
10#[cfg(std_io)]
11use std::{
12 fs::{File, OpenOptions},
13 io::{BufWriter, Write},
14 path::PathBuf,
15};
16
17#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
21#[serde(bound = "")]
22pub struct LoggerConfig<L: LogLevel> {
23 #[serde(default)]
25 #[cfg(std_io)]
26 pub file: Option<PathBuf>,
27
28 #[serde(default = "append_default")]
34 pub append: bool,
35
36 #[serde(default)]
38 pub stdout: bool,
39
40 #[serde(default)]
42 pub stderr: bool,
43
44 #[serde(default)]
46 pub log: Option<LogCrateLevel>,
47
48 #[serde(default)]
50 pub level: L,
51}
52
53impl<L: LogLevel> Default for LoggerConfig<L> {
54 fn default() -> Self {
55 Self {
56 #[cfg(std_io)]
57 file: None,
58 append: true,
59 #[cfg(feature = "autotune-checks")]
60 stdout: true,
61 #[cfg(not(feature = "autotune-checks"))]
62 stdout: false,
63 stderr: false,
64 log: None,
65 level: L::default(),
66 }
67 }
68}
69
70#[derive(
74 Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq,
75)]
76pub enum LogCrateLevel {
77 #[default]
79 #[serde(rename = "info")]
80 Info,
81
82 #[serde(rename = "debug")]
84 Debug,
85
86 #[serde(rename = "trace")]
88 Trace,
89}
90
91impl LogLevel for u32 {}
92
93fn append_default() -> bool {
94 true
95}
96
97pub trait LogLevel:
99 serde::de::DeserializeOwned + serde::Serialize + Clone + Copy + core::fmt::Debug + Default
100{
101}
102
103#[derive(Debug)]
105pub struct Logger {
106 loggers: Vec<LoggerKind>,
108
109 compilation_index: Vec<usize>,
111
112 profiling_index: Vec<usize>,
114
115 autotune_index: Vec<usize>,
117
118 streaming_index: Vec<usize>,
120
121 memory_index: Vec<usize>,
123
124 pub config: Arc<GlobalConfig>,
126}
127
128impl Default for Logger {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl Logger {
135 pub fn new() -> Self {
142 let config = GlobalConfig::get();
143 let mut loggers = Vec::new();
144 let mut compilation_index = Vec::new();
145 let mut profiling_index = Vec::new();
146 let mut autotune_index = Vec::new();
147 let mut streaming_index = Vec::new();
148 let mut memory_index = Vec::new();
149
150 #[derive(Hash, PartialEq, Eq)]
151 enum LoggerId {
152 #[cfg(std_io)]
153 File(PathBuf),
154 #[cfg(feature = "std")]
155 Stdout,
156 #[cfg(feature = "std")]
157 Stderr,
158 LogCrate(LogCrateLevel),
159 }
160
161 let mut logger2index = HashMap::<LoggerId, usize>::new();
162
163 fn new_logger<S: Clone, ID: Fn(S) -> LoggerId, LG: Fn(S) -> LoggerKind>(
164 setting_index: &mut Vec<usize>,
165 loggers: &mut Vec<LoggerKind>,
166 logger2index: &mut HashMap<LoggerId, usize>,
167 state: S,
168 func_id: ID,
169 func_logger: LG,
170 ) {
171 let id = func_id(state.clone());
172
173 if let Some(index) = logger2index.get(&id) {
174 setting_index.push(*index);
175 } else {
176 let logger = func_logger(state);
177 let index = loggers.len();
178 logger2index.insert(id, index);
179 loggers.push(logger);
180 setting_index.push(index);
181 }
182 }
183
184 fn register_logger<L: LogLevel>(
185 #[allow(unused_variables)] kind: &LoggerConfig<L>, #[allow(unused_variables)] append: bool, level: Option<LogCrateLevel>,
188 setting_index: &mut Vec<usize>,
189 loggers: &mut Vec<LoggerKind>,
190 logger2index: &mut HashMap<LoggerId, usize>,
191 ) {
192 #[cfg(std_io)]
193 if let Some(file) = &kind.file {
194 new_logger(
195 setting_index,
196 loggers,
197 logger2index,
198 (file, append),
199 |(file, _append)| LoggerId::File(file.clone()),
200 |(file, append)| LoggerKind::File(FileLogger::new(file, append)),
201 );
202 }
203
204 #[cfg(feature = "std")]
205 if kind.stdout {
206 new_logger(
207 setting_index,
208 loggers,
209 logger2index,
210 (),
211 |_| LoggerId::Stdout,
212 |_| LoggerKind::Stdout,
213 );
214 }
215
216 #[cfg(feature = "std")]
217 if kind.stderr {
218 new_logger(
219 setting_index,
220 loggers,
221 logger2index,
222 (),
223 |_| LoggerId::Stderr,
224 |_| LoggerKind::Stderr,
225 );
226 }
227
228 if let Some(level) = level {
229 new_logger(
230 setting_index,
231 loggers,
232 logger2index,
233 level,
234 LoggerId::LogCrate,
235 LoggerKind::Log,
236 );
237 }
238 }
239
240 if let CompilationLogLevel::Disabled = config.compilation.logger.level {
241 } else {
242 register_logger(
243 &config.compilation.logger,
244 config.compilation.logger.append,
245 config.compilation.logger.log,
246 &mut compilation_index,
247 &mut loggers,
248 &mut logger2index,
249 )
250 }
251
252 if let ProfilingLogLevel::Disabled = config.profiling.logger.level {
253 } else {
254 register_logger(
255 &config.profiling.logger,
256 config.profiling.logger.append,
257 config.profiling.logger.log,
258 &mut profiling_index,
259 &mut loggers,
260 &mut logger2index,
261 )
262 }
263
264 if let AutotuneLogLevel::Disabled = config.autotune.logger.level {
265 } else {
266 register_logger(
267 &config.autotune.logger,
268 config.autotune.logger.append,
269 config.autotune.logger.log,
270 &mut autotune_index,
271 &mut loggers,
272 &mut logger2index,
273 )
274 }
275
276 if let StreamingLogLevel::Disabled = config.streaming.logger.level {
277 } else {
278 register_logger(
279 &config.streaming.logger,
280 config.streaming.logger.append,
281 config.streaming.logger.log,
282 &mut streaming_index,
283 &mut loggers,
284 &mut logger2index,
285 )
286 }
287
288 if let MemoryLogLevel::Disabled = config.memory.logger.level {
289 } else {
290 register_logger(
291 &config.memory.logger,
292 config.memory.logger.append,
293 config.memory.logger.log,
294 &mut memory_index,
295 &mut loggers,
296 &mut logger2index,
297 )
298 }
299
300 Self {
301 loggers,
302 compilation_index,
303 profiling_index,
304 autotune_index,
305 streaming_index,
306 memory_index,
307 config,
308 }
309 }
310
311 pub fn log_streaming<S: Display>(&mut self, msg: &S) {
313 let length = self.streaming_index.len();
314 if length > 1 {
315 let msg = msg.to_string();
316 for i in 0..length {
317 let index = self.streaming_index[i];
318 self.log(&msg, index)
319 }
320 } else if let Some(index) = self.streaming_index.first() {
321 self.log(&msg, *index)
322 }
323 }
324
325 pub fn log_memory<S: Display>(&mut self, msg: &S) {
327 let length = self.memory_index.len();
328 if length > 1 {
329 let msg = msg.to_string();
330 for i in 0..length {
331 let index = self.memory_index[i];
332 self.log(&msg, index)
333 }
334 } else if let Some(index) = self.memory_index.first() {
335 self.log(&msg, *index)
336 }
337 }
338
339 pub fn log_compilation<S: Display>(&mut self, msg: &S) {
341 let length = self.compilation_index.len();
342 if length > 1 {
343 let msg = msg.to_string();
344 for i in 0..length {
345 let index = self.compilation_index[i];
346 self.log(&msg, index)
347 }
348 } else if let Some(index) = self.compilation_index.first() {
349 self.log(&msg, *index)
350 }
351 }
352
353 pub fn log_profiling<S: Display>(&mut self, msg: &S) {
355 let length = self.profiling_index.len();
356 if length > 1 {
357 let msg = msg.to_string();
358 for i in 0..length {
359 let index = self.profiling_index[i];
360 self.log(&msg, index)
361 }
362 } else if let Some(index) = self.profiling_index.first() {
363 self.log(&msg, *index)
364 }
365 }
366
367 pub fn log_autotune<S: Display>(&mut self, msg: &S) {
369 let length = self.autotune_index.len();
370 if length > 1 {
371 let msg = msg.to_string();
372 for i in 0..length {
373 let index = self.autotune_index[i];
374 self.log(&msg, index)
375 }
376 } else if let Some(index) = self.autotune_index.first() {
377 self.log(&msg, *index)
378 }
379 }
380
381 pub fn log_level_streaming(&self) -> StreamingLogLevel {
383 self.config.streaming.logger.level
384 }
385
386 pub fn log_level_autotune(&self) -> AutotuneLogLevel {
388 self.config.autotune.logger.level
389 }
390
391 pub fn log_level_compilation(&self) -> CompilationLogLevel {
393 self.config.compilation.logger.level
394 }
395
396 pub fn log_level_profiling(&self) -> ProfilingLogLevel {
398 self.config.profiling.logger.level
399 }
400
401 fn log<S: Display>(&mut self, msg: &S, index: usize) {
402 let logger = &mut self.loggers[index];
403 logger.log(msg);
404 }
405}
406
407#[derive(Debug)]
409enum LoggerKind {
410 #[cfg(std_io)]
412 File(FileLogger),
413
414 #[cfg(feature = "std")]
416 Stdout,
417
418 #[cfg(feature = "std")]
420 Stderr,
421
422 Log(LogCrateLevel),
424}
425
426impl LoggerKind {
427 fn log<S: Display>(&mut self, msg: &S) {
428 match self {
429 #[cfg(std_io)]
430 LoggerKind::File(file_logger) => file_logger.log(msg),
431 #[cfg(feature = "std")]
432 LoggerKind::Stdout => println!("{msg}"),
433 #[cfg(feature = "std")]
434 LoggerKind::Stderr => eprintln!("{msg}"),
435 LoggerKind::Log(level) => match level {
436 LogCrateLevel::Info => log::info!("{msg}"),
437 LogCrateLevel::Trace => log::debug!("{msg}"),
438 LogCrateLevel::Debug => log::trace!("{msg}"),
439 },
440 }
441 }
442}
443
444#[derive(Debug)]
446#[cfg(std_io)]
447struct FileLogger {
448 writer: BufWriter<File>,
449}
450
451#[cfg(std_io)]
452impl FileLogger {
453 fn new(path: &PathBuf, append: bool) -> Self {
455 let file = OpenOptions::new()
456 .write(true)
457 .append(append)
458 .create(true)
459 .open(path)
460 .unwrap();
461
462 Self {
463 writer: BufWriter::new(file),
464 }
465 }
466
467 fn log<S: Display>(&mut self, msg: &S) {
469 writeln!(self.writer, "{msg}").expect("Should be able to log debug information.");
470 self.writer.flush().expect("Can complete write operation.");
471 }
472}