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. 启用日志组件

配置项

说明

默认值

CONFIG_LOG

使能日志组件

n

在 prj.conf 中添加:

CONFIG_LOG=y

启用后,系统会自动使用 EasyLogger 作为日志前端,UART 作为日志后端。

2. 日志级别

日志级别从严重到详细依次为:

级别

配置项

说明

使用场景

ASSERT

CONFIG_LOG_LEVEL_ASSERT

断言级别

致命错误,程序无法继续运行

ERROR

CONFIG_LOG_LEVEL_ERROR

错误级别

严重错误,功能受影响

WARN

CONFIG_LOG_LEVEL_WARN

警告级别

潜在问题,不影响正常运行

INFO

CONFIG_LOG_LEVEL_INFO

信息级别(默认)

一般信息,程序运行状态

DEBUG

CONFIG_LOG_LEVEL_DEBUG

调试级别

调试信息,开发阶段使用

VERBOSE

CONFIG_LOG_LEVEL_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. 异步模式配置

异步模式可以避免日志输出阻塞应用程序,提高系统响应性能。

配置项

说明

默认值

调整建议

CONFIG_LOG_MODE_ASYNC

使能异步模式

y

建议保持开启

CONFIG_LOG_ASYNC_BUF_SIZE

异步缓冲区大小(字节)

10240

日志量大时可增加

CONFIG_LOG_ASYNC_TASK_STACK_SIZE

异步任务栈大小(字节)

512

栈溢出时增加

CONFIG_LOG_ASYNC_TASK_PRIORITY

异步任务优先级

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. 高级配置

配置项

说明

默认值

调整建议

CONFIG_LOG_LINE_BUF_SIZE

单行日志缓冲区大小(字节)

1024

单条日志过长时增加

CONFIG_LOG_USE_POSIX_TIME

使用 POSIX 时间戳

n

需要精确时间时启用

CONFIG_LOG_BACKEND_SYS_NAME

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 默认输出行为

不启用重定向时(默认):

函数

默认输出

说明

printf

UART(syslog)

通过 syslog 直接立即输出到 UART

printk

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

启用重定向后的行为:

函数

重定向后输出

说明

printf

日志系统

经过 lisa_log 格式化,带时间戳、标签等

printk

日志系统

经过 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:异步模式下,日志调用会立即返回,对实时性影响很小。如果对实时性要求极高,可以在关键代码段降低日志级别或关闭日志。