日志级别与输出控制:loglevel参数、console_loglevel、default_message_loglevel
内核日志系统,说白了就是内核的「黑匣子」。你想想看,系统崩了、驱动挂了、莫名其妙卡住了,第一反应是什么?肯定是翻日志。但日志怎么控制、怎么过滤、怎么让该出现的出现、不该出现的别刷屏,这里面的门道可不少。
我个人习惯,调内核日志就像调音响——高音、中音、低音得分开控制。内核里提供了三个核心控制点:loglevel 启动参数、console_loglevel 和 default_message_loglevel。搞懂它们,你就能精准控制日志输出。
1. 日志级别的本质:数字越小越严重
内核日志级别用数字 0-7 表示。数字越小,代表事件越严重。我刚开始接触时总搞反,后来记了个口诀:「0 是完蛋,7 是废话」。
| 级别 | 名称 | 含义 | 典型场景 |
|---|---|---|---|
| 0 | KERN_EMERG | 紧急事件 | 系统即将崩溃 |
| 1 | KERN_ALERT | 需要立即处理 | 严重硬件故障 |
| 2 | KERN_CRIT | 临界条件 | 驱动加载失败 |
| 3 | KERN_ERR | 错误 | I/O 操作失败 |
| 4 | KERN_WARNING | 警告 | 资源不足 |
| 5 | KERN_NOTICE | 普通但重要 | 系统启动信息 |
| 6 | KERN_INFO | 信息 | 设备枚举信息 |
| 7 | KERN_DEBUG | 调试信息 | 开发调试用 |
核心原则:只有日志级别 小于等于 控制阈值时,才会输出。比如 console_loglevel=4,那么只有 0-4 的日志能上控制台。
2. console_loglevel:控制台的门卫
console_loglevel 是控制台日志的「总闸门」。它决定了哪些级别的日志能打印到串口、显示器或虚拟终端上。
我在项目中遇到过一个问题:生产环境的服务器,控制台被调试日志刷爆了,导致关键错误信息被淹没。排查了半天,最后发现是 console_loglevel 被设成了 7。
查看当前值很简单:
cat /proc/sys/kernel/printk
# 输出示例:4 4 1 7
# 四个数字依次是:
# console_loglevel
# default_message_loglevel
# minimum_console_loglevel
# default_console_loglevel
修改方式有两种:
- 运行时修改:
echo "3 4 1 7" > /proc/sys/kernel/printk,立即生效,重启失效。 - 启动时设置:在 GRUB 内核命令行加
loglevel=3,永久生效。
我的习惯:开发环境设成 7,方便调试。生产环境设成 3 或 4,只保留错误和更严重的信息。你想想看,线上机器每秒几十万次 I/O,要是每个 INFO 都打印,性能直接崩了。
3. default_message_loglevel:日志的默认标签
这个参数很有意思。它决定了:当代码里没指定日志级别时,默认按什么级别处理。
为什么会这样?很多驱动开发者在写 printk() 时偷懒,不写日志级别前缀。比如:
// 不推荐:没有指定级别
printk("Device initialized\n");
// 推荐:明确指定级别
printk(KERN_INFO "Device initialized\n");
对于第一种写法,内核就用 default_message_loglevel 来补位。默认值是 4(KERN_WARNING)。也就是说,所有偷懒的 printk 都会被当成警告级别处理。
我曾经接手过一个项目,整个驱动代码里全是裸的 printk(),没有级别。结果控制台上全是「警告」,真正的错误反而被淹没了。后来我把 default_message_loglevel 改成 6(KERN_INFO),至少能区分一下。
避坑指南:千万不要依赖默认级别。我曾经见过一个案例,某驱动在调试阶段用裸 printk 打印敏感数据,上线前忘了改,结果默认级别是 4,所有调试信息全打到生产环境控制台上了。嗯,那哥们后来被约谈了。
4. loglevel 启动参数:一锤定音
loglevel 是内核启动参数,它同时设置 console_loglevel 和 default_message_loglevel。说白了,一个参数干两件事。
用法:在 GRUB 配置中,找到 linux 开头的行,末尾加上 loglevel=3。
# /etc/default/grub 示例
GRUB_CMDLINE_LINUX="quiet splash loglevel=3"
# 更新 GRUB 配置
update-grub # Debian/Ubuntu
grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS
这里有个细节:quiet 参数会把 console_loglevel 设为 4,而 loglevel=3 会覆盖它。所以最终效果是控制台只打印 0-3 级别的日志。
5. 三个参数的关系:一张图说清楚
我画了一张流程图,帮你理清这三个参数怎么协同工作:
从这张图能清楚看到:default_message_loglevel 是兜底方案,console_loglevel 是最终过滤。而 loglevel 启动参数,就是一次性设置这两个值的快捷方式。
6. 实战技巧:什么时候调什么
根据我的经验,不同场景下的推荐配置:
| 场景 | console_loglevel | default_message_loglevel | 说明 |
|---|---|---|---|
| 日常开发 | 7 | 6 | 所有日志都看,方便调试 |
| 生产环境 | 3 | 4 | 只保留错误和更严重的信息 |
| 性能压测 | 1 | 3 | 减少日志 I/O 开销 |
| 排查死机 | 7 | 7 | 临时开启,捕获所有信息 |
一个小技巧:如果你不想重启,可以用 echo "7" > /proc/sys/kernel/printk 只改第一个值。排查完再改回去。我经常在客户现场这么干,省得重启服务。
7. 内核代码里的实际用法
看一段真实的内核代码片段,你就明白这些参数怎么用了:
// kernel/printk/printk.c 中的核心逻辑
void vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
// 如果没指定级别,用 default_message_loglevel
if (level == LOGLEVEL_DEFAULT)
level = default_message_loglevel;
// 判断是否要输出到控制台
if (level <= console_loglevel) {
// 写入控制台设备
console_unlock();
}
// 无论如何,都写入日志缓冲区
log_store(facility, level, ...);
}
注意最后一行:不管控制台输不输出,日志缓冲区都会记录。这意味着你用 dmesg 能看到所有日志,但控制台只显示一部分。这个设计很巧妙——既保证了信息不丢失,又避免了控制台被刷爆。
避坑指南:我曾经在生产环境遇到过一个问题:某驱动在错误路径下疯狂打印 KERN_ERR 日志,虽然 console_loglevel=3 没让它们上控制台,但日志缓冲区被撑爆了,导致 dmesg 里全是重复信息,真正的关键日志被冲掉了。后来我加了日志限速 printk_ratelimit() 才解决。
嗯,关于日志级别和输出控制,核心就是这三个参数。搞懂了它们,你就能像调音响一样,精准控制内核日志的「音量」和「音色」。下次遇到日志刷屏或者关键信息丢失,先检查一下 /proc/sys/kernel/printk 里的四个数字,八成能找到问题。
公众号:蓝海资料掘金营,微信deep3321