SimpleKernel 1.17.0
Loading...
Searching...
No Matches
kernel_log.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <cpu_io.h>
8#include <etl/format.h>
9
10#include <MPMCQueue.hpp>
11#include <atomic>
12#include <cstdint>
13
14#include "kstd_cstdio"
15
16namespace klog {
17namespace detail {
18
21inline constexpr auto kReset = "\033[0m";
22inline constexpr auto kRed = "\033[31m";
23inline constexpr auto kGreen = "\033[32m";
24inline constexpr auto kYellow = "\033[33m";
25inline constexpr auto kCyan = "\033[36m";
27
29enum class Level : uint8_t {
30 kDebug = 0,
31 kInfo = 1,
32 kWarn = 2,
33 kErr = 3,
34};
35
37inline constexpr auto kMinLevel =
38 static_cast<Level>(SIMPLEKERNEL_MIN_LOG_LEVEL);
39
41inline constexpr const char* kLevelLabel[] = {
42 "DEBUG",
43 "INFO ",
44 "WARN ",
45 "ERROR",
46};
47
49inline constexpr const char* kLevelColor[] = {
50 // 调试
51 kGreen,
52 // 信息
53 kCyan,
54 // 警告
55 kYellow,
56 // 错误
57 kRed,
58};
59
61struct LogEntry {
62 uint64_t seq{0};
63 uint64_t core_id{0};
65 char msg[239]{};
66};
67static_assert(sizeof(LogEntry) == 256, "LogEntry must be 256 bytes");
68
70inline mpmc_queue::MPMCQueue<LogEntry, 256> log_queue;
71
73inline std::atomic<uint64_t> log_seq{0};
74
76inline std::atomic_flag drain_flag = ATOMIC_FLAG_INIT;
77
79inline std::atomic<uint64_t> dropped_count{0};
80
82__always_inline auto PutStr(const char* s) -> void {
83 if (!s) {
84 s = "(null)";
85 }
86 while (*s) {
87 etl_putchar(static_cast<unsigned char>(*s));
88 ++s;
89 }
90}
91
98inline auto TryDrain() -> void {
99 // 非阻塞 try-lock:若其他核心正在排空则立即返回
100 if (drain_flag.test_and_set(std::memory_order_acquire)) {
101 return;
102 }
103
104 // 若有丢弃条目则上报
105 auto dropped = dropped_count.exchange(0, std::memory_order_relaxed);
106 if (dropped > 0) {
107 char drop_buf[64];
108 auto* end = etl::format_to_n(drop_buf, sizeof(drop_buf) - 1,
109 "\033[31m[LOG] dropped {} entries\033[0m\n",
110 static_cast<uint64_t>(dropped));
111 *end = '\0';
112 PutStr(drop_buf);
113 }
114
115 // 排空循环
116 auto printer_core = cpu_io::GetCurrentCoreId();
117 LogEntry entry{};
118 while (log_queue.pop(entry)) {
119 // 格式: [id][core_id1 core_id2 LEVEL] msg
120 char hdr_buf[48];
121 auto* hdr_end =
122 etl::format_to_n(hdr_buf, sizeof(hdr_buf) - 1, "[{}][{} {} {}]",
123 entry.seq, entry.core_id, printer_core,
124 kLevelLabel[static_cast<uint8_t>(entry.level)]);
125 *hdr_end = '\0';
126 PutStr(kLevelColor[static_cast<uint8_t>(entry.level)]);
127 PutStr(hdr_buf);
128 PutStr(entry.msg);
129 PutStr(kReset);
130 PutStr("\n");
131 }
132
133 drain_flag.clear(std::memory_order_release);
134}
135
142template <Level Lvl, typename... Args>
143__always_inline auto Log(etl::format_string<Args...> fmt, Args&&... args)
144 -> void {
145 if constexpr (Lvl < kMinLevel) {
146 return;
147 }
148
149 LogEntry entry{};
150 entry.seq = log_seq.fetch_add(1, std::memory_order_relaxed);
151 entry.core_id = cpu_io::GetCurrentCoreId();
152 entry.level = Lvl;
153 auto* end = etl::format_to_n(entry.msg, sizeof(entry.msg) - 1, fmt,
154 static_cast<Args&&>(args)...);
155 *end = '\0';
156
157 if (!log_queue.push(entry)) {
158 // 队列满:尝试排空后重试一次
159 TryDrain();
160 if (!log_queue.push(entry)) {
161 dropped_count.fetch_add(1, std::memory_order_relaxed);
162 return;
163 }
164 }
165
166 TryDrain();
167}
168
169} // namespace detail
170
172template <typename... Args>
173inline auto Debug(etl::format_string<Args...> fmt, Args&&... args) -> void {
175 return;
176 }
177 detail::Log<detail::Level::kDebug>(fmt, static_cast<Args&&>(args)...);
178}
179
181template <typename... Args>
182inline auto Info(etl::format_string<Args...> fmt, Args&&... args) -> void {
183 if constexpr (detail::Level::kInfo < detail::kMinLevel) {
184 return;
185 }
186 detail::Log<detail::Level::kInfo>(fmt, static_cast<Args&&>(args)...);
187}
188
190template <typename... Args>
191inline auto Warn(etl::format_string<Args...> fmt, Args&&... args) -> void {
192 if constexpr (detail::Level::kWarn < detail::kMinLevel) {
193 return;
194 }
195 detail::Log<detail::Level::kWarn>(fmt, static_cast<Args&&>(args)...);
196}
197
199template <typename... Args>
200inline auto Err(etl::format_string<Args...> fmt, Args&&... args) -> void {
201 if constexpr (detail::Level::kErr < detail::kMinLevel) {
202 return;
203 }
204 detail::Log<detail::Level::kErr>(fmt, static_cast<Args&&>(args)...);
205}
206
208__always_inline auto Flush() -> void { detail::TryDrain(); }
209
211__always_inline auto RawPut(const char* msg) -> void { detail::PutStr(msg); }
212
213} // namespace klog
auto etl_putchar(int c) -> void
早期控制台字符输出
void * end[]
内核结束
auto GetCurrentCoreId() -> size_t
Definition cpu_io.h:26
std::atomic_flag drain_flag
单消费者排空保护(非阻塞 try-lock)
__always_inline auto Log(etl::format_string< Args... > fmt, Args &&... args) -> void
核心实现:格式化消息并入队,随后尝试排空
constexpr auto kReset
constexpr auto kYellow
std::atomic< uint64_t > log_seq
用于跨核心排序的单调递增序列计数器
constexpr auto kGreen
Level
日志级别
constexpr auto kCyan
constexpr auto kRed
constexpr const char * kLevelLabel[]
日志级别标签(定宽,对齐输出)
mpmc_queue::MPMCQueue< LogEntry, 256 > log_queue
全局日志队列(64 KB 静态内存,constexpr 构造)
constexpr auto kMinLevel
编译期最低日志级别
__always_inline auto PutStr(const char *s) -> void
通过 etl_putchar 输出字符串(空指针安全)
auto TryDrain() -> void
将队列中所有条目输出至串口
constexpr const char * kLevelColor[]
日志级别对应的颜色(按 Level 枚举索引)
std::atomic< uint64_t > dropped_count
队列满时丢弃的条目计数
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
__always_inline auto RawPut(const char *msg) -> void
绕过队列直接输出至串口(用于 panic 路径)
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
__always_inline auto Flush() -> void
强制将队列中所有日志条目输出至串口
auto Debug(etl::format_string< Args... > fmt, Args &&... args) -> void
以 DEBUG 级别记录日志(SIMPLEKERNEL_MIN_LOG_LEVEL > 0 时编译期消除)
auto Warn(etl::format_string< Args... > fmt, Args &&... args) -> void
以 WARN 级别记录日志
存储于 MPMC 队列中的日志条目