os/log/
log_core.rs

1//! 日志系统核心实现
2//!
3//! 该模块将所有日志状态和逻辑封装到一个单独的 `LogCore` 结构体中,
4//! 可以在保持**无锁、零分配**设计的同时,独立实例化用于测试。
5
6use crate::arch::lib::console::Stdout;
7
8use super::buffer::GlobalLogBuffer;
9use super::config::{DEFAULT_CONSOLE_LEVEL, DEFAULT_LOG_LEVEL};
10use super::context;
11use super::entry::LogEntry;
12use super::level::LogLevel;
13use core::fmt;
14use core::sync::atomic::{AtomicU8, Ordering};
15
16/// 核心日志系统
17///
18/// 封装了环形缓冲区和过滤状态。可以为测试目的而实例化,
19/// 或在生产环境中用作全局单例。
20///
21/// # 线程安全性
22///
23/// 所有方法都使用原子操作进行同步,使得整个结构体在
24/// 线程之间安全共享,无需外部加锁。
25pub struct LogCore {
26    /// 用于日志存储的无锁环形缓冲区
27    buffer: GlobalLogBuffer,
28
29    /// 全局日志级别阈值(控制日志是否缓冲)
30    global_level: AtomicU8,
31
32    /// 控制台输出级别阈值(控制是否立即打印)
33    console_level: AtomicU8,
34}
35
36impl LogCore {
37    /// 使用默认日志级别创建新的 LogCore 实例
38    ///
39    /// 这是一个 `const fn`,可以在编译时进行评估,
40    /// 从而实现零开销的静态初始化。
41    ///
42    /// 使用配置中的默认级别:
43    /// - 全局级别: Info (Debug 级别的日志将被过滤)
44    /// - 控制台级别: Warning (只打印 Warning 和 Error 级别的日志)
45    ///
46    /// # 示例
47    ///
48    /// ```rust
49    /// // 全局单例 (编译时初始化)
50    /// static GLOBAL_LOG: LogCore = LogCore::default();
51    /// ```
52    pub const fn default() -> Self {
53        Self {
54            buffer: GlobalLogBuffer::new(),
55            global_level: AtomicU8::new(DEFAULT_LOG_LEVEL as u8),
56            console_level: AtomicU8::new(DEFAULT_CONSOLE_LEVEL as u8),
57        }
58    }
59
60    /// 使用自定义日志级别创建新的 LogCore 实例
61    ///
62    /// 此构造函数允许在创建时指定全局和控制台日志级别,
63    /// 这对于测试尤其有用。
64    ///
65    /// # 参数
66    ///
67    /// * `global_level` - 日志被缓冲的最低级别
68    /// * `console_level` - 日志被打印到控制台的最低级别
69    ///
70    /// # 示例
71    ///
72    /// ```rust
73    /// // 启用 Debug 级别的测试实例
74    /// let test_log = LogCore::new(LogLevel::Debug, LogLevel::Warning);
75    ///
76    /// // 使用自定义级别的生产实例
77    /// let log = LogCore::new(LogLevel::Info, LogLevel::Error);
78    /// ```
79    pub fn new(global_level: LogLevel, console_level: LogLevel) -> Self {
80        Self {
81            buffer: GlobalLogBuffer::new(),
82            global_level: AtomicU8::new(global_level as u8),
83            console_level: AtomicU8::new(console_level as u8),
84        }
85    }
86
87    /// 核心日志记录实现
88    ///
89    /// 此方法由生产宏(通过 GLOBAL_LOG)和测试代码(通过本地实例)调用。
90    ///
91    /// # 无锁操作
92    ///
93    /// 1. 原子读取 global_level (Acquire)
94    /// 2. 如果被过滤,则提前返回
95    /// 3. 收集上下文 (时间戳、CPU ID、任务 ID)
96    /// 4. 创建日志条目 (栈分配)
97    /// 5. 原子缓冲区写入 (无锁)
98    /// 6. 可选的控制台输出 (如果满足 console_level)
99    ///
100    /// # 参数
101    ///
102    /// * `level` - 日志级别 (Emergency 到 Debug)
103    /// * `args` - 来自 `format_args!` 的格式化参数
104    pub fn _log(&self, level: LogLevel, args: fmt::Arguments) {
105        // 1. 早期过滤 (全局级别)
106        if !self.is_level_enabled(level) {
107            return;
108        }
109
110        // 2. 收集上下文
111        let log_context = context::collect_context();
112
113        // 3. 创建日志条目
114        let entry = LogEntry::from_args(
115            level,
116            log_context.cpu_id,
117            log_context.task_id,
118            log_context.timestamp,
119            args,
120        );
121
122        // 4. 写入缓冲区 (无锁)
123        self.buffer.write(&entry);
124
125        // 5. 可选的即时控制台输出
126        if self.is_console_level(level) {
127            self.direct_print_entry(&entry);
128        }
129    }
130
131    /// 从缓冲区读取下一个日志条目
132    ///
133    /// 如果没有可用条目,则返回 `None`。这是一个**无锁**的
134    /// 单消费者操作。
135    pub fn _read_log(&self) -> Option<LogEntry> {
136        self.buffer.read()
137    }
138
139    /// 非破坏性读取:按索引 peek 日志条目,不移动读指针
140    pub fn _peek_log(&self, index: usize) -> Option<LogEntry> {
141        self.buffer.peek(index)
142    }
143
144    /// 获取当前可读取的起始索引
145    pub fn _log_reader_index(&self) -> usize {
146        self.buffer.reader_index()
147    }
148
149    /// 获取当前写入位置
150    pub fn _log_writer_index(&self) -> usize {
151        self.buffer.writer_index()
152    }
153
154    /// 返回未读日志条目的数量
155    pub fn _log_len(&self) -> usize {
156        self.buffer.len()
157    }
158
159    /// 返回未读日志的总字节数(格式化后)
160    pub fn _log_unread_bytes(&self) -> usize {
161        self.buffer.unread_bytes()
162    }
163
164    /// 返回由于缓冲区溢出而丢弃的日志计数
165    pub fn _log_dropped_count(&self) -> usize {
166        self.buffer.dropped_count()
167    }
168
169    /// 设置全局日志级别阈值
170    ///
171    /// 级别 > 阈值的日志将被丢弃。
172    ///
173    /// # 内存顺序
174    ///
175    /// 使用 Release 顺序以确保新级别对所有核心可见。
176    pub fn _set_global_level(&self, level: LogLevel) {
177        self.global_level.store(level as u8, Ordering::Release);
178    }
179
180    /// 获取当前全局日志级别
181    pub fn _get_global_level(&self) -> LogLevel {
182        let level = self.global_level.load(Ordering::Acquire);
183        LogLevel::from_u8(level)
184    }
185
186    /// 设置控制台输出级别阈值
187    ///
188    /// 只有级别 <= 阈值的日志才会立即打印。
189    pub fn _set_console_level(&self, level: LogLevel) {
190        self.console_level.store(level as u8, Ordering::Release);
191    }
192
193    /// 获取当前控制台输出级别
194    pub fn _get_console_level(&self) -> LogLevel {
195        let level = self.console_level.load(Ordering::Acquire);
196        LogLevel::from_u8(level)
197    }
198
199    // ========== 内部辅助函数 ==========
200
201    /// 检查日志级别是否启用 (全局过滤器)
202    #[inline(always)]
203    fn is_level_enabled(&self, level: LogLevel) -> bool {
204        level as u8 <= self.global_level.load(Ordering::Acquire)
205    }
206
207    /// 检查日志是否应该打印到控制台
208    #[inline(always)]
209    fn is_console_level(&self, level: LogLevel) -> bool {
210        level as u8 <= self.console_level.load(Ordering::Acquire)
211    }
212
213    /// 使用 ANSI 颜色直接将日志条目打印到控制台(无堆分配)
214    ///
215    /// 此方法在早期启动时即可使用,因为它仅使用栈和 core::fmt::Write,
216    /// 不依赖堆分配器。
217    ///
218    /// **重要**: 此函数的格式化逻辑必须与 `format_log_entry` 和 `buffer::calculate_formatted_length` 保持一致。
219    /// 如果修改了日志输出格式,需要同步更新三处:
220    /// - `direct_print_entry` (此函数) - 用于早期启动的控制台输出
221    /// - `format_log_entry` - 用于 syslog 系统调用
222    /// - `buffer::calculate_formatted_length` - 用于精确字节计数
223    fn direct_print_entry(&self, entry: &LogEntry) {
224        use core::fmt::Write;
225
226        let mut stdout = Stdout;
227        // 直接格式化输出,不使用堆分配
228        let _ = write!(
229            stdout,
230            "{}{} [{:12}] [CPU{}/T{:3}] {}{}",
231            entry.level().color_code(),
232            entry.level().as_str(),
233            entry.timestamp(),
234            entry.cpu_id(),
235            entry.task_id(),
236            entry.message(),
237            entry.level().reset_color_code()
238        );
239        let _ = writeln!(stdout);
240    }
241}
242
243// 标记为 Sync 允许在 static 中使用
244unsafe impl Sync for LogCore {}
245
246/// 格式化日志条目为字符串(带 ANSI 颜色和上下文信息)
247///
248/// 将 LogEntry 格式化为用户可读的字符串,用于 syslog 系统调用等场景。
249/// 包含 ANSI 颜色代码、时间戳、CPU ID、任务 ID 等上下文信息。
250///
251/// **注意**:此函数使用堆分配(`alloc::format!`),仅在堆分配器初始化后可用。
252/// 主要用于 syslog 系统调用等运行时场景。早期启动时的控制台输出使用
253/// `direct_print_entry` 方法,该方法不依赖堆分配。
254///
255/// **重要**:此函数的格式化逻辑必须与 `direct_print_entry` 和 `buffer::calculate_formatted_length` 保持一致。
256/// 如果修改了日志输出格式,需要同步更新三处:
257/// - `direct_print_entry` - 用于早期启动的控制台输出(无堆分配)
258/// - `format_log_entry` (此函数) - 用于 syslog 系统调用(使用堆分配)
259/// - `buffer::calculate_formatted_length` - 用于精确字节计数
260///
261/// # 格式
262/// ```
263/// <color_code>[LEVEL] [timestamp] [CPU<id>/T<tid>] message<reset>
264/// ```
265///
266/// # 示例
267/// ```
268/// \x1b[37m[INFO] [      123456] [CPU0/T  1] Kernel initialized\x1b[0m
269/// \x1b[31m[ERR] [      789012] [CPU0/T  5] Failed to mount /dev/sda1\x1b[0m
270/// ```
271///
272/// # 参数
273/// * `entry` - 要格式化的日志条目
274///
275/// # 返回值
276/// 格式化后的字符串(包含 ANSI 颜色代码和上下文信息)
277pub fn format_log_entry(entry: &LogEntry) -> alloc::string::String {
278    use alloc::format;
279
280    format!(
281        "{}{} [{:12}] [CPU{}/T{:3}] {}{}",
282        entry.level().color_code(),
283        entry.level().as_str(),
284        entry.timestamp(),
285        entry.cpu_id(),
286        entry.task_id(),
287        entry.message(),
288        entry.level().reset_color_code()
289    )
290}