blender学习系列之--CLOG

news/2024/12/21 14:25:32/文章来源:https://www.cnblogs.com/jiaping/p/16546640.html

CLOG为blender提供的日志记录模块,它实现了日志记录的基本功能,同时提供了过滤功能,可通过参数来控制,实现准确记录所关注的记录,来帮助定位问题关键点。

该模块只有二个文件:CLG_log.h,clog.c。

在CLG_log.h文件最后有以下定义:

#define CLOG_INFO(clg_ref, level, ...) \CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
#define CLOG_WARN(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
#define CLOG_ERROR(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
#define CLOG_FATAL(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)#define CLOG_STR_INFO(clg_ref, level, str) \CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, str)
#define CLOG_STR_WARN(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, str)
#define CLOG_STR_ERROR(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, str)
#define CLOG_STR_FATAL(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, str)/* Allocated string which is immediately freed. */
#define CLOG_STR_INFO_N(clg_ref, level, str) \CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, str)
#define CLOG_STR_WARN_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, str)
#define CLOG_STR_ERROR_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, str)
#define CLOG_STR_FATAL_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, str)

总结来说,上面的代码定义了一组宏,方便用于程序中添加日志记录。分为三组,每组按严重程序分为4级,分别对应:INOF,WARN,ERROR,FATAL;

所有宏最终对应定义到CLOG_AT_SEVERITY,CLOG_STR_AT_SEVERITY,CLOG_STR_AT_SEVERITY_N这三个宏。

#define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) \{ \CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \(severity >= CLG_SEVERITY_WARN)) { \CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \} \} \((void)0)#define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) \{ \CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \(severity >= CLG_SEVERITY_WARN)) { \CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \} \} \((void)0)#define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) \{ \CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \(severity >= CLG_SEVERITY_WARN)) { \const char *_str = str; \CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \MEM_freeN((void *)_str); \} \} \((void)0)

 这三个宏的定义仅有细微差别,CLOG_AT_SEVERITY接受可变参数,最终调用CLOG_logf函数完成日志记录,CLOG_STR_AT_SEVERITY,CLOG_STR_AT_SEVERITY_N这两个宏均接受字符串作为记录内容,调用CLG_log_str函数记录到日志,后一个害在完成日志记录后立即释放字符串占用内存。

这三个宏在记录之前均对记录器类型进行检查,严重性大于或等于CLG_SEVERITY_WARN的准予记录,或者记录器类型标志为CLG_FLAG_USE且level大于或等于verbose_level的才准予记录,主要是为了过滤INFO类型记录过多而设置。

最终这些宏均调用CLG_logf或CLG_log_str来实现日志的记录。

在进一步分析时,来看看日志的几个结构:

1、日志上下文结构

typedef struct CLogContext {CLG_LogType *types;  //记录器类型的单链表 CLG_LogRef *refs;  //记录器引用的单链表
#ifdef WITH_CLOG_PTHREADSpthread_mutex_t types_lock;
#endifCLG_IDFilter *filters[2]; //记录过滤器,用于包含或排除某些信息的记录bool use_color;bool use_basename;bool use_timestamp;/** Borrowed, not owned. */int output;FILE *output_file;/** For timer (use_timestamp). */uint64_t timestamp_tick_start;/** For new types. */struct {int level;} default_type;//定义回调函数struct {void (*error_fn)(void *file_handle);void (*fatal_fn)(void *file_handle);void (*backtrace_fn)(void *file_handle);} callbacks;
} CLogContext;

 在clog.c中定义了g_ctx全局静态变量,在Blender中,只实现了单一的全局上下文。在CLG_init函数中调用CLG_ctx_init函数对g_ctx进行了初始化。代码如下:

/* 我们可以同时支持多个,但现在似乎不需要。 */
static struct CLogContext *g_ctx = NULL;void CLG_init(void)
{g_ctx = CLG_ctx_init();clg_color_table_init(g_ctx->use_color);
}static CLogContext *CLG_ctx_init(void)
{CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
#ifdef WITH_CLOG_PTHREADSpthread_mutex_init(&ctx->types_lock, NULL);
#endifctx->default_type.level = 1;//这个定义了冗余信息显示级别,仅当记录的冗余级别小于或等于该值时才被记录CLG_ctx_output_set(ctx, stdout);return ctx;
}static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
{ctx->output_file = file_handle;ctx->output = fileno(ctx->output_file);//output中保存文件描述符
#if defined(__unix__) || defined(__APPLE__)ctx->use_color = isatty(ctx->output);
#elif defined(WIN32)/* 根据windows10构建18298,所有的标准控制台都支持颜色 *就像linux终端一样,但它需要被打开。 *要打开颜色,我们需要通过传递标志ENABLE_VIRTUAL_TERMINAL_PROCESSING到SetConsoleMode来启用虚拟终端处理 *如果系统不支持虚拟终端处理,它将悄然失败和标志 *将不会被设置。*/GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &clg_previous_console_mode);ctx->use_color = 0;if (IsWindows10OrGreater() && isatty(ctx->output)) {DWORD mode = clg_previous_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;if (SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), mode)) {ctx->use_color = 1;}}
#endif
}

从上面代码看出,在g_ctx初始化时,仅初始化了default_type和output二个字段,对两个列表并没有初始化。这就要求在使用时对链表进行维护,同时也提供了灵活性(lazyer access)。

 全局上下文中设置了日志输出,所有与它相关记录器具有了相同的上下文。从上下文定义可以看出,一个上下文可以有多种记录器类型,多个记录器,它们构成了上下文中的单链表。

再来看具体的记录器和类型: 

/* Don't typedef enums. */
enum CLG_LogFlag {CLG_FLAG_USE = (1 << 0),
};
//定义记录严重程序
enum CLG_Severity {CLG_SEVERITY_INFO = 0,CLG_SEVERITY_WARN,CLG_SEVERITY_ERROR,CLG_SEVERITY_FATAL,
};/* Each logger ID has one of these. */
typedef struct CLG_LogType {struct CLG_LogType *next;char identifier[64];/** FILE output. */struct CLogContext *ctx;/** Control behavior. */int level;enum CLG_LogFlag flag;
} CLG_LogType;//记录器
typedef struct CLG_LogRef {const char *identifier;    //记录器标识CLG_LogType *type;         //记录器类型struct CLG_LogRef *next;   //记录器链表指针
} CLG_LogRef;

从代码中可知,记录器和类型结构定义了日志上下文的链表项,记录器项主要有二个字段,标识和类型。类型项包含类型标识、上下文指针、冗余信息控制级及是否可用标志。

Blender由众多模块构成,在调试时为区分信息,在第个模块中定义了各自记录器,给出了标识符。如下面代码位于wm_init_exit.c:

CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_OPERATORS, "wm.operator");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_HANDLERS, "wm.handler");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_EVENTS, "wm.event");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_KEYMAPS, "wm.keymap");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_TOOLS, "wm.tool");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_PUB, "wm.msgbus.pub");
CLG_LOGREF_DECLARE_GLOBAL(WM_LOG_MSGBUS_SUB, "wm.msgbus.sub");

上面代码定义了窗口管理的几个日志记录器。它使用了CLG_LOGREF_DECLARE_GLOBAL宏来定义,在定义了static变量同时也定义了一个同名的非静态指针变量。也可直接如action.c:

static CLG_LogRef LOG = {"bke.action"};

在相关模块记录信息时可直接如下使用:

CLOG_INFO(WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
CLOG_ERROR(&LOG, "Pose copy error, pose to:%p from:%p", (void *)to, (void *)from);

到这里,我们并没有将记录器、类型、上下文进行关联,在哪里使它们组合在一起的呢?

参考上面CLOG_AT_SEVERITY等几个宏的定义,在记录信息时,调用了CLOG_ENSURE宏,它在第一次使用该记录器时会调用CLG_logref_init函数

void CLG_logref_init(CLG_LogRef *clg_ref)
{
#ifdef WITH_CLOG_PTHREADS/* 在大多数情况下,初始化静态类型时仅运行一次 */pthread_mutex_lock(&g_ctx->types_lock);
#endifif (clg_ref->type == NULL) {/* 将记录器添加到上下文链表*/clg_ref->next = g_ctx->refs;g_ctx->refs = clg_ref;//根据记录器标识查找类型,如没找到则添加类型并关联到上下文CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);if (clg_ty == NULL) {clg_ty = clg_ctx_type_register(g_ctx, clg_ref->identifier);}
#ifdef WITH_CLOG_PTHREADSatomic_cas_ptr((void **)&clg_ref->type, clg_ref->type, clg_ty);
#elseclg_ref->type = clg_ty;
#endif}
#ifdef WITH_CLOG_PTHREADSpthread_mutex_unlock(&g_ctx->types_lock);
#endif
}static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
{assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);ty->next = ctx->types;ctx->types = ty;strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);ty->ctx = ctx;ty->level = ctx->default_type.level;//这里通过过滤器检查确定当前记录器是否记录到日志,设定flagif (clg_ctx_filter_check(ctx, ty->identifier)) {ty->flag |= CLG_FLAG_USE;}return ty;
}

在初始化记录器时,如果是新的记录器,则链接到上下文中,检查是否有相关的记录器类型,没有则创建一个,也链接到上下文。同时检查过滤器是否包含该类型,如果不包含,则设置flag为不使用。

延后处理记录器和类型的好处是灵活多变,可在任何模块中灵活使用。

当我们在代码中调用了相关记录宏,但可能日志并没有真正输出,是因为有二个地方判定不输出导致的,一个就是冗余级别超出了类型设定的级别;另一个就是过滤器排除了它的输出。也就是通过这二个来更精准地控制日志的输出。

 

最基本的使用如下代码:


static CLG_LogRef LOG = { "CLOG.test" };int main()
{CLG_init();const char* test_filter = "CLOG.*";//必须设置过滤器,默认不显示任何记录//在blender中,可通过启动程序参数来设置需要过滤显示的日志记录,具体代码见creator_args.c中arg_handle_log_set函数CLG_type_filter_include(test_filter, strlen(test_filter));//仅显示level<=1的日志CLOG_INFO(&LOG, 1, "test log!");CLOG_INFO(&LOG, 2, "不会显示!");std::cout << "Hello World!\n";CLG_exit();
}

如果在window环境下控制台不能正确显示中文,可使用下面的代码来设置:

SetConsoleOutputCP(CP_UTF8);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/856352.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

老生常谈——分布式限流:部分Sentinal源码解读

基础知识HTTP CODE = 429 “请求过多”A. 限流的类型服务端客户端限流的标的IP用户...基本要求准确限制过量的请求。低延时。限流器不能拖慢HTTP响应时间。尽量占用较少的内存。这是一个分布式限流器,可以在多个服务器或者进程之间共享。需要处理异常。当用户的请求被拦截时,…

可扩展系统——基于SPI扩展

一、我们为什么讨论SPI? 为具有悠久历史的大型项目(屎山)添加新功能时,我们常常不太好评估变更的影响范围。因为原系统不具备良好的扩展性,导致修改整体发散,且不易单测。此时可以考虑使用接口来描述业务逻辑较为稳定的流程,并使用SPI机制来灵活的隔离加载实际的实现,来…

大模型--采样技术 TopK TopP 惩罚系数--37

目录1. 参考2. 概述重复惩罚(Repetition Penalty) 1. 参考 https://mp.weixin.qq.com/s/mBZA6PaMotJw7WeVdA359g 2. 概述 大型语言模型(LLMs)通过“根据上下文预测下一个 token 的概率分布”来生成文本。最简单的采样方法是贪心采样(Greedy Sampling),它在每一步选择概率…

关于分布式锁的的思考

关于分布式锁的的思考 结论先行: 对于分布式锁我们在考虑不同方案的时候需要先思考需要的效果是什么?为了效率(efficiency),协调各个客户端避免做重复的工作。即使锁偶尔失效了,只是可能把某些操作多做一遍而已,不会产生其它的不良后果。比如重复发送了一封同样的 email(…

2024-12-21:从魔法师身上吸取的最大能量。用go语言,在一个神秘的地牢里,有 n 名魔法师排成一列。每位魔法师都有一个能量属性,有的提供正能量,而有的则会消耗你的能量。 你被施加了一种诅咒,吸

2024-12-21:从魔法师身上吸取的最大能量。用go语言,在一个神秘的地牢里,有 n 名魔法师排成一列。每位魔法师都有一个能量属性,有的提供正能量,而有的则会消耗你的能量。 你被施加了一种诅咒,吸收来自第 i 位魔法师的能量后,你会立即被传送到第 (i + k) 位魔法师。在这个…

平替兼容MFRC523|国产13.56MHz智能门锁NFC读卡芯片KYN523

NFC是一种非接触式识别和互联技术,可以在移动设备、消费类电子产品等设备间进行近距离无线通信。通过 NFC 可实现数据传输、移动支付等功能。 KYN523是一款高度集成的工作在 13.56MHZ 下的非接触读写器芯片,支持符合ISO/IEC 14443 TypeA、ISO/IEC 14443 TypeB 协议的非接触读…

redis-cli (error) NOAUTH Authentication required问题解决

1.查找redis-cli所在目录 which redis-cli2.切换到redis-cli目录3.切换到usr/bin 目录 执行以下命令redis-cli -h ip -p port 4. 验证redis登录密码 auth password5.获取redis数据

快速幂优化高精度乘法

NOI 1.6 12 题目描述题目给出的 \(n\) 最大可以取到 \(100\) ,即计算 \(2^{100}\) ,明显是超过 long long 的 \(2^{63}-1\),所以需要使用高精度来计算幂次方的乘法简单的高精度,即每次计算一个小整数乘上一个大整数循环 \(n\) 次,每次对上一次的积都乘以 \(2\) vector<…

Docker网络基础知识

Docker 网络是 Docker 容器之间以及容器与主机或其他网络资源之间通信的基础。Docker网络基础1.默认网络当你启动一个容器是,如果没有特别指定网络,它会自动连接到Docker的默认桥接网络(bridge network)。 这个默认的桥接网络通常被称为bridge,它允许容器之间通过IP地址相…

川土微代理商深圳|CA-IS3740,CA-IS3741,CA-IS3742高性能四通道数字隔离芯片

CA-IS3740,CA-IS3741,CA-IS3742产品特性 •信号传输速率:DCto150Mbps •宽电源电压范围:2.5Vto5.5V •宽温度范围:‐40Cto125C •无需启动初始化 •默认输出高电平和低电平选项 •优异的电磁抗扰度 •高CMTI:150kV/s(典型值) •低功耗,(典型值): ▪电流为1.5mA/通道(@5…

大学8086汇编debug——关于int3断点之后继续调试的方法

预先 在汇编中打入int 3,然后在debug中利用G,就可以一路运行到断点处。 正文 在断点上可以用U来查看上下代码的位置断点后运行 然后用-g=xxxx:xxxx可以运行到下一个断点,或是直接运行至结束 还可以用-t=xxxx:xxxx逐步运行 注意:xxxx:xxxx是int 3下一个命令的地址