Logger 组件
概述
lisa_log 是 ARCS SDK 的日志管理组件,提供统一的日志输出接口。该组件基于 EasyLogger 库实现,当前通过 UART 后端输出日志到串口,支持异步输出、多级别过滤等特性。
主要特性
基于 EasyLogger:使用成熟的 EasyLogger 库进行日志格式化和管理
UART 串口输出:通过 syslog 将日志输出到 UART 串口
异步日志输出:支持异步模式,避免阻塞应用程序,提高系统性能
日志级别控制:支持多级别日志过滤(ERROR、WARN、INFO、DEBUG、VERBOSE)
彩色输出:支持终端彩色日志输出,便于区分不同级别
线程安全:支持多任务环境下的并发日志输出
灵活配置:通过
prj.conf文件提供丰富的配置选项
注意:当前版本仅支持 UART 后端,未来版本可能会支持其他后端类型(如文件、网络等)。
快速开始
统计日志格式字符串
启用 EasyLogger 时,LOGE/W/I/D/V 和 LISA_LOGE/W/I/D/V 的常量格式串分别进入
.logstr.error、.logstr.warn、.logstr.info、.logstr.debug、
.logstr.verbose。LISA_LOGT 归入 debug,LISA_LOG 归入 info。
HAL 的 CLOGE/W/I/D/V 和 TASK_CLOGE/W/I/D/V 按等级计入独立的
.clogstr.error/warn/info/debug/verbose 段;无等级的 CLOG 计入
.clogstr.raw,ARCS 的 CLOGN 计入 .clogstr.none。CLOG 格式串包含原有
等级前缀与换行符;其运行时 cloglvl 判断不会在编译期剔除格式串。
链接完成后,在仓库根目录执行:
sh arcs-sdk/system/log/log-size.sh build/<project-name>
结果是最终 ELF 中各等级格式串的字节数(含字符串结尾的 NUL)。日志宏的格式参数须为
字符串字面量或展开为字面量的宏;动态格式参数需直接调用对应的 elog_* 或
logDbg 接口,不参与统计。
统计也不包含日志 TAG、printf/printk 和预编译库中的字符串,
不代表全部日志相关代码和数据大小。
应使用当前源码重新链接后的 ELF;旧 ELF 不能用于本次统计。
可选字典模式
默认仍使用文本日志。要使 LOG*、LISA_LOG*、CLOG*、TASK_CLOG* 的
调用点格式串只保留在 ELF,并让固件输出二进制帧,可在应用配置中启用:
CONFIG_LOG=y
CONFIG_LOG_DICTIONARY=y
CONFIG_LOG_DICTIONARY_PACKET_BUF_SIZE=4352
数据包中的来源字段由设备运行时填写为 core_id,不需要为每个镜像单独配置编号。
关闭 CONFIG_LOG_DICTIONARY 即回到原文本宏及原 .logstr/.clogstr 统计段。
字典模式把格式串链接到 CONFIG_LOG_DICTIONARY_TOKEN_BASE 指定的虚拟 .logdict 地址;
该地址只用作 token,
设备代码不得解引用。构建生成的固件 .bin 和 .hex 会排除该段,ELF 保留它,
同时自动导出 <构建目录>/<目标名>.logdict.bin 供上位机解析。
log-size.sh 在此模式下追加 DICT ELF_ONLY,表示 ELF 中被排除的字典字节数,
不能直接当作最终 Flash 净节省。
可以用以下命令导出地址字典或解码保存的原始串口字节流:
python3 arcs-sdk/tools/log/log-dictionary-decode.py build/snap-learn.logdict.bin --export /tmp/log-dictionary.json
python3 arcs-sdk/tools/log/log-dictionary-decode.py build/snap-learn.logdict.bin /tmp/serial.raw
也可直接从串口实时采集、解码并追加到明文日志文件(需要 pyserial):
python3 arcs-sdk/tools/log/log-dictionary-decode.py build/snap-learn.logdict.bin \
--port /dev/ttyACM0 --baudrate 921600 --output /tmp/snap-learn.log
终端同步显示按等级着色的明文,按 Ctrl+C 停止;--output 文件始终不含颜色码。
默认开启终端颜色,可用 --no-color 关闭,也可用 --color 显式开启;普通文本日志原样显示。
可追加
--raw-output /tmp/snap-learn.raw 同时保存原始串口字节。必须使用与设备固件
同一次构建的字典文件或 ELF,且串口不能同时被其他终端程序占用。
串口接收独立于终端显示;当显示速度跟不上时,脚本会在终端和明文日志中报告
丢失的实时解码字节。指定 --raw-output 后,原始数据仍会继续保存,可供离线重解码。
串口采集默认逐字符转发终端输入,换行转换为 \r\n;只抓日志时可加
--no-shell 关闭输入。--shell 仍可显式指定。
需要只查看某个核的日志时追加 --core-id <编号>;默认接收所有核的帧。
字典文件仍必须与产生日志的固件构建匹配。
.logdict.bin 自带字典基地址和 payload 长度,上位机不需要内置基地址。格式地址在帧中
编码为 fmt_addr - CONFIG_LOG_DICTIONARY_TOKEN_BASE 的 20 位偏移,最多支持 1 MiB,
其余 12 位保留为 0。帧使用 D7 4C 魔数、版本、元数据字节、设备时间、长度、
32 位格式偏移、TAG、参数和可选 CRC16-CCITT,解码器能在混合文本流中寻找完整帧。
包长由帧头的 16 位字段提供,解码脚本无需同步固件的包缓冲区配置。
版本 4 和 5 的元数据字节从低位到高位依次为:日志等级 3 bit、core ID 2 bit、时间格式 1 bit、
CRC 使能 1 bit、字符串复制 1 bit。时间格式为 0 时设备时间是运行毫秒数,为 1 时是 Unix
时间戳的低 32 位 Unix 毫秒值。解码器按主机当前时间补齐回绕高位,并按 HH:MM:SS.mmm 显示 UTC 时间。CRC 和字符串复制均可通过 CONFIG_LOG_DICTIONARY_CRC、
CONFIG_LOG_DICTIONARY_STRING_COPY 独立关闭。上位机明文前缀按时间、等级、TAG、
核编号排列。运行时间和 TAG 分别占 13、16 字符,过长的 TAG 在显示时以 ~ 标记截断;
Unix 时间显示为 UTC 日期时间,前缀会比运行时间日志更长。
旧版帧无时间时显示 -。版本 2 的每个参数固定为一个 32 位字;宏只展开为一次发送函数调用,
不在调用点创建参数数组。解码器
根据 ELF 中的格式串解释参数,不在固件中保存参数类型。float 保留 32 位
位型,double 缩为 float;64 位整数仅保留低 32 位,上位机标注截断。
版本 5 帧对 char*/const char* 参数在调用时最多复制 4096 字节,字符串长度使用
16 bit 编码,最高位表示截断;超出当前包容量的部分在上位机标注 [truncated]。
普通任务使用 CONFIG_LOG_DICTIONARY_PACKET_BUF_SIZE 字节的共享 PSRAM 缓冲区,
调度器未运行、中断、临界区、panic 或共享缓冲区被占用时使用 512 字节栈缓冲区,
因此可能更早截断。
默认容量 4352 字节足以容纳单个 4096 字节字符串及最大参数和 TAG;多个长字符串
共享同一包容量。异步日志缓冲区空间不足时会整帧丢弃;使用异步模式时,
CONFIG_LOG_ASYNC_BUF_SIZE 应大于字典包缓冲区容量,否则最大尺寸的包无法入队。
旧版 3/4 帧的字符串复制格式仍可解码。空指针显示 (null)。调用点传入的字符串
必须在函数调用期间可读且以 NUL 结尾;%.*s 的精度不会限制
设备端复制长度,不能用它传入未终止的缓冲区。类型识别无法区分以 char* 传给 %p
的情况,此时也会尝试读取字符串;这类指针应转为 void*。其他类型的 %s
参数仍显示指针值。版本 2 旧帧也继续按指针显示。
TAG 最多 31 字节,单次调用最多 32 个参数,long double 不支持。解码器仍
支持旧版含类型信息的版本 1 帧。
此配置只覆盖上述宏;直接 elog_*、logDbg、printf、printk、断言和
十六进制 dump 仍以文本输出,它们的字符串及作为参数传入的字符串字面量仍可能
占用 Flash。初始化前的字典日志目前无法通过常规后端输出;启用后应确认启动期
日志需求。上位机对不支持的 printf 转换会显示原格式与参数,不会伪造还原文本。
需要用目标设备检查串口传输、异步缓冲溢出、滤波与最坏栈占用,再决定是否在
生产固件启用。
1. 启用日志组件
在项目的 prj.conf 文件中添加以下配置:
CONFIG_LOG=y
2. 基本使用
// 重要:LOG_TAG 必须在包含头文件之前定义
#define LOG_TAG "MyApp"
#include "lisa_log.h"
void app_main(void) {
// 日志系统已在启动时自动初始化,直接使用即可
LOGI("Application started, version: %s", "1.0.0");
LOGD("Debug mode enabled");
LOGW("This is a warning message");
LOGE("Error code: %d", -1);
}
配置选项
在项目的 prj.conf 文件中配置日志组件的各项参数。
1. 启用日志组件
配置项 |
说明 |
默认值 |
|---|---|---|
|
使能日志组件 |
n |
在 prj.conf 中添加:
CONFIG_LOG=y
启用后,系统会自动使用 EasyLogger 作为日志前端,UART 作为日志后端。
2. 日志级别
日志级别从严重到详细依次为:
级别 |
配置项 |
说明 |
使用场景 |
|---|---|---|---|
ASSERT |
|
断言级别 |
致命错误,程序无法继续运行 |
ERROR |
|
错误级别 |
严重错误,功能受影响 |
WARN |
|
警告级别 |
潜在问题,不影响正常运行 |
INFO |
|
信息级别(默认) |
一般信息,程序运行状态 |
DEBUG |
|
调试级别 |
调试信息,开发阶段使用 |
VERBOSE |
|
详细级别 |
最详细的日志,包含所有信息 |
配置方式:
在 prj.conf 中选择一个日志级别(只能选择一个):
# 选择信息级别(默认,推荐生产环境)
CONFIG_LOG_LEVEL_INFO=y
# 或者选择其他级别(根据需要)
# CONFIG_LOG_LEVEL_ASSERT=y
# CONFIG_LOG_LEVEL_ERROR=y
# CONFIG_LOG_LEVEL_WARN=y
# CONFIG_LOG_LEVEL_DEBUG=y # 开发调试
# CONFIG_LOG_LEVEL_VERBOSE=y # 最详细
说明:
选择某个级别后,该级别及以上的日志都会被输出
例如选择
CONFIG_LOG_LEVEL_INFO=y,则 ASSERT、ERROR、WARN、INFO 级别的日志会输出,DEBUG 和 VERBOSE 不会输出建议生产环境使用
INFO或WARN,开发调试时使用DEBUG或VERBOSE
3. 异步模式配置
异步模式可以避免日志输出阻塞应用程序,提高系统响应性能。
配置项 |
说明 |
默认值 |
调整建议 |
|---|---|---|---|
|
使能异步模式 |
y |
建议保持开启 |
|
异步缓冲区大小(字节) |
10240 |
日志量大时可增加 |
|
异步任务栈大小(字节) |
512 |
栈溢出时增加 |
|
异步任务优先级 |
1 |
根据系统调度需求调整 |
配置方式:
在 prj.conf 中添加(使用默认值):
CONFIG_LOG_MODE_ASYNC=y
或者自定义异步模式参数:
CONFIG_LOG_MODE_ASYNC=y
CONFIG_LOG_ASYNC_BUF_SIZE=20480 # 增加缓冲区到 20KB
CONFIG_LOG_ASYNC_TASK_STACK_SIZE=1024 # 增加栈大小到 1KB
CONFIG_LOG_ASYNC_TASK_PRIORITY=3 # 提高任务优先级
如需使用同步模式(不推荐):
# CONFIG_LOG_MODE_ASYNC=n # 注释掉或设为 n
工作原理:
应用程序调用
LOGI()等宏时,日志数据会先写入缓冲区,立即返回后台任务从缓冲区读取日志数据,通过 UART 串口输出
缓冲区满时,新的日志会被丢弃(建议增加缓冲区大小)
异步模式 vs 同步模式:
模式 |
优点 |
缺点 |
适用场景 |
|---|---|---|---|
异步(推荐) |
不阻塞应用,性能高 |
可能丢失日志(缓冲区满) |
一般应用场景 |
同步 |
不丢失日志 |
阻塞应用,性能低 |
调试关键问题 |
4. 高级配置
配置项 |
说明 |
默认值 |
调整建议 |
|---|---|---|---|
|
单行日志缓冲区大小(字节) |
1024 |
单条日志过长时增加 |
|
使用 POSIX 时间戳 |
n |
需要精确时间时启用 |
|
UART 后端名称 |
“sys.log” |
一般不需要修改 |
配置方式:
在 prj.conf 中添加(可选):
# 增加单行日志缓冲区大小
CONFIG_LOG_LINE_BUF_SIZE=2048
# 启用 POSIX 时间戳(需要系统支持)
CONFIG_LOG_USE_POSIX_TIME=y
# 修改 UART 后端名称(一般不需要)
CONFIG_LOG_BACKEND_SYS_NAME="sys.log"
5. UART 后端配置(syslog)
日志组件通过系统 syslog 接口将日志输出到 UART 串口。syslog 的 UART 配置需要在 prj.conf 中设置。
5.1 选择 UART 设备
在 prj.conf 中选择用于日志输出的 UART 设备(只能选择一个):
# 选择 UART0(默认)
CONFIG_SYSLOG_UART_DEVICE_UART0=y
# 或选择 UART1
# CONFIG_SYSLOG_UART_DEVICE_UART1=y
# 或选择 UART2
# CONFIG_SYSLOG_UART_DEVICE_UART2=y
5.2 配置波特率
在 prj.conf 中设置 UART 波特率:
# 设置波特率(默认 921600)
CONFIG_SYSLOG_UART_BAUDRATE=921600
# 常用波特率选项:
# CONFIG_SYSLOG_UART_BAUDRATE=115200
# CONFIG_SYSLOG_UART_BAUDRATE=460800
# CONFIG_SYSLOG_UART_BAUDRATE=921600
说明:
波特率越高,日志输出越快,但对硬件和连接质量要求越高
建议使用 921600 或 115200
确保串口工具的波特率设置与此配置一致
6. printf 和 printk 输出配置
系统提供了 printf 和 printk 两种标准输出函数,可以配置它们的输出行为。
6.1 默认输出行为
不启用重定向时(默认):
函数 |
默认输出 |
说明 |
|---|---|---|
|
UART(syslog) |
通过 syslog 直接立即输出到 UART |
|
UART(syslog) |
通过 syslog 直接立即输出到 UART |
// 默认情况下,printf 和 printk 都直接同步输出到 UART
printf("Hello from printf\n"); // 同步到 UART
printk("Hello from printk\n"); // 同步到 UART
6.2 重定向到日志系统
可以将 printf 和 printk 重定向到日志系统(lisa_log),由日志系统决定后续行为。
配置方式:
在 prj.conf 中添加:
# 将 printk 重定向到日志系统
CONFIG_SYSLOG_PRINTK_REDIRECT=y
# 将 printf 重定向到日志系统
CONFIG_SYSLOG_PRINTF_REDIRECT=y
启用重定向后的行为:
函数 |
重定向后输出 |
说明 |
|---|---|---|
|
日志系统 |
经过 lisa_log 格式化,带时间戳、标签等 |
|
日志系统 |
经过 lisa_log 格式化,带时间戳、标签等 |
// 启用重定向后,printf 和 printk 会经过日志系统处理
printf("Hello from printf\n"); // 格式化输出:[I/TAG] Hello from printf
printk("Hello from printk\n"); // 格式化输出:[I/TAG] Hello from printk
使用建议:
场景 |
推荐配置 |
原因 |
|---|---|---|
调试第三方库 |
不重定向(默认) |
第三方库的 printf 直接输出,不受日志级别影响 |
统一日志管理 |
启用重定向 |
所有输出都经过日志系统,便于过滤和管理 |
高性能应用 |
不重定向(默认) |
避免日志系统开销 |
6.3 使用日志宏 vs printf/printk
推荐使用日志宏(LOGI、LOGD 等)而不是 printf/printk:
// ✅ 推荐:使用日志宏
#define LOG_TAG "MyApp"
#include "lisa_log.h"
LOGI("Application started"); // 带日志级别、标签、时间戳
LOGD("Debug value: %d", value); // 可以通过级别过滤
// ❌ 不推荐:使用 printf/printk
printf("Application started\n"); // 无日志级别,无法过滤
printk("Debug value: %d\n", value); // 无法通过日志级别控制
日志宏的优势:
支持日志级别过滤
自动添加时间戳、文件名、行号等信息
支持编译期优化(低级别日志可被优化掉)
统一的输出格式
7. 完整配置示例
以下是典型的 prj.conf 配置示例,包含日志组件和 syslog 配置。
开发调试配置(详细日志,高波特率,大缓冲区):
# ========== 日志组件配置 ==========
# 启用日志组件
CONFIG_LOG=y
# 日志级别:调试模式
CONFIG_LOG_LEVEL_DEBUG=y
# 异步模式
CONFIG_LOG_MODE_ASYNC=y
CONFIG_LOG_ASYNC_BUF_SIZE=20480
CONFIG_LOG_ASYNC_TASK_STACK_SIZE=1024
CONFIG_LOG_ASYNC_TASK_PRIORITY=2
# 行缓冲区
CONFIG_LOG_LINE_BUF_SIZE=2048
# ========== syslog UART 配置 ==========
# UART 设备选择
CONFIG_SYSLOG_UART_DEVICE_UART0=y
# 波特率(高速输出)
CONFIG_SYSLOG_UART_BAUDRATE=921600
# printf/printk 重定向(统一日志管理)
CONFIG_SYSLOG_PRINTF_REDIRECT=y
CONFIG_SYSLOG_PRINTK_REDIRECT=y
生产环境配置(精简日志,标准波特率,小内存占用):
# ========== 日志组件配置 ==========
# 启用日志组件
CONFIG_LOG=y
# 日志级别:仅显示警告和错误
CONFIG_LOG_LEVEL_WARN=y
# 异步模式
CONFIG_LOG_MODE_ASYNC=y
CONFIG_LOG_ASYNC_BUF_SIZE=4096
CONFIG_LOG_ASYNC_TASK_STACK_SIZE=512
CONFIG_LOG_ASYNC_TASK_PRIORITY=1
# 行缓冲区
CONFIG_LOG_LINE_BUF_SIZE=512
# ========== syslog UART 配置 ==========
# UART 设备选择
CONFIG_SYSLOG_UART_DEVICE_UART0=y
# 波特率(标准波特率,兼容性好)
CONFIG_SYSLOG_UART_BAUDRATE=115200
# printf/printk 重定向(统一日志管理)
CONFIG_SYSLOG_PRINTF_REDIRECT=y
CONFIG_SYSLOG_PRINTK_REDIRECT=y
API 参考
初始化
lisa_log_init
int lisa_log_init(void);
功能:初始化日志系统
返回值:
0:成功其他:失败
说明:
系统会在启动流程中自动调用此函数,用户代码无需手动调用
会自动初始化 EasyLogger 前端和 UART 后端
初始化完成后,日志会自动输出到 UART 串口
重复调用是安全的
日志输出宏
基于 TAG 的日志宏
#define LOGE(fmt, ...) // 错误级别
#define LOGW(fmt, ...) // 警告级别
#define LOGI(fmt, ...) // 信息级别
#define LOGD(fmt, ...) // 调试级别
#define LOGV(fmt, ...) // 详细级别
重要说明:
使用这些宏之前,必须先定义
LOG_TAG宏LOG_TAG定义必须在#include "lisa_log.h"之前每个源文件只需定义一次
LOG_TAG
使用方法:
// 重要:LOG_TAG 必须在包含头文件之前定义
#define LOG_TAG "MyModule"
#include "lisa_log.h"
void my_function(void) {
LOGI("Application started");
LOGD("Debug info: value = %d", 42);
LOGE("Error occurred: %s", "connection failed");
}
带标签参数的日志宏
#define LISA_LOGE(tag, format, ...)
#define LISA_LOGW(tag, format, ...)
#define LISA_LOGI(tag, format, ...)
#define LISA_LOGD(tag, format, ...)
#define LISA_LOGV(tag, format, ...)
使用方法:
void my_function(void) {
LISA_LOGI("Network", "Connection established");
LISA_LOGD("Network", "IP: %s", "192.168.1.1");
}
十六进制转储
#define LOGH(name, data, len) // 基于 TAG 的转储
#define LISA_LOGH(tag, data, len, name) // 带标签的转储
使用方法:
#define LOG_TAG "DataDump"
#include "lisa_log.h"
void dump_data(void) {
uint8_t buffer[16] = {0x01, 0x02, 0x03, ...};
LOGH("buffer", buffer, 16);
LISA_LOGH("Network", buffer, 16, "rx_data");
}
日志级别控制
lisa_log_set_level
void lisa_log_set_level(uint8_t lvl);
功能:动态设置日志输出级别
参数:
lvl:日志级别(LISA_LOG_LEVEL_ERROR, LISA_LOG_LEVEL_INFO 等)
使用方法:
// 只输出错误级别及以上的日志
lisa_log_set_level(LISA_LOG_LEVEL_ERROR);
// 输出所有级别的日志
lisa_log_set_level(LISA_LOG_LEVEL_VERBOSE);
日志刷新
log_flush
void log_flush(void);
功能:刷新异步日志缓冲区
说明:
在异步模式下,确保所有日志都已输出
在系统进入休眠或关键操作前调用
UART 后端控制
UART 后端在系统初始化时自动启用,以下 API 可用于控制日志输出:
log_flush
void log_flush(void);
功能:刷新异步日志缓冲区,确保所有日志已通过 UART 输出
使用场景:
系统进入休眠前
关键操作前,确保日志已完整输出
调试时需要立即查看日志
示例:
LOGI("Entering sleep mode...");
log_flush(); // 确保日志已输出
enter_sleep();
使用示例
基本使用
// 重要:LOG_TAG 必须在包含头文件之前定义
#define LOG_TAG "MyApp"
#include "lisa_log.h"
void app_main(void) {
// 注意:日志系统已在系统启动时自动初始化,通常不需要手动调用 lisa_log_init()
// 输出不同级别的日志
LOGI("Application started, version: %s", "1.0.0");
LOGD("Debug mode enabled");
LOGW("This is a warning message");
LOGE("Error code: %d", -1);
// 十六进制转储
uint8_t data[] = {0xAA, 0xBB, 0xCC, 0xDD};
LOGH("data", data, sizeof(data));
}
动态控制日志级别
#define LOG_TAG "LogCtrl"
#include "lisa_log.h"
void set_debug_level(const char *level) {
if (strcmp(level, "error") == 0) {
lisa_log_set_level(LISA_LOG_LEVEL_ERROR);
} else if (strcmp(level, "info") == 0) {
lisa_log_set_level(LISA_LOG_LEVEL_INFO);
} else if (strcmp(level, "debug") == 0) {
lisa_log_set_level(LISA_LOG_LEVEL_DEBUG);
} else if (strcmp(level, "verbose") == 0) {
lisa_log_set_level(LISA_LOG_LEVEL_VERBOSE);
}
LOGI("Log level changed to: %s", level);
}
使用带标签的日志
#include "lisa_log.h"
void network_task(void) {
LISA_LOGI("Network", "Task started");
if (connect_to_server()) {
LISA_LOGI("Network", "Connected to server");
} else {
LISA_LOGE("Network", "Connection failed");
}
}
void storage_task(void) {
LISA_LOGI("Storage", "Task started");
if (mount_filesystem()) {
LISA_LOGI("Storage", "Filesystem mounted");
} else {
LISA_LOGE("Storage", "Mount failed");
}
}
进入低功耗前刷新日志
#define LOG_TAG "Power"
#include "lisa_log.h"
void enter_low_power_mode(void) {
LOGI("Preparing to enter low power mode");
// 刷新所有待输出的日志
log_flush();
// 进入低功耗模式
hal_pm_enter_sleep();
}
void exit_low_power_mode(void) {
LOGI("System resumed from low power mode");
}
注意事项
1. LOG_TAG 定义顺序
重要:使用基于 TAG 的日志宏(LOGE/LOGW/LOGI/LOGD/LOGV)时,必须在包含头文件之前定义 LOG_TAG:
// ✅ 正确的顺序
#define LOG_TAG "MyModule"
#include "lisa_log.h"
// ❌ 错误的顺序
#include "lisa_log.h"
#define LOG_TAG "MyModule" // 错误:定义太晚
说明:
LOG_TAG必须在#include "lisa_log.h"之前定义如果未定义
LOG_TAG,默认会使用"NO_TAG"使用
LISA_LOG*系列宏(如LISA_LOGI)不需要定义LOG_TAG
2. 异步模式日志丢失
在异步模式下,如果日志输出速度过快,可能导致缓冲区满而丢失日志。
解决方法:
增加
CONFIG_LOG_ASYNC_BUF_SIZE(缓冲区大小)提高异步任务优先级
CONFIG_LOG_ASYNC_TASK_PRIORITY降低日志输出频率
在关键位置调用
log_flush()确保日志输出
常见问题
如果日志无输出或出现乱码,请检查:
串口工具波特率是否与
CONFIG_SYSLOG_UART_BAUDRATE一致是否连接到了正确的 UART 设备(
prj.conf中配置的 UART0/UART1/UART2)硬件连接是否正确(TX、RX、GND)
日志级别是否过滤了所有输出
4. 日志级别过滤
日志级别在编译期和运行期都可以过滤:
编译期过滤(减少代码体积):
通过
pri.conf设置CONFIG_LOG_LEVEL_*低于设置级别的日志宏会被优化掉
运行期过滤(动态调整):
调用
lisa_log_set_level()动态设置级别只影响运行期输出,不减少代码体积
5. 线程安全
日志系统是线程安全的,可以在多任务环境下并发调用
内部使用互斥锁保护共享数据
在中断上下文中调用时,建议使用同步模式避免延迟
6. 编译期日志优化
通过定义 LISA_LOG_LEVEL 宏可以在编译期过滤特定模块的日志:
// 在模块头文件中定义
#define LISA_LOG_LEVEL LISA_LOG_LEVEL_INFO
#include "lisa_log.h"
// 该模块中低于 INFO 级别的日志会被编译器优化掉
// LOGD() 和 LOGV() 不会产生代码
常见问题 FAQ
Q1:如何修改日志输出的串口?
A:日志使用的串口由系统 syslog 决定,需要在系统启动代码中配置 UART 初始化。日志组件本身不负责串口选择。
Q2:异步模式和同步模式如何选择?
A:
异步模式(推荐):适合一般应用,不阻塞程序,性能好
同步模式:适合调试关键问题,确保日志不丢失
Q3:如何在代码中动态开关日志?
A:使用 lisa_log_set_level() 设置日志级别:
// 关闭日志(只输出 ASSERT)
lisa_log_set_level(LISA_LOG_LEVEL_ASSERT);
// 开启所有日志
lisa_log_set_level(LISA_LOG_LEVEL_VERBOSE);
Q4:日志会影响实时性吗?
A:异步模式下,日志调用会立即返回,对实时性影响很小。如果对实时性要求极高,可以在关键代码段降低日志级别或关闭日志。