1use super::GlobalConfig;
2use crate::config::{
3 autotune::AutotuneLogLevel, compilation::CompilationLogLevel, profiling::ProfilingLogLevel,
4};
5use alloc::{string::ToString, sync::Arc, vec::Vec};
6use core::fmt::Display;
7use hashbrown::HashMap;
8
9#[cfg(std_io)]
10use std::{
11 fs::{File, OpenOptions},
12 io::{BufWriter, Write},
13 path::PathBuf,
14};
15
16#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
20#[serde(bound = "")]
21pub struct LoggerConfig<L: LogLevel> {
22 #[serde(default)]
24 #[cfg(std_io)]
25 pub file: Option<PathBuf>,
26
27 #[serde(default = "append_default")]
33 pub append: bool,
34
35 #[serde(default)]
37 pub stdout: bool,
38
39 #[serde(default)]
41 pub stderr: bool,
42
43 #[serde(default)]
45 pub log: Option<LogCrateLevel>,
46
47 #[serde(default)]
49 pub level: L,
50}
51
52impl<L: LogLevel> Default for LoggerConfig<L> {
53 fn default() -> Self {
54 Self {
55 #[cfg(std_io)]
56 file: None,
57 append: true,
58 #[cfg(feature = "autotune-checks")]
59 stdout: true,
60 #[cfg(not(feature = "autotune-checks"))]
61 stdout: false,
62 stderr: false,
63 log: None,
64 level: L::default(),
65 }
66 }
67}
68
69#[derive(
73 Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq,
74)]
75pub enum LogCrateLevel {
76 #[default]
78 #[serde(rename = "info")]
79 Info,
80
81 #[serde(rename = "debug")]
83 Debug,
84
85 #[serde(rename = "trace")]
87 Trace,
88}
89
90impl LogLevel for u32 {}
91
92fn append_default() -> bool {
93 true
94}
95
96pub trait LogLevel:
98 serde::de::DeserializeOwned + serde::Serialize + Clone + Copy + core::fmt::Debug + Default
99{
100}
101
102#[derive(Debug)]
104pub struct Logger {
105 loggers: Vec<LoggerKind>,
107
108 compilation_index: Vec<usize>,
110
111 profiling_index: Vec<usize>,
113
114 autotune_index: Vec<usize>,
116
117 pub config: Arc<GlobalConfig>,
119}
120
121impl Default for Logger {
122 fn default() -> Self {
123 Self::new()
124 }
125}
126
127impl Logger {
128 pub fn new() -> Self {
135 let config = GlobalConfig::get();
136 let mut loggers = Vec::new();
137 let mut compilation_index = Vec::new();
138 let mut profiling_index = Vec::new();
139 let mut autotune_index = Vec::new();
140
141 #[derive(Hash, PartialEq, Eq)]
142 enum LoggerId {
143 #[cfg(std_io)]
144 File(PathBuf),
145 #[cfg(feature = "std")]
146 Stdout,
147 #[cfg(feature = "std")]
148 Stderr,
149 LogCrate(LogCrateLevel),
150 }
151
152 let mut logger2index = HashMap::<LoggerId, usize>::new();
153
154 fn new_logger<S: Clone, ID: Fn(S) -> LoggerId, LG: Fn(S) -> LoggerKind>(
155 setting_index: &mut Vec<usize>,
156 loggers: &mut Vec<LoggerKind>,
157 logger2index: &mut HashMap<LoggerId, usize>,
158 state: S,
159 func_id: ID,
160 func_logger: LG,
161 ) {
162 let id = func_id(state.clone());
163
164 if let Some(index) = logger2index.get(&id) {
165 setting_index.push(*index);
166 } else {
167 let logger = func_logger(state);
168 let index = loggers.len();
169 logger2index.insert(id, index);
170 loggers.push(logger);
171 setting_index.push(index);
172 }
173 }
174
175 fn register_logger<L: LogLevel>(
176 #[allow(unused_variables)] kind: &LoggerConfig<L>, #[allow(unused_variables)] append: bool, level: Option<LogCrateLevel>,
179 setting_index: &mut Vec<usize>,
180 loggers: &mut Vec<LoggerKind>,
181 logger2index: &mut HashMap<LoggerId, usize>,
182 ) {
183 #[cfg(std_io)]
184 if let Some(file) = &kind.file {
185 new_logger(
186 setting_index,
187 loggers,
188 logger2index,
189 (file, append),
190 |(file, _append)| LoggerId::File(file.clone()),
191 |(file, append)| LoggerKind::File(FileLogger::new(file, append)),
192 );
193 }
194
195 #[cfg(feature = "std")]
196 if kind.stdout {
197 new_logger(
198 setting_index,
199 loggers,
200 logger2index,
201 (),
202 |_| LoggerId::Stdout,
203 |_| LoggerKind::Stdout,
204 );
205 }
206
207 #[cfg(feature = "std")]
208 if kind.stderr {
209 new_logger(
210 setting_index,
211 loggers,
212 logger2index,
213 (),
214 |_| LoggerId::Stderr,
215 |_| LoggerKind::Stderr,
216 );
217 }
218
219 if let Some(level) = level {
220 new_logger(
221 setting_index,
222 loggers,
223 logger2index,
224 level,
225 LoggerId::LogCrate,
226 LoggerKind::Log,
227 );
228 }
229 }
230
231 if let CompilationLogLevel::Disabled = config.compilation.logger.level {
232 } else {
233 register_logger(
234 &config.compilation.logger,
235 config.compilation.logger.append,
236 config.compilation.logger.log,
237 &mut compilation_index,
238 &mut loggers,
239 &mut logger2index,
240 )
241 }
242
243 if let ProfilingLogLevel::Disabled = config.profiling.logger.level {
244 } else {
245 register_logger(
246 &config.profiling.logger,
247 config.profiling.logger.append,
248 config.profiling.logger.log,
249 &mut profiling_index,
250 &mut loggers,
251 &mut logger2index,
252 )
253 }
254
255 if let AutotuneLogLevel::Disabled = config.autotune.logger.level {
256 } else {
257 register_logger(
258 &config.autotune.logger,
259 config.autotune.logger.append,
260 config.autotune.logger.log,
261 &mut autotune_index,
262 &mut loggers,
263 &mut logger2index,
264 )
265 }
266
267 Self {
268 loggers,
269 compilation_index,
270 profiling_index,
271 autotune_index,
272 config,
273 }
274 }
275
276 pub fn log_compilation<S: Display>(&mut self, msg: &S) {
278 let length = self.compilation_index.len();
279 if length > 1 {
280 let msg = msg.to_string();
281 for i in 0..length {
282 let index = self.compilation_index[i];
283 self.log(&msg, index)
284 }
285 } else if let Some(index) = self.compilation_index.first() {
286 self.log(&msg, *index)
287 }
288 }
289
290 pub fn log_profiling<S: Display>(&mut self, msg: &S) {
292 let length = self.profiling_index.len();
293 if length > 1 {
294 let msg = msg.to_string();
295 for i in 0..length {
296 let index = self.profiling_index[i];
297 self.log(&msg, index)
298 }
299 } else if let Some(index) = self.profiling_index.first() {
300 self.log(&msg, *index)
301 }
302 }
303
304 pub fn log_autotune<S: Display>(&mut self, msg: &S) {
306 let length = self.autotune_index.len();
307 if length > 1 {
308 let msg = msg.to_string();
309 for i in 0..length {
310 let index = self.autotune_index[i];
311 self.log(&msg, index)
312 }
313 } else if let Some(index) = self.autotune_index.first() {
314 self.log(&msg, *index)
315 }
316 }
317
318 pub fn log_level_autotune(&self) -> AutotuneLogLevel {
320 self.config.autotune.logger.level
321 }
322
323 pub fn log_level_compilation(&self) -> CompilationLogLevel {
325 self.config.compilation.logger.level
326 }
327
328 pub fn log_level_profiling(&self) -> ProfilingLogLevel {
330 self.config.profiling.logger.level
331 }
332
333 fn log<S: Display>(&mut self, msg: &S, index: usize) {
334 let logger = &mut self.loggers[index];
335 logger.log(msg);
336 }
337}
338
339#[derive(Default, Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
343pub enum BinaryLogLevel {
344 #[default]
346 #[serde(rename = "disabled")]
347 Disabled,
348
349 #[serde(rename = "full")]
351 Full,
352}
353
354impl LogLevel for BinaryLogLevel {}
355
356#[derive(Debug)]
358enum LoggerKind {
359 #[cfg(std_io)]
361 File(FileLogger),
362
363 #[cfg(feature = "std")]
365 Stdout,
366
367 #[cfg(feature = "std")]
369 Stderr,
370
371 Log(LogCrateLevel),
373}
374
375impl LoggerKind {
376 fn log<S: Display>(&mut self, msg: &S) {
377 match self {
378 #[cfg(std_io)]
379 LoggerKind::File(file_logger) => file_logger.log(msg),
380 #[cfg(feature = "std")]
381 LoggerKind::Stdout => println!("{msg}"),
382 #[cfg(feature = "std")]
383 LoggerKind::Stderr => eprintln!("{msg}"),
384 LoggerKind::Log(level) => match level {
385 LogCrateLevel::Info => log::info!("{msg}"),
386 LogCrateLevel::Trace => log::debug!("{msg}"),
387 LogCrateLevel::Debug => log::trace!("{msg}"),
388 },
389 }
390 }
391}
392
393#[derive(Debug)]
395#[cfg(std_io)]
396struct FileLogger {
397 writer: BufWriter<File>,
398}
399
400#[cfg(std_io)]
401impl FileLogger {
402 fn new(path: &PathBuf, append: bool) -> Self {
404 let file = OpenOptions::new()
405 .write(true)
406 .append(append)
407 .create(true)
408 .open(path)
409 .unwrap();
410
411 Self {
412 writer: BufWriter::new(file),
413 }
414 }
415
416 fn log<S: Display>(&mut self, msg: &S) {
418 writeln!(self.writer, "{msg}").expect("Should be able to log debug information.");
419 self.writer.flush().expect("Can complete write operation.");
420 }
421}