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

Creative Commons
本作品采用知识共享署名

本文说明如何使用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如下

1
2
3
4
5
6
7
#include <logging/log.h>

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

示例代码sample_instance.c如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#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如下:

1
2
3
4
5
6
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,为了区分不同的实例的打印使用了实例日志,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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进制输出接口如下

1
2
3
4
5
6
7
8
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