7.20. LOG
7.20.1. 概述
SDK 的打印系统通过 printf/putchar/puts/put_buf 等标准接口,将日志通过串口实时输出。打印功能由多个编译宏协同控制,支持系统打印和用户打印两套独立开关。
主要特性:
多级编译宏精确控制打印开关
系统打印(
printf)与用户打印(user_printf)独立管理支持分模块、分级控制打印输出
支持时间戳输出
7.20.2. 打印配置与接口
宏 |
作用 |
|---|---|
CONFIG_DEBUG_ENABLE |
打印总开关,注释掉则系统打印和用户打印同时关闭 |
CONFIG_SYS_DEBUG_DISABLE |
单独关闭系统打印,不影响用户打印 |
CONFIG_USER_DEBUG_ENABLE |
单独控制用户打印,默认关闭 |
7.20.2.1. 打印总开关
CONFIG_DEBUG_ENABLE定义在 app_config.h 中,是整个打印系统的总开关。注释掉后,系统打印和用户打印同时关闭。
// app_config.h
#define CONFIG_DEBUG_ENABLE // 打印总开关,注释掉则关闭系统打印和用户打印
关闭原理: printf/putchar/puts 等函数在库(.a)中有默认实现。debug.c 和 debug_user.c 中通过条件编译将这些函数重新定义为空函数。链接时 .c 文件中的函数定义会覆盖库中的弱符号实现,从而达到关闭打印的效果。
7.20.2.2. 系统打印
CONFIG_SYS_DEBUG_DISABLE单独控制系统打印,不影响用户打印。定义后即使 CONFIG_DEBUG_ENABLE 已开启,系统打印也会被关闭。
#define CONFIG_SYS_DEBUG_DISABLE // 关闭系统打印
接口:
printf("hello %d\n", val); // 格式化输出
puts("hello"); // 输出字符串并换行
putchar('A'); // 输出单个字符
put_buf(buf, len); // 输出二进制数据
put_u8hex(0xAB); // 十六进制输出
7.20.2.3. 用户打印
CONFIG_USER_DEBUG_ENABLE单独控制用户打印,不影响系统打印。
// app_config.h
#define CONFIG_USER_DEBUG_ENABLE // 开启用户打印
接口:
user_printf("user: %d\n", val);
user_puts("user hello\n");
user_putchar('U');
user_put_buf(buf, len);
例程路径:apps/common/example/system/printf/main.c。
7.20.2.4. 系统打印与用户打印的互斥问题
系统打印(printf)和用户打印(user_printf)底层没有做互斥保护。当两者同时开启时,可能出现以下问题:
打印内容丢失
字符交叉错乱
输出不完整
建议方案:使用 CONFIG_SYS_DEBUG_DISABLE + CONFIG_USER_DEBUG_ENABLE 组合,仅保留一路打印。
7.20.3. 时间戳控制
日志输出中默认附带系统时间戳,格式为 [HH:MM:SS.ms],代表距离系统上电的时间。若不需要时间戳,修改 apps/common/config/log_config/lib_system_config.c 中的全局变量:
// 打印是否时间打印信息
const int config_printf_time = 1; // 改为 0 关闭时间戳
1:每条日志前输出[00:00:00.246]格式的时间戳0:不输出时间戳
7.20.4. 分级日志
debug.h 提供带 TAG的6 级日志过滤机制,支持分模块、分级别独立控制。
7.20.4.1. 日志级别
含义 |
对应宏 |
|---|---|
冗余调试 |
|
调试信息 |
|
基本信息 |
|
警告 |
|
错误 |
|
字符输出 |
|
7.20.4.2. 分级日志开关
7.20.4.2.1. 总开关
LIB_DEBUG 是分级日志的总开关。与 CONFIG_DEBUG_LIB(x) 宏配合使用。
// app_config.h
#if defined CONFIG_RELEASE_ENABLE || TCFG_DEBUG_DLOG_ENABLE
#define LIB_DEBUG 1
#else
#define LIB_DEBUG 0
#endif
#define CONFIG_DEBUG_LIB(x) (x & LIB_DEBUG)
LIB_DEBUG设为 0 时,CONFIG_DEBUG_LIB()始终返回 0,所有层级的分级日志全部关闭printf的打印不受LIB_DEBUG影响。
7.20.4.2.2. 分级开关
模块在 apps/common/config/log_config/*_config.c 下的配置文件中声明了 log_tag_const_* 常量,通过控制对应级别的常量值为TRUE/FALSE来实现分模块、分级开关打印。
// lib_system_config.c 或 app_config.c
const char log_tag_const_v_APP AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(FALSE); // Verbose 关闭
const char log_tag_const_d_APP AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(FALSE); // Debug 关闭
const char log_tag_const_i_APP AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE); // Info 开启
const char log_tag_const_w_APP AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE); // Warn 开启
const char log_tag_const_e_APP AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE); // Error 开启
7.20.4.3. 添加分级打印
开发者需要在自己实现的模块中添加分级打印,可以参考如下代码。
在
apps/common/config/log_config/*_config.c中选择合适的文件添加控制常量,比如:
// apps/common/config/log_config/*_config.c
const char log_tag_const_v_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
const char log_tag_const_d_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
const char log_tag_const_i_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
const char log_tag_const_w_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
const char log_tag_const_e_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
const char log_tag_const_c_TEST AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIB(TRUE);
在模块
.c文件中添加分级打印支持,比如:
// test.c
#define LOG_TAG_CONST TEST // 常量 TAG
#define LOG_TAG "[TEST]" // 字符串 TAG
#define LOG_ERROR_ENABLE // 使能 Error 级别
#define LOG_DEBUG_ENABLE // 使能 Debug 级别
#define LOG_INFO_ENABLE // 使能 Info 级别
#include "debug.h"
void log_test(void)
{
u8 buf[16];
log_verb("I am verb Message.");
log_info("I am info Message.");
log_debug("I am debug Message.");
log_warn("I am warn Message.");
log_error("I am error Message.");
log_char('@');
log_info_hexdump(buf, sizeof(buf));
}
打印输出:
[Verb]: [TEST]I am verb Message.
[Info]: [TEST]I am info Message.
[Debug]: [TEST]I am debug Message.
(warn): <Warn>: [TEST]I am warn Message.
(error): <Error>: [TEST]I am error Message.
@
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7.20.5. 常见问题
系统每分钟定时打印任务栈信息,如何关闭?
答:在
app_config.h屏蔽RTOS_STACK_CHECK_ENABLE如何动态开关打印?
答:可以通过
config_debug_enable全局变量控制,设置config_debug_enable值为 0 关闭打印,为 1 打开打印。注意: 使用打印保存到 TF 卡功能时,动态控制打印会有丢失数据问题。可使用
sd_log_flush()强制将打印 buf 的缓存写入 SD 卡中,之后再使用config_debug_enable = 0关闭打印。如何把打印输出到 TF 卡里面保存成文件?
答:在
debug_user.c中开启宏定义CONFIG_SDCARD_DEBUG_ENABLE。注意: 打印保存到 SD 卡中使用缓存 buf,buf 每满 10 KB 写入一次到 SD 卡中(避免频繁写 SD 卡),不满则不写入。如需查看最后打印,可以使用
sd_log_flush()强制将打印 buf 的缓存写入 SD 卡中;或者在使用动态开关打印时,关打印前强制将缓存写入 SD 卡中。如何把打印输出到云端服务器保存?
答:
在
debug_user.c中开启宏定义CONFIG_NETWORK_DEBUG_ENABLE;打开云串口调试工具,获取 KeyCode 值,并将生成的 KeyCode 值写到
debug_user.c的LOG_KEYCODE中,如:
#define LOG_KEYCODE "KRPNOCNSTGUMSRNL" // 客户随机填写12-16个字符,用于日志查询
开启 wifi,使用 STA 模式,具体可以参考
wifi配置;定时调用
void upload_log_trig(void);函数,具体定时中断可以参考timer配置;在云串口调试工具中查看日志,关于云串口调试工具的使用可以参考
云串口调试工具.pdf。
如何使用 USB 虚拟串口作为打印口?
答:
在
debug_user.c中开启宏定义CONFIG_USB_DEBUG_ENABLE在
app_config.h中增加如下宏定义
#define TCFG_PC_ENABLE 1 #define USB_PC_NO_APP_MODE 2 #define TCFG_USB_SLAVE_ENABLE 1 #define TCFG_UDISK_ENABLE 0 #define USB_DEVICE_CLASS_CONFIG (CDC_CLASS)
注意: 在使用打印输出到 TF 卡、打印输出到云端服务器保存,以及 USB 虚拟串口作为打印口时,需要在
app_config.h中开启宏CONFIG_DEBUG_ENABLE。