日志系统使用指南

本页依据 文档基线与后续更新 中记录的 Zephyr main 快照更新。

Zephyr 日志系统提供模块/实例标识、严重等级、过滤、延后处理和多个后端。 公共头文件是 <zephyr/logging/log.h>,旧 <logging/log.h> 路径需要迁移。

模块日志

在 prj.conf 中配置:

CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_LOG_MODE_DEFERRED=y

然后在一个源文件中注册模块:

#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(sensor_app, LOG_LEVEL_INF);

void log_measurement(int value)
{
    LOG_INF("measurement=%d", value);
    if (value < 0) {
        LOG_WRN("unexpected value");
    }
}

等级数值是:0 关闭,1 ERR,2 WRN,3 INF,4 DBG。 LOG_ERR/WRN/INF/DBG 使用对应等级,LOG_HEXDUMP_* 输出二进制数据。 格式与参数类型需匹配,例如 size_t 使用 %zu,指针使用 %p 并转换为 void *。

同一模块跨多个源文件时,只有一个文件 LOG_MODULE_REGISTER,其余文件使用 LOG_MODULE_DECLARE,并保持编译等级一致。 不要把注册宏放在会被多个源文件包含的普通头文件中。

配置模块等级

使用 Kconfig 模板创建应用自己的日志等级:

mainmenu "Sensor application"
source "Kconfig.zephyr"

module = SENSOR_APP
module-str = sensor application
source "subsys/logging/Kconfig.template.log_config"

随后以 CONFIG_SENSOR_APP_LOG_LEVEL 注册模块,并在 prj.conf 中选择模板生成的 等级选项,例如 CONFIG_SENSOR_APP_LOG_LEVEL_DBG=y。 全局 CONFIG_LOG_DEFAULT_LEVEL 本身是整数,不存在可通用套用的 CONFIG_LOG_DEFAULT_LEVEL_DBG=y 配置。

实例日志

同一模块有多个实例时,可使用 LOG_INSTANCE_* 按实例过滤:

#include <zephyr/logging/log.h>
#include <zephyr/logging/log_instance.h>

LOG_LEVEL_SET(LOG_LEVEL_INF);

struct sensor {
    LOG_INSTANCE_PTR_DECLARE(log);
    unsigned int id;
};

LOG_INSTANCE_REGISTER(sensor, first, LOG_LEVEL_INF);
static struct sensor first_sensor = {
    LOG_INSTANCE_PTR_INIT(log, sensor, first)
    .id = 1,
};

void report_sensor(void)
{
    LOG_INST_INF(first_sensor.log, "id=%u", first_sensor.id);
}

相关宏处理日志禁用时的字段裁剪;不要绕过宏直接假设实例字段始终存在。

编译过滤与运行过滤

编译期过滤决定哪些语句进入镜像;运行时过滤不能恢复已经在编译期删除的日志。 CONFIG_LOG_MAX_LEVEL、CONFIG_LOG_OVERRIDE_LEVEL 和模块等级共同影响编译结果。

启用 CONFIG_LOG_RUNTIME_FILTERING=y 后,可通过日志控制接口对源和后端调整运行等级。 有多个后端时,每个后端可采用自己的过滤等级。 结合 Shell 的日志命令可查看状态和调整过滤,具体语法先查看设备上的 log --help。

处理模式与字符串

  • CONFIG_LOG_MODE_DEFERRED:前端打包,后续由处理线程或应用处理;常用于减少调用点耗时。

  • CONFIG_LOG_MODE_IMMEDIATE:调用点同步处理,耗时包括格式化与输出。

  • CONFIG_LOG_MODE_MINIMAL:以较少资源提供功能受限的日志。

延后模式需要有效的处理机制。若关闭内部日志处理线程,应按 <zephyr/logging/log_ctrl.h> 中的接口驱动处理,不能只写日志却从不消费。 CONFIG_LOG_BUFFER_SIZE、触发阈值、处理线程栈和输出速率共同影响是否丢日志。

当前日志实现内建对瞬态字符串参数的打包/复制机制,不应继续套用旧版日志系统的 log_strdup() 教程。自定义 cbprintf package、后端或前端时,仍应明确其格式字符串、 参数和载荷生命周期,见 cbprintf 与格式化打包。

后端、限流与 panic

UART、RTT、网络等后端需要对应设备和配置。CONFIG_LOG_PRINTK 可把 printk 导入日志, 延后模式下输出时间也会随之延后。

当前日志提供 LOG_*_RATELIMIT 及指定间隔的变体,适合重复错误或高频状态。 它们受 CONFIG_LOG_RATELIMIT 和关闭时 fallback 策略控制, 不能假设关闭限流功能后总会退化成普通日志。

log_panic() 切换到 panic 处理用于故障场景。正常应用仍应通过合理等级、缓冲区容量 和后端吞吐控制负载;输出日志会影响执行时间,不能仅凭“延后模式”认为没有实时性成本。

上游依据