Zephyr日志使用指南-实例日志
本文说明如何使用Zephyr实例日志。
当一个模块会以多实例运行时,不同的实例显示出来的模块标签都是同一个,这将导致无法识别到底时那个实例的日志输出。为解决该问题Zephyr日志系统提供了多实例支持,可以在实例级别进行过滤。
使用方法 链接到标题
按照如下步骤使用实例日志:
-
在模块文件中使用
LOG_MODULE_REGISTER进行注册 -
使用
LOG_INSTANCE_PTR_DECLARE在实例结构中指定日志的结构 -
使用
LOG_INSTANCE_REGISTER注册实例,并使用LOG_INSTANCE_PTR_INIT进行日志实例初始化。 -
使用
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