6.1 日志系统 Dlog
6.1 日志系统 Dlog
📚 本节导读
学习时长: 约 60 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: C 语言宏定义、变参函数、第4章内核组件
🎯 学习目标
- 理解 Dlog 日志系统的设计理念与核心架构
- 掌握四种日志级别(ERROR / WARNING / INFO / DEBUG)及其使用场景
- 理解编译级别与全局输出级别的区别与协同作用
- 掌握 LOG_E / LOG_W / LOG_I / LOG_D / LOG_RAW / LOG_HEX 等日志宏
- 掌握后端(dlog_backend)机制,包括控制台后端和文件系统后端
- 了解异步输出、ISR 安全、运行时过滤、彩色输出等高级特性
- 掌握 Syslog 兼容层的 API 用法
- 能够根据实际需求配置和使用 Dlog
一、概述
Dlog 是 OneOS 内置的日志系统,为嵌入式开发提供了一套功能完备、灵活高效的日志输出框架。其核心设计特点包括:
- 多级别日志输出:支持 ERROR、WARNING、INFO、DEBUG 四种级别,与标准 syslog 优先级兼容
- 多后端支持:内置控制台后端(串口输出)和文件系统后端(文件记录),支持自定义后端扩展
- 异步输出模式:日志先写入缓冲区,由专用任务异步输出,减少对调用任务的阻塞
- ISR 安全:日志输出 API 可在中断服务例程中安全使用
- 运行时过滤:支持 tag 过滤、关键词过滤、tag 级别过滤等多种运行时过滤方式
- 彩色输出:不同级别日志使用不同颜色,便于快速识别
- 编译时裁剪:通过编译级别控制,未使用的日志级别在编译时即被裁剪,不占用代码空间
- Syslog 兼容层:提供标准 POSIX syslog API,方便移植已有代码
Dlog 的源码位于 components/dlog/ 目录,核心头文件为 components/dlog/include/dlog.h。
二、日志级别
2.1 级别定义
Dlog 定义了四种日志级别,数值与标准 syslog 优先级兼容(源码 dlog.h 第38-41行):
#define DLOG_ERROR 3 /* Error conditions */
#define DLOG_WARNING 4 /* Warning conditions */
#define DLOG_INFO 6 /* Informational */
#define DLOG_DEBUG 7 /* Debug-level messages */各级别使用场景:
| 级别 | 数值 | 使用场景 | 示例 |
|---|---|---|---|
DLOG_ERROR | 3 | 系统运行中的错误条件,可能导致功能异常 | 内存分配失败、外设初始化失败 |
DLOG_WARNING | 4 | 潜在问题,不影响当前运行但需关注 | 参数超出推荐范围、重试操作 |
DLOG_INFO | 6 | 正常运行中的关键信息 | 系统启动完成、任务创建成功、网络连接状态 |
DLOG_DEBUG | 7 | 调试信息,用于开发阶段定位问题 | 函数入口/出口、变量值、状态转换 |
注意:级别数值 3、4、6、7 并非连续,这是为了与 syslog 的
LOG_ERR(3)、LOG_WARNING(4)、LOG_INFO(6)、LOG_DEBUG(7) 保持一致。
2.2 两个独立的控制维度
Dlog 使用两个独立的维度来控制日志输出:
(1)编译级别(DLOG_COMPILE_LEVEL)
编译时由 Kconfig 配置,决定哪些级别的日志宏在编译时被保留。级别低于编译级别的日志宏会被展开为空语句,不占用代码空间和 CPU 时间。
// 例如:DLOG_COMPILE_LEVEL = DLOG_INFO(6)
// 则 LOG_E 和 LOG_W 和 LOG_I 会生成实际代码
// 而 LOG_D 会被展开为空(2)全局输出级别(DLOG_GLOBAL_PRINT_LEVEL)
运行时通过 dlog_global_lvl_set() 设置,决定当前哪些级别的日志被实际输出。即使日志宏在编译时被保留,如果其级别低于全局输出级别,也不会输出。
// 运行时设置
dlog_global_lvl_set(DLOG_WARNING); // 只输出 WARNING 及以上级别
dlog_global_lvl_set(DLOG_DEBUG); // 输出所有级别两个维度协同工作,形成"编译时做减法,运行时做开关"的灵活控制策略。
三、核心日志宏详解
3.1 基本日志宏
Dlog 提供了四个基本日志宏(LOG_E/LOG_W/LOG_I/LOG_D),分别对应四种日志级别。每个宏都接受一个 tag 参数标识日志来源模块,并受 DLOG_WITH_FUNC_LINE 与编译级别双重控制。来源:components/dlog/include/dlog.h。
3.1.1 附加函数名/行号时的定义(DLOG_WITH_FUNC_LINE)
当定义了 DLOG_WITH_FUNC_LINE 时,日志宏会自动在输出末尾追加 [函数名][行号]。完整源码:
#ifdef DLOG_WITH_FUNC_LINE
#if (DLOG_ERROR <= DLOG_COMPILE_LEVEL)
#define LOG_E(tag, fmt, ...) \
dlog_output(DLOG_ERROR, tag, OS_TRUE, fmt " [%s][%d]", ##__VA_ARGS__, __FUNCTION__, __LINE__)
#else
#define LOG_E(tag, fmt, ...)
#endif
#if (DLOG_WARNING <= DLOG_COMPILE_LEVEL)
#define LOG_W(tag, fmt, ...) \
dlog_output(DLOG_WARNING, tag, OS_TRUE, fmt " [%s][%d]", ##__VA_ARGS__, __FUNCTION__, __LINE__)
#else
#define LOG_W(tag, fmt, ...)
#endif
#if (DLOG_INFO <= DLOG_COMPILE_LEVEL)
#define LOG_I(tag, fmt, ...) \
dlog_output(DLOG_INFO, tag, OS_TRUE, fmt " [%s][%d]", ##__VA_ARGS__, __FUNCTION__, __LINE__)
#else
#define LOG_I(tag, fmt, ...)
#endif
#if (DLOG_DEBUG <= DLOG_COMPILE_LEVEL)
#define LOG_D(tag, fmt, ...) \
dlog_output(DLOG_DEBUG, tag, OS_TRUE, fmt " [%s][%d]", ##__VA_ARGS__, __FUNCTION__, __LINE__)
#else
#define LOG_D(tag, fmt, ...)
#endif关键理解:每个日志宏都有两层 #if/#else:
- 外层判断
DLOG_xxx <= DLOG_COMPILE_LEVEL:编译级别低于该宏级别时,宏被展开为空(#define LOG_E(...)),在编译时即被裁剪,不产生任何代码; - 内层:级别满足时展开为
dlog_output(...)调用,且因DLOG_WITH_FUNC_LINE定义了,格式串末尾追加" [%s][%d]",并多传入__FUNCTION__、__LINE__两个参数。
3.1.2 不附加函数名/行号时的定义(未定义 DLOG_WITH_FUNC_LINE)
完整源码:
/* Print logs without function name and file line number */
#else /* Not define OS_DEBUG_LOG_WITH_FUNC_LINE */
#if (DLOG_ERROR <= DLOG_COMPILE_LEVEL)
#define LOG_E(tag, fmt, ...) dlog_output(DLOG_ERROR, tag, OS_TRUE, fmt, ##__VA_ARGS__)
#else
#define LOG_E(tag, fmt, ...)
#endif
#if (DLOG_WARNING <= DLOG_COMPILE_LEVEL)
#define LOG_W(tag, fmt, ...) dlog_output(DLOG_WARNING, tag, OS_TRUE, fmt, ##__VA_ARGS__)
#else
#define LOG_W(tag, fmt, ...)
#endif
#if (DLOG_INFO <= DLOG_COMPILE_LEVEL)
#define LOG_I(tag, fmt, ...) dlog_output(DLOG_INFO, tag, OS_TRUE, fmt, ##__VA_ARGS__)
#else
#define LOG_I(tag, fmt, ...)
#endif
#if (DLOG_DEBUG <= DLOG_COMPILE_LEVEL)
#define LOG_D(tag, fmt, ...) dlog_output(DLOG_DEBUG, tag, OS_TRUE, fmt, ##__VA_ARGS__)
#else
#define LOG_D(tag, fmt, ...)
#endif
#endif /* DLOG_WITH_FUNC_LINE */关键理解:未定义 DLOG_WITH_FUNC_LINE 时,宏展开为 dlog_output(level, tag, OS_TRUE, fmt, ##__VA_ARGS__),格式串不加 [%s][%d],也不传函数名/行号。OS_TRUE 表示默认在日志末尾追加换行符。
数值演算(编译裁剪):设 DLOG_COMPILE_LEVEL = DLOG_INFO(6),各宏是否被编译:
| 宏 | 级别值 | 条件 级别 <= 6? | 编译结果 |
|---|---|---|---|
LOG_E | 3 | 3 ≤ 6 是 | 展开为 dlog_output 调用 |
LOG_W | 4 | 4 ≤ 6 是 | 展开为 dlog_output 调用 |
LOG_I | 6 | 6 ≤ 6 是 | 展开为 dlog_output 调用 |
LOG_D | 7 | 7 ≤ 6 否 | 展开为空(#define LOG_D(...)) |
即编译级别为 INFO 时,DEBUG 日志宏(7 > 6)被裁剪为空,代码中即使写了 LOG_D(...) 也不产生任何指令,从而节省代码空间和 CPU 时间。
3.1.3 各宏的用法(以 DLOG_WITH_FUNC_LINE 关闭为例)
| 宏 | 对应级别 | 输出效果 |
|---|---|---|
LOG_E(tag, fmt, ...) | DLOG_ERROR(3) | 输出错误条件 |
LOG_W(tag, fmt, ...) | DLOG_WARNING(4) | 输出警告信息 |
LOG_I(tag, fmt, ...) | DLOG_INFO(6) | 输出信息消息 |
LOG_D(tag, fmt, ...) | DLOG_DEBUG(7) | 输出调试信息 |
3.2 函数名和行号(DLOG_WITH_FUNC_LINE)
通过编译期宏 DLOG_WITH_FUNC_LINE 控制是否在日志中自动附加函数名和行号(源码 dlog.h 第43-100行,即上文 3.1.1/3.1.2 的完整分支):
- 启用
DLOG_WITH_FUNC_LINE:走 3.1.1 分支,格式串追加" [%s][%d]",自动带上__FUNCTION__和__LINE__; - 关闭:走 3.1.2 分支,仅输出用户指定内容。
关键理解:
DLOG_WITH_FUNC_LINE是编译期开关(Kconfig 的DLOG_WITH_FUNC_LINE,见 6.6),一旦开启,所有LOG_E/W/I/D都会附加[函数名][行号]。这不改变日志级别逻辑,只是增加调试定位信息。
3.3 特殊输出宏
(1)LOG_RAW - 原始输出
#define LOG_RAW(fmt, ...) dlog_raw(fmt, ##__VA_ARGS__)不带任何格式修饰(无级别、无 tag、无时间戳),直接将内容输出到所有后端。适用于输出纯文本或特殊格式内容。
(2)LOG_HEX - 十六进制 dump
#define LOG_HEX(tag, width, buf, size) dlog_hexdump(tag, width, buf, size)以十六进制格式输出内存缓冲区内容。width 参数指定每行显示的字节数(通常为 16 或 8)。
关键理解:与
LOG_E/W/I/D不同,LOG_RAW和LOG_HEX不受编译级别裁剪,也无DLOG_WITH_FUNC_LINE分支——它们在源码dlog.h中无条件定义(见 3.1 上方第102-103行)。LOG_RAW内部直接调用dlog_raw,LOG_HEX调用dlog_hexdump。
四、日志后端(dlog_backend)
4.1 后端结构体
Dlog 采用后端(backend)机制,将日志输出与具体输出设备解耦。后端结构体定义如下(源码 dlog.h 第112-128行):
struct dlog_backend
{
os_list_node_t list_node;
char name[OS_NAME_MAX + 1]; /* Name of log backend. */
os_bool_t support_color; /* Whether backend support color. */
os_bool_t support_isr; /* Whether backend support isr. */
void (*init)(struct dlog_backend *backend); /* Initialize backend. */
void (*deinit)(struct dlog_backend *backend); /* De-initialize backend. */
void (*output)(struct dlog_backend *backend, char *log, os_size_t len); /* Output log to backend */
void (*flush)(struct dlog_backend *backend); /* Flush logs in the buffer of backend */
};
typedef struct dlog_backend dlog_backend_t;字段详解:
| 字段 | 类型 | 含义 |
|---|---|---|
list_node | os_list_node_t | 链表节点,用于把后端挂到 Dlog 的全局后端链表(源码 dlog.c 的 gs_dlog_backend_list),每个后端注册时挂入 |
name | char[OS_NAME_MAX+1] | 后端名称(如 "console"、"filesystem") |
support_color | os_bool_t | 是否支持彩色输出(支持则日志带 ANSI 颜色码) |
support_isr | os_bool_t | 是否支持在中断上下文调用(OS_TRUE 才允许 ISR 中输出) |
init | 函数指针 | 初始化后端(注册/启用时调用) |
deinit | 函数指针 | 反初始化后端 |
output | 函数指针 | 核心输出函数,把格式化后的日志写入该后端(控制台/文件等) |
flush | 函数指针 | 刷新后端的日志缓冲区(异步模式下强制立即写出) |
关键理解——后端链表从哪来:list_node 挂的链表头是 dlog.c 中全局静态变量 gs_dlog_ctrl_info(类型 struct dlog_ctrl_info)的成员 backend_list_head——整个系统只有 1 个控制信息结构体,其中就含这条后端链表头。所有已注册的后端通过各自的 list_node 挂入。每次 dlog_output 底层最终调用 dlog_output_to_all_backend,用 os_list_for_each 遍历 backend_list_head,把日志同时发送到所有已注册后端。
4.2 内置后端类型
(1)控制台后端(console_backend)
- 配置项:
DLOG_BACKEND_USING_CONSOLE(默认开启) - 功能:将日志通过控制台(串口)输出
- 特点:简单直接,适合于开发调试阶段
(2)文件系统后端(filesystem_backend)
- 配置项:
DLOG_BACKEND_USING_FILESYSTEM(默认关闭) - 功能:将日志记录到文件系统,支持日志文件的持久化存储
- 特点:自动选择异步输出模式,支持日志文件滚动(按大小和数量限制)
- 相关配置:文件路径(
DLOG_FILE_DIR)、文件名(DLOG_FILE_NAME)、文件大小(DLOG_FILE_SIZE)、文件数量(DLOG_FILE_NUM)、文件缓存(DLOG_FILE_ENABLE_CACHE)
4.3 后端注册与注销
extern os_err_t dlog_backend_register(dlog_backend_t *backend);
extern os_err_t dlog_backend_unregister(dlog_backend_t *backend);注册后的后端会被添加到全局后端链表(gs_dlog_ctrl_info.backend_list_head)中,所有日志输出会同时发送到所有已注册的后端。
五、核心运行时 API
5.1 系统初始化
extern os_err_t dlog_init(void);初始化日志系统,通常在系统启动时调用。该函数会初始化所有已注册的后端。
5.2 底层输出函数
extern void dlog_output(uint16_t level, const char *tag, os_bool_t newline, const char *format, ...);日志系统的核心输出函数,所有日志宏最终都调用此函数。参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
level | uint16_t | 日志级别,如 DLOG_ERROR、DLOG_INFO |
tag | const char * | 日志标签,用于标识模块来源 |
newline | os_bool_t | 是否在末尾追加换行符 |
format | const char * | 格式化字符串,与 printf 兼容 |
... | - | 可变参数 |
extern void dlog_raw(const char *format, ...);原始输出,不带级别、tag、时间戳等格式修饰。
extern void dlog_hexdump(const char *tag, os_size_t width, uint8_t *buf, os_size_t size);十六进制 dump 输出,用于查看内存数据。
5.3 全局级别控制
extern void dlog_global_lvl_set(uint16_t level);
extern uint16_t dlog_global_lvl_get(void);运行时动态调整日志输出级别,控制哪些级别的日志被实际输出。
数值演算(全局输出过滤):设当前全局级别为
DLOG_WARNING(4),调用dlog_output(DLOG_INFO, ...)(级别 6)时,因6 < 4不成立(6 比 4 高),该 INFO 日志不会输出;调用dlog_output(DLOG_ERROR, ...)(级别 3)时,3 < 4成立,输出。即级别数值越小越重要,全局级别只输出"数值 ≤ 当前全局级别"的日志。
5.4 缓冲区刷新
extern void dlog_flush(void);刷新所有后端的日志缓冲区。在异步输出模式下,日志可能暂存在缓冲区中,调用此函数可强制立即输出。
六、Kconfig 配置详解
Dlog 的配置项位于 components/dlog/Kconfig,在 menu "Dlog" 下。
6.1 基础配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
OS_USING_DLOG | bool | y | 启用 Dlog 日志系统 |
DLOG_GLOBAL_PRINT_LEVEL | int | 4(WARNING) | 全局输出级别(运行时) |
DLOG_COMPILE_LEVEL | int | 7(DEBUG) | 编译级别(编译时) |
关键理解:DLOG_GLOBAL_PRINT_LEVEL 和 DLOG_COMPILE_LEVEL 这两个 int 值并非直接配置,而是由两个 choice 单选决定:
DLOG_PRINT_LVL_E/W/I/D(全局输出级别 choice,默认DLOG_PRINT_LVL_W=4)→ 决定DLOG_GLOBAL_PRINT_LEVEL = 3/4/6/7;DLOG_COMPILE_LVL_E/W/I/D(编译级别 choice,默认DLOG_COMPILE_LVL_D=7)→ 决定DLOG_COMPILE_LEVEL = 3/4/6/7。
这两个 choice 正是 2.2"两个独立控制维度"的 Kconfig 实现。
6.2 ISR 安全
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_USING_ISR_LOG | bool | y | 启用 ISR 安全日志,允许在中断中调用日志 API |
6.3 运行时过滤
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_USING_FILTER | bool | y | 启用运行时过滤,支持 tag 过滤、关键词过滤、tag 级别过滤 |
启用后可通过 Shell 命令动态控制过滤规则:
dlog_gtag_ctrl- 全局 tag 控制(仅输出指定 tag 的日志)dlog_gkw_ctrl- 全局关键词控制(仅输出包含指定关键词的日志)dlog_tlvl_ctrl- tag 级别控制(针对特定 tag 设置独立的输出级别)
6.4 异步输出
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_USING_ASYNC_OUTPUT | bool | y | 启用异步输出模式 |
DLOG_ASYNC_OUTPUT_BUF_SIZE | int | 2048 | 异步输出缓冲区大小(字节) |
DLOG_ASYNC_OUTPUT_TASK_STACK_SIZE | int | 2048 | 异步输出任务栈大小(字节) |
DLOG_ASYNC_OUTPUT_TASK_PRIORITY | int | 20(32级) | 异步输出任务优先级 |
6.5 Syslog 兼容
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_USING_SYSLOG | bool | n | 启用 Syslog 兼容层 |
6.6 日志格式
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_OUTPUT_FLOAT | bool | n | 启用浮点数支持(需 libc,会增加栈开销) |
DLOG_WITH_FUNC_LINE | bool | y | 在日志中附加函数名和行号 |
DLOG_USING_COLOR | bool | y | 启用彩色日志输出 |
DLOG_OUTPUT_TIME_INFO | bool | y | 启用时间信息输出 |
DLOG_TIME_USING_TIMESTAMP | bool | n | 使用时间戳格式(需 libc,依赖 DLOG_OUTPUT_TIME_INFO) |
6.7 文件系统后端
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_BACKEND_USING_FILESYSTEM | bool | n | 启用文件系统后端 |
DLOG_FILESYSTEM_FAL_PARTITION_NAME | string | "filesystem" | FAL 分区名称 |
DLOG_FILE_DIR | string | "/dlog/" | 日志文件目录 |
DLOG_FILE_NAME | string | "log.txt" | 日志文件名 |
DLOG_FILE_SIZE | int | 20480 | 单个日志文件大小(字节) |
DLOG_FILE_NUM | int | 5 | 日志文件最大数量 |
DLOG_FILE_ENABLE_CACHE | bool | y | 启用文件缓存(减少 I/O) |
DLOG_FILE_CACHE_BUF_SIZE | int | 1024 | 文件缓存缓冲区大小 |
DLOG_FILESYSTE_INTERNAL_PRINT_LEVEL | int | 1(ERROR) | 文件系统后端内部打印级别(DLOG_FILESYSTE_INTERNAL_PRINT_LVL_* choice 决定) |
七、Syslog 兼容层
Dlog 提供了 POSIX 标准 syslog 兼容层,方便移植使用 syslog 的已有代码。源码位于 components/dlog/include/syslog/syslog.h。
7.1 优先级定义
完整源码:
#define LOG_EMERG 0 /* System is unusable */
#define LOG_ALERT 1 /* Action must be taken immediately */
#define LOG_CRIT 2 /* Critical conditions */
#define LOG_ERR 3 /* Error conditions */
#define LOG_WARNING 4 /* Warning conditions */
#define LOG_NOTICE 5 /* Normal but significant condition */
#define LOG_INFO 6 /* Informational */
#define LOG_DEBUG 7 /* Debug-level messages */7.2 Facility 代码
完整源码:
/* Acility codes */
#define LOG_KERN (0 << 3) /* Kernel messages */
#define LOG_USER (1 << 3) /* Random user-level messages */
#define LOG_MAIL (2 << 3) /* Mail system */
#define LOG_DAEMON (3 << 3) /* System daemons */
#define LOG_AUTH (4 << 3) /* Security/authorization messages */
#define LOG_SYSLOG (5 << 3) /* Messages generated internally by syslogd */
#define LOG_LPR (6 << 3) /* Line printer subsystem */
#define LOG_NEWS (7 << 3) /* Network news subsystem */
#define LOG_UUCP (8 << 3) /* UUCP subsystem */
#define LOG_CRON (9 << 3) /* Clock daemon */
#define LOG_AUTHPRIV (10 << 3) /* Security/authorization messages (private) */
/* Other codes through 15 reserved for system use */
#define LOG_LOCAL0 (16 << 3) /* Reserved for local use */
#define LOG_LOCAL1 (17 << 3) /* Reserved for local use */
#define LOG_LOCAL2 (18 << 3) /* Reserved for local use */
#define LOG_LOCAL3 (19 << 3) /* Reserved for local use */
#define LOG_LOCAL4 (20 << 3) /* Reserved for local use */
#define LOG_LOCAL5 (21 << 3) /* Reserved for local use */
#define LOG_LOCAL6 (22 << 3) /* Reserved for local use */
#define LOG_LOCAL7 (23 << 3) /* Reserved for local use */数值演算(facility 编码):facility 用
(N << 3)编码,即占用优先级之上 3~7 位。LOG_USER = (1 << 3) = 0x08、LOG_DAEMON = (3 << 3) = 0x18、LOG_LOCAL0 = (16 << 3) = 0x80。Dlog 的syslog兼容层在内部处理时会把 facility 与 priority 组合成 syslog 消息。
7.3 API
完整源码(头文件声明):
extern void closelog(void);
extern void openlog(const char *ident, int option, int facility);
extern void syslog(int priority, const char *format, ...);openlog:设置日志标识符(ident)、选项(LOG_PID/LOG_CONS/LOG_NDELAY等)和 facility(LOG_USER/LOG_LOCAL0等)syslog:按指定的priority输出格式化日志(priority为LOG_ERR、LOG_INFO等)closelog:关闭日志连接
八、使用示例
8.1 基本日志输出
#include <dlog.h>
void basic_log_example(void)
{
LOG_E("main", "System initialization failed, error code: %d", -1);
LOG_W("main", "Battery level low: %d%%", 15);
LOG_I("main", "System started successfully, version: %s", "v3.3.1");
LOG_D("main", "Task stack usage: %d/%d bytes", 512, 2048);
}输出示例:
[E/main] System initialization failed, error code: -1 [basic_log_example][42]
[W/main] Battery level low: 15% [basic_log_example][43]
[I/main] System started successfully, version: v3.3.1 [basic_log_example][44]
[D/main] Task stack usage: 512/2048 bytes [basic_log_example][45]8.2 带 tag 的模块日志
#include <dlog.h>
/* 网络模块 */
void network_module(void)
{
LOG_I("net", "Network interface eth0 up");
LOG_D("net", "IP address: 192.168.1.100, netmask: 255.255.255.0");
LOG_W("net", "Packet retransmission count: %d", 3);
LOG_E("net", "Connection timeout, server: %s", "10.0.0.1");
}
/* 传感器模块 */
void sensor_module(void)
{
LOG_I("sensor", "Temperature sensor initialized");
LOG_D("sensor", "Current temperature: %.1f C", 25.5);
LOG_W("sensor", "Temperature exceeds threshold: %.1f C", 85.0);
}8.3 十六进制 dump 示例
#include <dlog.h>
void hex_dump_example(void)
{
uint8_t packet[] = {
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x4F, 0x6E,
0x65, 0x4F, 0x53, 0x00, 0x01, 0x02, 0x03, 0xFF
};
LOG_I("comm", "Received packet:");
LOG_HEX("comm", 16, packet, sizeof(packet));
}8.4 全局级别运行时控制
#include <dlog.h>
void level_control_example(void)
{
/* 开发阶段:输出所有日志 */
dlog_global_lvl_set(DLOG_DEBUG);
LOG_D("test", "This debug message will be output");
LOG_I("test", "This info message will be output");
/* 发布阶段:仅输出警告和错误 */
dlog_global_lvl_set(DLOG_WARNING);
LOG_D("test", "This debug message will NOT be output");
LOG_I("test", "This info message will NOT be output");
LOG_W("test", "This warning message will be output");
LOG_E("test", "This error message will be output");
}8.5 自定义后端注册示例
#include <dlog.h>
/* 自定义后端:输出到自定义存储 */
static void my_output(struct dlog_backend *backend, char *log, os_size_t len)
{
/* 将日志写入自定义存储设备 */
my_storage_write(log, len);
}
static void my_init(struct dlog_backend *backend)
{
/* 初始化自定义存储设备 */
my_storage_init();
}
static void my_flush(struct dlog_backend *backend)
{
/* 刷新存储缓冲区 */
my_storage_flush();
}
static dlog_backend_t my_backend =
{
.name = "my_storage",
.support_color = OS_FALSE,
.support_isr = OS_FALSE,
.init = my_init,
.deinit = OS_NULL,
.output = my_output,
.flush = my_flush,
};
void register_custom_backend(void)
{
dlog_backend_register(&my_backend);
LOG_I("custom", "Custom backend registered: %s", my_backend.name);
}8.6 Syslog 兼容层使用示例
#include <syslog/syslog.h>
void syslog_example(void)
{
/* 打开日志连接 */
openlog("my_app", LOG_PID | LOG_CONS, LOG_USER);
/* 使用 syslog 输出日志 */
syslog(LOG_ERR, "Failed to open device: %s", "/dev/uart0");
syslog(LOG_WARNING, "Disk usage exceeds 80%%");
syslog(LOG_INFO, "Application started successfully");
syslog(LOG_DEBUG, "Debug: variable x = %d", 42);
/* 关闭日志连接 */
closelog();
}📝 本节小结
核心要点回顾
- 四种日志级别:
DLOG_ERROR(3)、DLOG_WARNING(4)、DLOG_INFO(6)、DLOG_DEBUG(7),数值与 syslog 兼容(3/4/6/7 不连续是有意为之)。 - 两级控制:编译级别
DLOG_COMPILE_LEVEL(编译时裁剪,级别 > 编译级别时宏展开为空)与全局输出级别dlog_global_lvl_set(运行时开关),协同实现"编译时做减法、运行时做开关"。 - 基本日志宏:
LOG_E/W/I/D各有两层#if/#else,受DLOG_WITH_FUNC_LINE和编译级别控制;启用DLOG_WITH_FUNC_LINE时自动追加[函数名][行号]。 - 特殊输出宏:
LOG_RAW(原始输出)与LOG_HEX(十六进制 dump)不受编译级别裁剪,无条件定义。 - 后端机制:
dlog_backend_t通过list_node挂到gs_dlog_ctrl_info.backend_list_head,dlog_output遍历链表把日志同时发到所有后端;内置控制台后端和文件系统后端,可自定义扩展。 - 高级特性:异步输出(rbb 环形缓冲 + 专用任务)、ISR 安全(
DLOG_USING_ISR_LOG自旋锁)、运行时过滤(tag/关键词/级别)、彩色输出、时间戳。 - Syslog 兼容:提供 POSIX 标准
openlog/syslog/closelog,支持优先级(LOG_ERR等)与 facility 代码。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| 误以为级别数值 1~7 连续 | 只有 3/4/6/7 四种,为兼容 syslog 而故意不连续 → 用枚举名而非数值 |
| 改了全局输出级别却仍输出 DEBUG | 可能没被编译裁剪,但全局级别过滤了 → 检查 DLOG_GLOBAL_PRINT_LEVEL 与 dlog_global_lvl_set |
| 想裁剪某级别日志只改运行时级别 | 运行时只做"开关",真正省代码要改编译级别 DLOG_COMPILE_LEVEL |
| 在源码中给宏/结构体加中文注释 | 违反"逐字一致"规范 → 英文注释保留,中文解释放正文 |
自定义后端忘记设 support_isr | 中断中调用时会因不支持 ISR 而出错 → 按后端能力正确设置该字段 |
| 后端未注册却期望输出 | 日志只会发给已注册的后端 → 用 dlog_backend_register 先注册 |
以为 LOG_RAW 也带时间戳/tag | LOG_RAW 是纯原始输出,无任何修饰 → 需要格式化的级别日志用 LOG_I 等 |
| 混淆编译级别与全局级别 | 编译级别是 #if 预处理;全局级别是运行时的 gs_dlog_ctrl_info.global_level 判断 |
下一步
- 6.2 SHELL 命令行:了解 OneOS 的 Shell 命令行交互机制,掌握命令注册、参数解析与常见调试命令的使用。