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}