Skip to content

概述

日志(大白话:程序运行时的"日记",把运行过程一条条记下来,方便你事后翻看)是嵌入式开发最重要的调试手段。Ai-WB2 SDK 内置 blog 日志系统,提供分级输出(大白话:按重要程度分层,如"提示/警告/错误",方便筛选)、组件级过滤(大白话:可以只让某个模块的日志显示出来)、ANSI 彩色显示能力。本教程演示 6 级日志的打印与过滤:设置不同日志级别,观察哪些日志会被打印。

用大白话讲:日志就像你记的"日记本"——程序每做一件事就写一行日记,方便你翻看它到底干了什么、哪里出错了。但日记太多翻起来费劲,所以还分了重要程度(普通记录、警告、错误……),并允许你设置"只显示警告及以上",就像看新闻只关注头条。本教程就是演示这些日志级别和过滤规则。

本教程基于安信可官方 SDKAi-Thinker-Open/Ai-Thinker-WB2,版本 release_bl_iot_sdk_1.6.40)的官方示例 applications/system/blog_demo 编写,代码可在本地 SDK 中直接找到。

🎯本页目标通过 blog 日志分级打印与级别过滤实验,掌握 blog_debug/info/warn/error 等 API 与级别设置方法。
🧰前置条件① Ai-WB2 开发板一块(Type-C 数据线)② 已按 [SDK 安装](../sdk/sdk_intro) 完成环境搭建。
🔗相关章节串口输出相关见 [UART(串口)](../basic/uart);日志口为 UART0(TX GPIO4 / RX GPIO3)。

进入示例工程

打开终端,进入官方 blog_demo 示例工程目录:

cd ~/Ai-Thinker-WB2/applications/system/blog_demo

说明:cd 是"进入目录"的命令,~ 表示你的用户主目录。这条命令进入 blog_demo 示例工程,后面所有 make 命令都要在这个目录里执行;如果提示 No such file or directory(没有这个目录),说明路径不对,见文末 FAQ。

编写代码

打开 blog_demo/main.c,本步完整代码已移至文末,见:

📜 完整代码 — 位于本页「完整代码」章节,默认折叠,点击展开,与官方示例(applications/system/blog_demo/blog_demo/main.c)完全一致。

代码要点:

代码 作用
blog_set_level_log_component(level, "blog_demo") 设置组件的过滤级别;不设置就看不到过滤效果
blog_print(...) 无条件打印;做"对照组",证明打印本身没问题
blog_debug / blog_info / blog_warn / blog_error 按级别打印;低于过滤级别的会被"筛掉"不输出
blog_assert(...) 断言级日志;条件不成立时提示,用于抓"不该发生"的情况

日志级别定义(从高到低过滤):

typedef enum _blog_leve {
    BLOG_LEVEL_ALL = 0,   /* 全部输出 */
    BLOG_LEVEL_DEBUG,     /* 调试 */
    BLOG_LEVEL_INFO,      /* 信息 */
    BLOG_LEVEL_WARN,      /* 警告 */
    BLOG_LEVEL_ERROR,     /* 错误 */
    BLOG_LEVEL_ASSERT,    /* 断言 */
    BLOG_LEVEL_NEVER,     /* 全部屏蔽 */
} blog_level_t;

📌 设为 BLOG_LEVEL_INFO 时,blog_debug 不输出、blog_info 及以上输出;设为 BLOG_LEVEL_NEVER 时全部屏蔽。

编译工程

在工程目录执行编译:

make -j8

说明:make 是"编译工程"的命令,把源代码翻译成开发板能运行的机器码;-j8 表示用 8 个核心并行编译,更快。

编译成功后生成固件 build_out/blog_demo.bin(固件:编译后烧进开发板的程序,相当于开发板的"操作系统+你的程序")。

烧录固件

开发板保持 USB 连接,确认串口设备号后执行烧录:

make flash p=/dev/ttyUSB0 b=921600

说明:make flash 是"烧录"命令,把编译好的固件下载进芯片(烧录:把程序写进芯片的过程);p=/dev/ttyUSB0 是串口设备号,要换成你电脑上实际的串口(Windows 下形如 COM3),b=921600 是烧录波特率(传输速度)。

⏳ 烧录过程中按提示长按开发板 EN 键进入下载模式,等待进度条完成即烧录成功。若一直卡在等待或报串口打不开,见文末 FAQ。

运行验证

烧录完成后开发板自动重启运行,打开串口助手(波特率 921600)观察日志,可见按级别过滤的规律:

The log level is LOG_LEVEL_ALL
DEBUG (5)[main.c:  22] The log level is LOG_LEVEL_DEBUG
INFO (11)[main.c:  23] The log level is LOG_LEVEL_INFO
WARN (16)[main.c:  24] The log level is LOG_LEVEL_WARN
ERROR (22)[main.c:  25] The log level is LOG_LEVEL_ERROR
ASSERT (28)[main.c:  26] The log level is LOG_LEVEL_ASSERT
The log level is LOG_LEVEL_NEVER

DEBUG (37)[main.c:  31] The log level is LOG_LEVEL_DEBUG
INFO (43)[main.c:  32] The log level is LOG_LEVEL_INFO
...

规律验证:设置为 BLOG_LEVEL_INFO 后,DEBUG 行不再出现;设置为 BLOG_LEVEL_NEVER 后该组件全部日志消失。

⚠️ 官方默认 DEBUG 级别不打印(即使设置为 DEBUG 也无输出),需要输出 DEBUG 日志时,修改组件配置头 blog_cfg.h 中的 BLOG_POWERON_SOFTLEVEL_FILEBLOG_LEVEL_DEBUG

预期结果:能看到多组日志,且级别设得越高(如 BLOG_LEVEL_ERROR)打印的日志越少、设置 BLOG_LEVEL_NEVER 后该组件日志全部消失,即验证通过。若日志固定不变、过滤没起作用,见文末 FAQ。

代码执行流程

例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):


本文 API 汇总

blog_set_level_log_component(level, name)

设置某个日志组件的输出级别,低于该级别的日志被过滤不输出。

参数

  • level:级别值,可选值:BLOG_LEVEL_ALL(全部输出)/ BLOG_LEVEL_DEBUG / BLOG_LEVEL_INFO / BLOG_LEVEL_WARN / BLOG_LEVEL_ERROR / BLOG_LEVEL_ASSERT / BLOG_LEVEL_NEVER(全部屏蔽)
  • name:组件名称字符串(注册组件时的名字,如 "tcp""axk_mqtt"

返回值:成功返回 0;失败返回负值错误码

blog_print(fmt, ...)

输出日志,不参与级别过滤,任何级别设置下都会打印。

参数

  • fmt:格式化字符串,同 printf 用法,必填
  • ...:变参,与 fmt 占位符对应,可省略

返回值:无

blog_debug(fmt, ...)

输出调试级日志(默认被过滤不显示,需先设置级别 ≤ DEBUG)。

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:无

blog_info(fmt, ...)

输出信息级日志(日常运行状态,最常见)。

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:无

blog_warn(fmt, ...)

输出警告级日志(异常但可继续运行)。

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:无

blog_error(fmt, ...)

输出错误级日志(严重异常)。

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:无

blog_assert(fmt, ...)

输出断言级日志(最高级别,条件不满足时提示)。

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:无

printf(fmt, ...)

标准 C 库输出到 UART0,不受 blog 级别过滤

参数

  • fmt:格式化字符串,必填
  • ...:变参,可省略

返回值:成功返回打印的字符数;失败返回负值


完整代码

以下为 blog_demo/main.c 完整源码,与官方示例(applications/system/blog_demo/blog_demo/main.c)完全一致:

📜 点击展开 blog_demo/main.c 完整代码
c
/**
 * @file main.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-22
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <stdio.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <blog.h>
#include "bl_sys.h"

void main(void)
{
    blog_set_level_log_component(BLOG_LEVEL_ALL, "blog_demo");
    blog_print("The log level is LOG_LEVEL_ALL\n");
    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");
    blog_print("The log level is LOG_LEVEL_NEVER\r\n");
    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_DEBUG, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");

    blog_print("The log level is LOG_LEVEL_NEVE\r\n");
    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_INFO, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");

    blog_print("The log level is LOG_LEVEL_NEVER\r\n");
    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_WARN, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");
    blog_print("The log level is LOG_LEVEL_NEVER\r\n");

    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_ERROR, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");
    blog_print("The log level is LOG_LEVEL_NEVER\r\n");

    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_ASSERT, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");
    blog_print("The log level is LOG_LEVEL_NEVER\r\n");

    printf("\r\n");
    blog_set_level_log_component(BLOG_LEVEL_NEVER, "blog_demo");

    blog_debug("The log level is LOG_LEVEL_DEBUG");
    blog_info("The log level is LOG_LEVEL_INFO");
    blog_warn("The log level is LOG_LEVEL_WARN");
    blog_error("The log level is LOG_LEVEL_ERROR");
    blog_assert("The log level is LOG_LEVEL_ASSERT");
    blog_print("The log level is LOG_LEVEL_NEVER\r\n");
}

常见问题与踩坑提示

⚠️ blog_debug 日志不打印
原因:SDK 默认关闭 DEBUG 级别输出(blog_cfg.hBLOG_POWERON_SOFTLEVEL_FILE 默认 BLOG_LEVEL_INFO
解决:将 blog_cfg.h#define BLOG_POWERON_SOFTLEVEL_FILE (BLOG_LEVEL_INFO) 改为 BLOG_LEVEL_DEBUG 后重新编译

⚠️ 设置了级别但日志没有过滤
原因blog_set_level_log_component 的第二个参数(组件名)与打印代码所在组件名不一致
解决:确认组件名与工程名一致(本示例为 blog_demo);或直接设置全局级别

⚠️ printf 与 blog 日志混排、格式不一致
原因:printf 不走 blog 系统,无级别前缀与颜色
解决:调试统一使用 blog 系列 API;printf 仅用于无需过滤的原始输出

⚠️ 串口打不开 / 看不到任何日志
原因:USB 转串口驱动未装、串口被占用,Linux 下还可能是没有访问权限
解决:Linux 用 lsusb 确认设备被识别,sudo chmod 666 /dev/ttyUSB0 或把用户加入 dialout 组后重试;Windows 在设备管理器查看 COM 口并安装 CH340/CP210x 驱动;串口助手的波特率要设为 921600

⚠️ 烧录一直卡在等待 / 提示找不到芯片
原因:未进入下载模式、数据线只能充电不能传数据,或波特率不对
解决:烧录时长按 EN 键直到进度条出现;换一根能传数据的数据线;确认 p= 串口号与 b=921600 无误

⚠️ cd 报错 No such file or directory / 找不到 Makefile
原因:没进入示例工程目录就执行了 make,或 SDK 安装路径与教程不同
解决:先 cd ~/Ai-Thinker-WB2/applications/system/blog_demo 再执行 make;若 ~/Ai-Thinker-WB2 不存在,用 find ~ -name "Ai-Thinker-WB2" 找到 SDK 实际位置

运行自检

串口输出 7 组不同级别设置下的日志,且级别越高(如 ERROR)过滤掉越多日志,即 blog 日志系统验证通过。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23