kernel/io/console/
tracing.rs1use core::fmt::{self, Write};
4
5use alloc::vec::Vec;
6use kernel_user_link::file::{BlockingMode, OpenOptions};
7use tracing::{info, span, Level};
8
9use crate::{
10 cmdline,
11 io::console,
12 sync::{once::OnceLock, spin::mutex::Mutex},
13};
14
15static LOG_FILE: OnceLock<Mutex<LogFile>> = OnceLock::new();
16
17const fn level_str(level: &Level, color: bool) -> &'static str {
18 if color {
19 match *level {
20 Level::TRACE => "\x1b[37mTRACE\x1b[0m",
21 Level::DEBUG => "\x1b[34mDEBUG\x1b[0m",
22 Level::INFO => "\x1b[32mINFO\x1b[0m",
23 Level::WARN => "\x1b[33mWARN\x1b[0m",
24 Level::ERROR => "\x1b[31mERROR\x1b[0m",
25 }
26 } else {
27 match *level {
28 Level::TRACE => "TRACE",
29 Level::DEBUG => "DEBUG",
30 Level::INFO => "INFO",
31 Level::WARN => "WARN",
32 Level::ERROR => "ERROR",
33 }
34 }
35}
36
37fn log_file() -> &'static Mutex<LogFile> {
38 LOG_FILE.get_or_init(|| Mutex::new(LogFile::default()))
39}
40
41pub fn flush_log_file() {
42 if let Some(log_file) = LOG_FILE.try_get() {
43 log_file.lock().flush()
44 }
45}
46
47pub fn shutdown_log_file() {
48 info!("Shutting down log file...");
49 if let Some(log_file) = LOG_FILE.try_get() {
50 log_file.lock().shutdown()
51 }
52}
53
54pub fn init() {
57 tracing::dispatch::set_global_default(tracing::Dispatch::from_static(&CONSOLE_SUBSCRIBER))
58 .unwrap();
59}
60
61pub fn move_to_dynamic_buffer() {
63 log_file().lock().move_to_dynamic_buffer()
64}
65
66static mut INITIAL_BUFFER: [u8; 0x1000] = [0; 0x1000];
67
68enum Buffer {
69 Static {
70 buffer: &'static mut [u8],
71 len: usize,
72 },
73 Dynamic(Vec<u8>),
74}
75
76impl Buffer {
77 fn static_buf() -> Self {
78 Buffer::Static {
79 buffer: unsafe { core::ptr::addr_of_mut!(INITIAL_BUFFER).as_mut().unwrap() },
81 len: 0,
82 }
83 }
84
85 fn move_to_dynamic(&mut self) {
86 match self {
87 Buffer::Static { buffer, len } => {
88 *self = Buffer::Dynamic(buffer[..*len].to_vec());
89 }
90 Buffer::Dynamic(_) => {}
91 }
92 }
93
94 fn is_empty(&self) -> bool {
95 match self {
96 Buffer::Static { len, .. } => *len == 0,
97 Buffer::Dynamic(v) => v.is_empty(),
98 }
99 }
100
101 fn as_bytes(&self) -> &[u8] {
102 match self {
103 Buffer::Static { buffer, len } => &buffer[..*len],
104 Buffer::Dynamic(v) => v,
105 }
106 }
107
108 fn clear(&mut self) {
109 match self {
110 Buffer::Static { len, .. } => *len = 0,
111 Buffer::Dynamic(v) => v.clear(),
112 }
113 }
114
115 fn push_str(&mut self, s: &str) {
116 match self {
117 Buffer::Static { buffer, len } => {
118 if len == &buffer.len() {
119 return;
120 }
121
122 let mut i = *len;
123 let new_buf = s.bytes();
124 let needed_end = i + new_buf.len();
125 let end = if needed_end > buffer.len() {
126 buffer.len()
127 } else {
128 needed_end
129 };
130
131 if end == buffer.len() {
132 println!("\n\nWARN: Buffer is full, cannot write more data");
133 }
134
135 for b in new_buf {
136 buffer[i] = b;
137 i += 1;
138 if i == end {
139 break;
140 }
141 }
142 *len = i;
143 }
144 Buffer::Dynamic(v) => {
145 v.extend_from_slice(s.as_bytes());
146 }
147 }
148 }
149}
150
151struct LogFile {
152 file: Option<crate::fs::File>,
153 shutdown: bool,
154 buffer: Buffer,
155}
156
157impl LogFile {
158 pub fn move_to_dynamic_buffer(&mut self) {
159 self.buffer.move_to_dynamic();
160 }
161
162 pub fn flush(&mut self) {
163 if self.buffer.is_empty() || self.shutdown {
164 return;
165 }
166
167 if self.file.is_none() {
168 let file = crate::fs::File::open_blocking(
169 cmdline::cmdline().log_file,
170 BlockingMode::None,
171 OpenOptions::CREATE | OpenOptions::TRUNCATE | OpenOptions::WRITE,
172 );
173 match file {
175 Err(crate::fs::FileSystemError::FileNotFound) => return,
177 Err(e) => {
178 println!("Failed to open log file: {:?}", e);
179 return;
180 }
181 Ok(f) => {
182 self.file = Some(f);
183 }
184 }
185 }
186
187 {
188 let file = self.file.as_mut().unwrap();
189
190 file.write(self.buffer.as_bytes()).unwrap();
191 file.flush().unwrap();
192 }
193
194 self.buffer.clear();
195 }
196
197 pub fn shutdown(&mut self) {
198 self.flush();
199 self.file = None;
200 self.shutdown = true;
201 }
202}
203
204impl Default for LogFile {
205 fn default() -> Self {
206 LogFile {
207 file: None,
208 shutdown: false,
209 buffer: Buffer::static_buf(),
210 }
211 }
212}
213
214impl Write for LogFile {
215 fn write_str(&mut self, s: &str) -> core::fmt::Result {
216 self.buffer.push_str(s);
217 Ok(())
218 }
219}
220
221struct MultiWriter<'a> {
222 console: &'a mut dyn Write,
223 file: &'a mut LogFile,
224}
225
226impl<'a> Write for MultiWriter<'a> {
227 fn write_str(&mut self, s: &str) -> core::fmt::Result {
228 self.console.write_str(s)?;
229 self.file.write_str(s)?;
230 Ok(())
231 }
232}
233
234static CONSOLE_SUBSCRIBER: ConsoleSubscriber = ConsoleSubscriber;
235
236pub struct ConsoleSubscriber;
237
238impl tracing::Collect for ConsoleSubscriber {
239 fn enabled(&self, metadata: &tracing::Metadata<'_>) -> bool {
240 let max: Level = cmdline::cmdline().max_log_level.into();
241 if metadata.level() > &max {
242 return false;
243 }
244 true
245 }
246
247 fn new_span(&self, _span: &span::Attributes<'_>) -> span::Id {
248 panic!("Spans are not supported")
249 }
250
251 fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {
252 panic!("Spans are not supported")
253 }
254
255 fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
256 panic!("Spans are not supported")
257 }
258
259 fn event(&self, event: &tracing::Event<'_>) {
260 console::run_with_console(|console| {
262 let log_file = &mut log_file().lock();
263
264 console.write_char('[')?;
266 log_file.write_char('[')?;
267 console.write_str(level_str(event.metadata().level(), true))?;
268 log_file.write_str(level_str(event.metadata().level(), false))?;
269
270 let mut writer = MultiWriter {
271 console,
272 file: log_file,
273 };
274 writer.write_str(" ")?;
275 writer.write_str(event.metadata().module_path().unwrap_or("unknown"))?;
276 writer.write_str("] ")?;
277
278 let mut visitor = Visitor::new(&mut writer);
279 event.record(&mut visitor);
280 visitor.finish()?;
281
282 writer.write_char('\n')?;
283
284 Ok::<_, fmt::Error>(())
285 })
286 .unwrap();
287 }
288
289 fn enter(&self, _span: &span::Id) {
290 panic!("Spans are not supported")
291 }
292
293 fn exit(&self, _span: &span::Id) {
294 panic!("Spans are not supported")
295 }
296
297 fn current_span(&self) -> tracing_core::span::Current {
298 tracing_core::span::Current::none()
299 }
300}
301
302struct Visitor<'a> {
303 console: &'a mut dyn Write,
304 result: Option<fmt::Error>,
305 is_first: bool,
306}
307
308impl<'a> Visitor<'a> {
309 fn new(console: &'a mut dyn Write) -> Self {
310 Self {
311 console,
312 result: None,
313 is_first: true,
314 }
315 }
316
317 fn finish(self) -> Result<(), fmt::Error> {
318 self.result.map_or(Ok(()), Err)
319 }
320}
321
322impl tracing::field::Visit for Visitor<'_> {
323 fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn alloc::fmt::Debug) {
324 if self.result.is_some() {
325 return;
326 }
327
328 if !self.is_first {
329 self.result = self.console.write_str(", ").err();
330 }
331
332 self.is_first = false;
333
334 if field.name() != "message" {
335 self.result = self
336 .console
337 .write_str(field.name())
338 .and_then(|_| self.console.write_str(" = "))
339 .err();
340 }
341
342 self.result = self.console.write_fmt(format_args!("{value:?}")).err();
343 }
344}