日志库

概述

Ameba SDK 提供了统一的标签式日志系统,可用于 ROM、Bootloader 及应用固件各阶段。日志输出通过两层独立机制进行控制:

  • 编译期过滤( COMPIL_LOG_LEVEL):超过该级别的日志调用在预处理阶段即被移除,不产生任何代码或数据开销。

  • 运行时过滤(rtk_log_level_set()):控制已编译进固件的日志实际输出,不影响二进制大小。

日志级别

日志级别由枚举 rtk_log_level_t 定义:

typedef enum {
    RTK_LOG_NONE,    /* 0 */
    RTK_LOG_ALWAYS,  /* 1 */
    RTK_LOG_ERROR,   /* 2 */
    RTK_LOG_WARN,    /* 3 */
    RTK_LOG_INFO,    /* 4 */
    RTK_LOG_DEBUG    /* 5 */
} rtk_log_level_t;

级别

数值

字母

说明

RTK_LOG_NONE

0

禁用所有输出。

RTK_LOG_ALWAYS

1

A

必须始终显示的关键信息,但不代表警告或错误。

RTK_LOG_ERROR

2

E

错误信息,如 API 使用不当导致的错误。

RTK_LOG_WARN

3

W

不合适的配置但仍可运行,或可能导致错误的情形。

RTK_LOG_INFO

4

I

正常且必要的提示信息(默认级别)。

RTK_LOG_DEBUG

5

D

出错或故障时的诊断信息,如指针指向和内存内容。

如需更改编译期或运行时的日志级别,请参阅 日志级别配置 章节。

日志格式

输出格式

日志宏的输出格式分为以下两种:

  • 带标签:

    [TAG-X] 消息内容
    
  • 不带标签(传入 NOTAG):

    消息内容
    

其中 X 为级别字母( AEWID )。所有宏均不自动追加换行符, 需在格式字符串末尾显式加入 \nNOTAG 适用于在循环中打印连续行等不需要重复前缀的场景。

标签命名规则

每个模块通过标签字符串标识自身。命名规则如下:

  • 将标签定义为 const char * 指针,通常位于文件作用域。

  • 同一文件或同一组件内的所有文件共享一个标签作为唯一标识符。

  • 标签字符串必须全大写,长度不超过 9 个字符,且只能包含数字、字母或下划线,不允许使用横杠。

  • 标签变量命名以 TAG_ 开头(例如 TAG_WIFI )。

static const char *TAG_APP = "APP";

日志输出宏

Ameba SDK 提供两类日志输出宏以适应不同的应用场景。

1. 标准输出宏 (RTK_LOGx)

这类宏使用 DiagPrintf ,支持除浮点数外的常见格式说明符。日志级别已编码在宏名称中,适合在常规任务场景中使用:

RTK_LOGA(tag, format, ...)   /* RTK_LOG_ALWAYS */
RTK_LOGE(tag, format, ...)   /* RTK_LOG_ERROR  */
RTK_LOGW(tag, format, ...)   /* RTK_LOG_WARN   */
RTK_LOGI(tag, format, ...)   /* RTK_LOG_INFO   */
RTK_LOGD(tag, format, ...)   /* RTK_LOG_DEBUG  */
  • tag:模块标签字符串,或传入 NOTAG (省略前缀)。

  • format: printf 风格的格式字符串。

2. 轻量级输出宏 (RTK_LOGS)

RTK_LOGS 底层调用 DiagPrintfNano ,它的栈占用更小,支持格式有限,适合在中断处理函数 (ISR) 或栈空间受限的任务中使用:

RTK_LOGS(tag, level, format, ...)
  • tag:模块标签字符串,或传入 NOTAG (省略前缀)。

  • level: rtk_log_level_t 枚举值之一。

  • format: printf 风格的格式字符串。

使用示例

#include "log.h"

static const char *TAG_APP = "APP";

void app_init(void)
{
    int err = 1;

    /* 推荐:nano 后端,栈占用更小 */
    RTK_LOGS(TAG_APP, RTK_LOG_INFO,  "version %d\n", 1);
    RTK_LOGS(TAG_APP, RTK_LOG_ERROR, "open failed, ret=%d\n", err);

    /* 打印标题行,循环内容使用 NOTAG 省略重复前缀 */
    RTK_LOGI(TAG_APP, "command list start\n");
    for (int i = 0; i < count; i++) {
        RTK_LOGI(NOTAG, "  [%d] %s\n", i, cmd_table[i].name);
    }
    RTK_LOGI(TAG_APP, "command list end\n");
}

预期输出:

[APP-I] version 1
[APP-E] open failed, ret=1
[APP-I] command list start
  [0] help
  [1] reset
[APP-I] command list end

日志级别配置

编译时日志级别设置

COMPIL_LOG_LEVEL 是全局定义,决定哪些日志级别会被编译进固件。 超过该级别的调用在预处理阶段即被移除,不占用任何 Flash 或 RAM 空间。

默认的编译日志级别设定为 RTK_LOG_INFO (DEBUG 默认被裁剪,在 log.h 中配置)。

若要更改单个文件或者整个组件的编译级别,请参阅下文的覆盖方法。

单文件覆盖

在包含头文件 log.h 前定义 COMPIL_LOG_LEVEL ,可覆盖默认值:

#define COMPIL_LOG_LEVEL  RTK_LOG_ERROR
#include "log.h"

static const char *TAG_FOO = "FOO";

/* 该文件中只有 ALWAYS 和 ERROR 级别的调用会被保留。 */
RTK_LOGE(TAG_FOO, "error code: %d\n", err);
RTK_LOGD(TAG_FOO, "debug value: %08x\n", val);  /* 编译期被裁剪 */

组件级覆盖

若要更改某个组件(多个文件)的编译级别,在该组件的 CMakeLists.txt 中使用 private_definitions 追加定义:

ameba_list_append(private_definitions COMPIL_LOG_LEVEL=RTK_LOG_DEBUG)

这等效于为该组件的所有源文件传入 -DCOMPIL_LOG_LEVEL=RTK_LOG_DEBUG 编译参数。

备注

COMPIL_LOG_LEVEL 是编译期上限。未编译进固件的日志,即使通过 rtk_log_level_set() 设置了对应级别,都无法被打印出来。运行时级别只能进一步 抑制输出,无法恢复编译期已裁剪的日志。

运行时日志级别设置

运行时可按标签或全局方式调整日志级别,不影响二进制大小。

rtk_log_level_set

条目

说明

功能介绍

为指定模块标签设置日志输出级别,或更新全局默认级别。

参数

  • tag:模块标签字符串,或 "*" 更新全局默认级别 ( rtk_log_default_level )。

  • level:目标日志级别( RTK_LOG_NONERTK_LOG_DEBUG )。

返回值

成功返回 RTK_SUCCESS (0);tag 为 NULL 或 level 超出范围时 返回 RTK_FAIL (-1)。

rtk_log_level_get

条目

说明

功能介绍

查询某模块标签当前生效的日志级别。

参数

  • tag:要查询的模块标签字符串。

返回值

若在缓存中找到该标签,返回其已注册的级别;否则返回全局默认级别 ( rtk_log_default_level )。

rtk_log_array_clear

条目

说明

功能介绍

清除缓存中所有按标签设置的级别条目,并将计数器重置为零。

参数

返回值

rtk_log_array_print

条目

说明

功能介绍

打印当前缓存中存储的所有标签/级别条目。

参数

  • rtk_log_tag_array:指向全局缓存数组的指针 (直接传入 rtk_log_tag_array 即可)。

返回值

成功返回 RTK_SUCCESS (0);指针为 NULL 时返回 RTK_FAIL (-1)。

全局级别与模块级别

运行时日志级别分为两个独立维度:

  • 全局默认级别( rtk_log_default_level ):通过 rtk_log_level_set("*", level) 设置,作用于未在缓存中单独注册的所有模块。

  • 模块级别:通过 rtk_log_level_set(tag, level) 针对特定标签设置,存储于标签缓存中。 已注册模块级别的标签在查询时直接返回其缓存值,全局默认级别对其不产生影响。

两者相互独立,互不覆盖。修改全局默认级别不会影响已注册模块级别的标签; 修改某模块级别也不会影响全局默认值或其他模块的设置。

标签缓存管理

标签缓存最多容纳 LOG_TAG_CACHE_ARRAY_SIZE (4)个条目,用于存储各模块的日志级别。 缓存已满时若写入新标签,将按环形策略覆盖索引 count % 4 处的槽位。 通配符 "*" 不占用缓存槽,其操作仅更新全局默认级别变量。

使用示例

/* 全局抑制 DEBUG 输出 */
rtk_log_level_set("*", RTK_LOG_INFO);

/* 仅对 WiFi 模块启用 DEBUG */
rtk_log_level_set("WIFI", RTK_LOG_DEBUG);

/* 查询生效的级别 */
rtk_log_level_t lvl = rtk_log_level_get("WIFI");  /* RTK_LOG_DEBUG */

/* 查看缓存内容 */
rtk_log_array_print(rtk_log_tag_array);

/* 重置所有按标签的设置 */
rtk_log_array_clear();

AT 命令

运行时日志级别也可通过 AT 命令进行控制,完整说明请参阅 AT+LOG

打印后端

每次日志宏调用在到达实际打印后端之前,需经过两级过滤,如下图所示。

../../_images/log_call_flow.svg

两级过滤说明:

  1. 编译期过滤( COMPIL_LOG_LEVEL ):级别超过该值的调用由预处理器移除,不生成任何代码。

  2. 运行时过滤( rtk_log_level_get ):将标签的当前生效级别与调用级别比较。若该标签已通过 rtk_log_level_set() 单独注册,则使用其对应级别;否则使用全局默认值 ( rtk_log_default_level )。

两种打印后端的区别:

  • DiagPrintfNano :栈占用更小,适合中断和小栈任务,格式说明符支持有限。

  • DiagPrintf :支持除浮点数以外的格式说明符,适合通用场景。

性能开销

两种宏在栈空间占用和格式支持方面存在差异:

  • RTK_LOGS 调用链: RTK_LOGSrtk_log_write_nano()DiagPrintfNano / DiagVprintfNano 。栈占用约 136 B,格式说明符支持有限。

  • RTK_LOGx 调用链: RTK_LOGxrtk_log_write()DiagPrintf / DiagVprintf 。栈占用约 252 B,支持除浮点数以外的格式说明符。

底层函数

栈占用(典型值)

格式支持

RTK_LOGS

DiagVprintfNano

~136 B

有限格式说明符

RTK_LOGx

DiagVprintf

~252 B

除浮点数外

选型建议:

  • 中断处理函数或栈空间受限的任务中,使用 RTK_LOGS

  • 需要打印长整型( %lld%lu 等)时,使用 RTK_LOGx

  • 浮点数格式说明符( %f%g 等)两类宏均不支持。

内存 Dump

以下三个辅助函数用于将内存区域以十六进制格式输出到日志。

rtk_log_memory_dump_word

条目

说明

功能介绍

以 32 位字为单位转储内存区域,每行显示 8 个字。

参数

  • src:目标内存区域的起始地址。

  • len:要转储的 32 位字的数量。

返回值

输出示例:

[200447b4] 20015e08 00000000 20000674 000055d9 0c002763 20000749 0c0067f4 00000000

rtk_log_memory_dump_byte

条目

说明

功能介绍

以字节为单位转储内存区域,每行显示 8 个字节。

参数

  • src:目标内存区域的起始地址。

  • len:要转储的字节数。

返回值

输出示例:

[200447b4] 08 5e 01 20 00 00 00 00

rtk_log_memory_dump2char

条目

说明

功能介绍

以十六进制与可打印 ASCII 组合格式转储内存区域,每行显示 16 字节。

参数

  • src_buff:目标内存区域的起始地址。

  • buff_len:要转储的字节数。

返回值

输出示例:

[0xe005263]   7c 03 f0 7f 03 23 74 cf  e7 07 25 cd e7 a1 69 30  ||....#t...%...i0|

API 参考

API / 宏

说明

RTK_LOGS(tag, level, fmt, ...)

日志输出(nano 后端, 推荐使用)

RTK_LOGA(tag, fmt, ...)

ALWAYS 级别日志

RTK_LOGE(tag, fmt, ...)

ERROR 级别日志

RTK_LOGW(tag, fmt, ...)

WARN 级别日志

RTK_LOGI(tag, fmt, ...)

INFO 级别日志

RTK_LOGD(tag, fmt, ...)

DEBUG 级别日志

rtk_log_level_set(tag, level)

设置标签运行时级别或 全局默认级别

rtk_log_level_get(tag)

获取标签运行时级别

rtk_log_array_clear()

清除标签级别缓存

rtk_log_array_print(rtk_log_tag_array)

打印标签级别缓存

rtk_log_memory_dump_word(src, len)

内存 Dump:32 位字

rtk_log_memory_dump_byte(src, len)

内存 Dump:字节

rtk_log_memory_dump2char(src_buff, buff_len)

内存 Dump:字节 + ASCII

rtk_log_mutex_init()

初始化日志互斥锁 (仅 RTL8730E)