Zephyr日志使用指南-实例日志

本文说明如何使用Zephyr实例日志。

当一个模块会以多实例运行时,不同的实例显示出来的模块标签都是同一个,这将导致无法识别到底时那个实例的日志输出。为解决该问题Zephyr日志系统提供了多实例支持,可以在实例级别进行过滤。

使用方法 链接到标题

按照如下步骤使用实例日志:

  1. 在模块文件中使用LOG_MODULE_REGISTER进行注册

  2. 使用LOG_INSTANCE_PTR_DECLARE在实例结构中指定日志的结构

  3. 使用LOG_INSTANCE_REGISTER注册实例,并使用LOG_INSTANCE_PTR_INIT进行日志实例初始化。

  4. 使用LOG_INST_*LOG_INST_HEXDUMP_*进行日志记录。

示例代码sample_instance.h如下

#include <logging/log.h>

/* 1. 在实例结构中用LOG_INSTANCE_PTR_DECLARE指定日志的结构 */
struct sample_instance {
    LOG_INSTANCE_PTR_DECLARE(log);
    uint32_t cnt;
};

示例代码sample_instance.c如下

#include "sample_instance.h"

/* 2.注册模块日志,模块名为module_inst,等级为DEBUG */
#include <logging/log.h>
#define SAMPLE_INSTANCE_MODULE_NAME module_inst
LOG_MODULE_REGISTER(SAMPLE_INSTANCE_MODULE_NAME, 4);


/* 该宏用于获取实例结构体遍历指针 */
#define SAMPLE_INSTANCE_PTR(idx) &sample_##idx##_info

/* 该宏用于生成实例,为每个实例注册日志,并进行初始化 */
#define SAMPLE_INSTANCE_DEVICE(idx) \
    LOG_INSTANCE_REGISTER(SAMPLE_INSTANCE_MODULE_NAME, inst##idx, 3); \
    struct sample_instance sample_##idx##_info = {           \
        LOG_INSTANCE_PTR_INIT(log, SAMPLE_INSTANCE_MODULE_NAME, inst##idx)       \
    };

/* 最多支持3个实例 */
#define SAMPLE_INSTANCE_NUM 3

/* 3.使用宏注册和初始化实例日志 */
SAMPLE_INSTANCE_DEVICE(0)
SAMPLE_INSTANCE_DEVICE(1)
SAMPLE_INSTANCE_DEVICE(2)


static struct sample_instance *sample_instace_ptr[]={
    SAMPLE_INSTANCE_PTR(0), 
    SAMPLE_INSTANCE_PTR(1), 
    SAMPLE_INSTANCE_PTR(2)};

struct sample_instance *sample_instance_open(uint8_t id)
{
    if(id < SAMPLE_INSTANCE_NUM){
        sample_instace_ptr[id]->cnt = 10;
        return sample_instace_ptr[id];
    }

    return NULL;
}

int sample_instance_close(struct sample_instance *inst)
{
    if(inst == NULL){
        return -1;
    }
    return 0;
}

int sample_instance_do(struct sample_instance *inst)
{
    if(inst == NULL){
        return -1;
    }

    /* 4.进行实例日志输入 */
    LOG_INST_INF(inst->log, "inst %p counter_value: %d", inst, inst->cnt);
    inst->cnt++;
    return 0;
}

当头文件中的函数需要加入到模块实例中时,需要在头文件的函数体内使用LOG_LEVEL_SET指定日志等级,再调用LOG_INST_*进行日志输出。示例代码添加再sample_instance.h如下:

static inline void sample_instance_header_do(struct sample_instance *inst)
{
    LOG_LEVEL_SET(LOG_LEVEL_INF);
    LOG_INST_INF(inst->log, "inst %p header do %d.", inst, inst->cnt);
    inst->cnt++;
}

该示例演示了一个可多实例工作的sample_instance模块,通过sample_instance_open开启一个新的实例,sample_instance_do来执行指定实例时通过日志系统输出自己的内部计数,sample_instance_header_do指定了一个INFO等级的实例日志输出。该模块在日志系统中的模块名为module_inst,为了区分不同的实例的打印使用了实例日志,测试代码如下:

void instance_logging(void)
{
    struct sample_instance *inst0 = sample_instance_open(0);
    struct sample_instance *inst1 = sample_instance_open(1);
    struct sample_instance *inst2 = sample_instance_open(2);

    sample_instance_do(inst0);
    sample_instance_do(inst1);
    sample_instance_do(inst2);
    sample_instance_header_do(inst0);

    sample_instance_close(inst0);
    sample_instance_close(inst1);
    sample_instance_close(inst2);
}

其输出结果为:

[00:00:25.731,861] module_inst.inst0: inst 0x3fc8c364 counter_value: 10 [00:00:25.731,901] module_inst.inst1: inst 0x3fc8c36c counter_value: 10 [00:00:25.731,904] module_inst.inst2: inst 0x3fc8c374 counter_value: 10 [00:00:25.731,922] module_inst.inst0: inst 0x3fc8c364 header do 11.

从结果能够看到在模块标签module_inst后又多了一个实例标签inst*,这样即使是同一份代码,也能够根据实例标签判断是哪个实例产生的日志。

注意:实例日志的注册宏LOG_INSTANCE_REGISTER要求参数有实例名,该宏是在编译期间就展开了,因此实例名必须在编译期就确定,无法运行时生成。这就要求使用实例日志的多实例模块必须要预先知道最大实例数,并指定好日志系统的实例名,而无法在运行时根据实际的实例数量动态生成。

接口说明 链接到标题

实例日志的格式化输出接口和原始数据16进制输出接口如下

LOG_INST_ERR(_log_inst, ...)
LOG_INST_WRN(_log_inst, ...)
LOG_INST_INF(_log_inst, ...)
LOG_INST_DBG(_log_inst, ...)
LOG_INST_HEXDUMP_ERR(_log_inst, _data, _length, _str)
LOG_INST_HEXDUMP_WRN(_log_inst, _data, _length, _str)
LOG_INST_HEXDUMP_INF(_log_inst, _data, _length, _str)
LOG_INST_HEXDUMP_DBG(_log_inst, _data, _length, _str)

只比模块日志多指定一个_log_inst的实例参数,其它使用方法一样,可以参考Zephyr日志使用指南-模块日志一文。

参考 链接到标题

https://docs.zephyrproject.org/latest/services/logging/index.html