在软件开发过程中,日志记录是一个重要的环节,它帮助开发者追踪程序的运行状态,定位问题。然而,传统的日志系统往往存在一些不足,比如日志信息过多导致难以管理,或者日志级别设置不够灵活等。本文将介绍一种动态日志系统的设计思路,旨在解决这些问题,提供一种更灵活、更易于使用的日志记录方式。
日志系统的设计需要在记录尽可能多的信息和避免日志文件过大之间找到平衡。通常的解决方案是设置不同的日志级别,如致命错误(Fatal)、错误(Error)、警告(Warning)、信息(Info)、调试(Debug)和跟踪(Trace)。但是,这种方法存在一些问题:
希望设计一种解决方案,以解决上述不足。基本需求包括:
幸运的是,可以使用一组类和C宏来满足所有定义的需求。
假设有一个标准的日志工具,例如:
C++ MyLog("the message");
它简单地将消息写入日志文件:
<the message>
假设代码中的每个调试日志条目都被替换为一个块。在块中,基于唯一的字符串标识符为该消息分配一个唯一的ID。如果该ID注册了日志记录,则使用标准的MyLog,其中包含所有现有的逻辑,如字符串构造和根据从XML读取的配置决定是否报告到日志文件。否则,什么也不做。
C++ {
static int i = -1;
const char * sDynalogId = "namespace::class::func.location";
if(-1 == i) {
i = AddReportable(sDynalogId);
}
if(IsReportable(i)) {
MyLog(sDynalogId << ": " << "the message");
}
}
虽然这是功能性的,但它非常繁琐。给定一个宏来包装那段代码:
C++ #define DYNALOG(_Src, ...) \
do { \
static int i = -1; \
if(-1 == i) { \
i = NDYNALOG::AddReportable(_Src, false); \
} \
if(NDYNALOG::IsReportable(i)) { \
MyLog(_Src << ": " << __VA_ARGS__); \
} \
} while(0)
上述代码变为:
C++ DYNALOG("MyClass::DoIt", "Entered");
例如:
C++ void MyClass::DoIt() {
DYNALOG("MyClass::DoIt", "Entered");
// Do something
DYNALOG("MyClass::DoIt.Leave", "Exited");
}
在配置文件中表示为:
C++ 0 MyClass::DoIt 0 MyClass::DoIt.Leave
要记录这两个条目,或者更改为1并重新加载配置文件:
C++ 0 MyClass::DoIt 1 MyClass::DoIt.Leave
只记录"Exited"消息。
配置文件是一个文本文件,可以在记事本中编辑。每条消息的唯一日志标签都在单独的一行上,第一字段是一个布尔值,表示是否需要记录日志。该行的其余部分是控制日志的标签。
虽然可以手动向文件中添加条目,但不建议这样做,因为出错的机会很高。建议的做法是运行应用程序,让它将标签收集到缓存中,然后执行保存DYNALOG配置操作。
保存/加载的样本配置文件格式如下(在Windows上):
C++ 0 f 0 f 0 f
在Linux上:
C++ 0 f 0 void f(T) [with T = char*] 0 void f(T) [with T = int]