cubecl_common/config/
logger.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3use core::fmt::Display;
4use hashbrown::HashMap;
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7
8#[cfg(std_io)]
9use std::{
10 fs::{File, OpenOptions},
11 io::{BufWriter, Write},
12 path::PathBuf,
13};
14
15#[cfg(feature = "std")]
16use std::{eprintln, println};
17
18#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
22#[serde(bound = "")]
23pub struct LoggerConfig<L: LogLevel> {
24 #[serde(default)]
26 #[cfg(std_io)]
27 pub file: Option<PathBuf>,
28
29 #[serde(default = "append_default")]
35 pub append: bool,
36
37 #[serde(default)]
39 pub stdout: bool,
40
41 #[serde(default)]
43 pub stderr: bool,
44
45 #[serde(default)]
47 pub log: Option<LogCrateLevel>,
48
49 #[serde(default)]
51 pub level: L,
52}
53
54impl<L: LogLevel> Default for LoggerConfig<L> {
55 fn default() -> Self {
56 Self {
57 #[cfg(std_io)]
58 file: None,
59 append: true,
60 stdout: false,
61 stderr: false,
62 log: None,
63 level: L::default(),
64 }
65 }
66}
67
68#[derive(
70 Clone, Copy, Debug, Default, serde::Serialize, serde::Deserialize, Hash, PartialEq, Eq,
71)]
72pub enum LogCrateLevel {
73 #[default]
75 #[serde(rename = "info")]
76 Info,
77
78 #[serde(rename = "debug")]
80 Debug,
81
82 #[serde(rename = "trace")]
84 Trace,
85}
86
87fn append_default() -> bool {
88 true
89}
90
91pub trait LogLevel:
93 DeserializeOwned + Serialize + Clone + Copy + core::fmt::Debug + Default
94{
95}
96
97impl LogLevel for u32 {}
98
99#[derive(Debug, Default)]
102pub struct LoggerSinks {
103 loggers: Vec<LoggerKind>,
104 logger2index: HashMap<LoggerId, usize>,
105}
106
107impl LoggerSinks {
108 pub fn new() -> Self {
110 Self::default()
111 }
112
113 pub fn register<L: LogLevel>(&mut self, config: &LoggerConfig<L>) -> Vec<usize> {
118 let mut indices = Vec::new();
119
120 #[cfg(std_io)]
121 if let Some(file) = &config.file {
122 self.insert(&mut indices, LoggerId::File(file.clone()), || {
123 LoggerKind::File(FileLogger::new(file, config.append))
124 });
125 }
126
127 #[cfg(feature = "std")]
128 if config.stdout {
129 self.insert(&mut indices, LoggerId::Stdout, || LoggerKind::Stdout);
130 }
131
132 #[cfg(feature = "std")]
133 if config.stderr {
134 self.insert(&mut indices, LoggerId::Stderr, || LoggerKind::Stderr);
135 }
136
137 if let Some(level) = config.log {
138 self.insert(&mut indices, LoggerId::LogCrate(level), || {
139 LoggerKind::Log(level)
140 });
141 }
142
143 indices
144 }
145
146 pub fn log<S: Display>(&mut self, indices: &[usize], msg: &S) {
148 match indices.len() {
149 0 => {}
150 1 => self.loggers[indices[0]].log(msg),
151 _ => {
152 let msg = msg.to_string();
153 for &index in indices {
154 self.loggers[index].log(&msg);
155 }
156 }
157 }
158 }
159
160 fn insert<F: FnOnce() -> LoggerKind>(
161 &mut self,
162 indices: &mut Vec<usize>,
163 id: LoggerId,
164 make: F,
165 ) {
166 if let Some(index) = self.logger2index.get(&id) {
167 indices.push(*index);
168 } else {
169 let index = self.loggers.len();
170 self.loggers.push(make());
171 self.logger2index.insert(id, index);
172 indices.push(index);
173 }
174 }
175}
176
177#[derive(Debug, Hash, PartialEq, Eq)]
178enum LoggerId {
179 #[cfg(std_io)]
180 File(PathBuf),
181 #[cfg(feature = "std")]
182 Stdout,
183 #[cfg(feature = "std")]
184 Stderr,
185 LogCrate(LogCrateLevel),
186}
187
188#[derive(Debug)]
189enum LoggerKind {
190 #[cfg(std_io)]
191 File(FileLogger),
192 #[cfg(feature = "std")]
193 Stdout,
194 #[cfg(feature = "std")]
195 Stderr,
196 Log(LogCrateLevel),
197}
198
199impl LoggerKind {
200 fn log<S: Display>(&mut self, msg: &S) {
201 match self {
202 #[cfg(std_io)]
203 LoggerKind::File(file_logger) => file_logger.log(msg),
204 #[cfg(feature = "std")]
205 LoggerKind::Stdout => println!("{msg}"),
206 #[cfg(feature = "std")]
207 LoggerKind::Stderr => eprintln!("{msg}"),
208 LoggerKind::Log(level) => match level {
209 LogCrateLevel::Info => log::info!("{msg}"),
210 LogCrateLevel::Debug => log::debug!("{msg}"),
211 LogCrateLevel::Trace => log::trace!("{msg}"),
212 },
213 }
214 }
215}
216
217#[cfg(std_io)]
218#[derive(Debug)]
219struct FileLogger {
220 writer: BufWriter<File>,
221}
222
223#[cfg(std_io)]
224impl FileLogger {
225 fn new(path: &PathBuf, append: bool) -> Self {
226 if let Some(parent) = path.parent() {
227 std::fs::create_dir_all(parent).unwrap();
228 }
229 let file = OpenOptions::new()
230 .write(true)
231 .append(append)
232 .create(true)
233 .open(path)
234 .unwrap();
235
236 Self {
237 writer: BufWriter::new(file),
238 }
239 }
240
241 fn log<S: Display>(&mut self, msg: &S) {
242 writeln!(self.writer, "{msg}").expect("Should be able to log debug information.");
243 self.writer.flush().expect("Can complete write operation.");
244 }
245}