summaryrefslogtreecommitdiff
path: root/crates/kernel/src/logger.rs
blob: 7cc3f70ff2f6446c9e8a6d6601912bab008926f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! The logger used to store logs from the kernel. This accepts logs from the `log` crate, and
//! delivers them to console devices.

use core::fmt::{self, Write};

/// Initializes the logger. This should be called exactly once, early in boot.
pub fn init_early() {
    log::set_logger(&Logger).expect("failed to set logger");
    log::set_max_level(log::LevelFilter::Trace);
    log::warn!("Using a bad unportable logger that only works on qemu-virt");
}

struct BadRiscvWrite;

impl Write for BadRiscvWrite {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let ptr = 0x10000000 as *mut u8;
        for b in s.bytes() {
            unsafe { ptr.write_volatile(b) };
        }
        Ok(())
    }
}

pub struct Logger;

impl log::Log for Logger {
    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn log(&self, record: &log::Record) {
        if !self.enabled(record.metadata()) {
            return;
        }

        let body = |line| {
            let level = match record.level() {
                log::Level::Error => "\x1b[1;31mERR\x1b[0m",
                log::Level::Warn => "\x1b[1;33mWRN\x1b[0m",
                log::Level::Info => "\x1b[1;36mINF\x1b[0m",
                log::Level::Debug => "\x1b[1;35mDBG\x1b[0m",
                log::Level::Trace => "TRC",
            };
            let file = record.file().unwrap_or("???");
            let args = record.args();

            let result = if args.as_str() == Some("") {
                // A silly convenience, but don't write the extra space if we're
                // not going to write anything anyways.
                writeln!(BadRiscvWrite, "[{level}][{file}:{line}]")
            } else {
                writeln!(BadRiscvWrite, "[{level}][{file}:{line}] {args}")
            };

            // UNWRAP: Since the fmt::Write impl for ConsoleInner has a
            // contract promising that it won't ever return an error, this
            // should be unreachable.
            result.unwrap();
        };

        // Some contortions to avoid running afoul of the lifetime requirements
        // of format_args...
        match record.line() {
            Some(line) => body(format_args!("{line}")),
            None => body(format_args!("???")),
        }
    }

    fn flush(&self) {}
}