适用于各种应用程序的高级日志记录






4.72/5 (12投票s)
易于使用的日志记录和跟踪类。
日志模块
此日志模块包含七个类:CLog
、CFuncLog
、IStoreLog
、CWinLog
、CFileLog
、CAutoCritic
、CLogSimpleLock
。
日志模块的主类是CLog
类,在大多数情况下,它应该是应用程序中的单例。不过,单例模式并不是其硬性要求。
第二有用的类是CFuncLog
。此类用于在函数进入和离开时记录日志。此外,此类还为开发人员提供了一种方便记录任何数据的方式。该类重载了<<
运算符,因此添加要记录的内容非常容易。
日志模块的UML设计
图1. UML设计类继承图
从图1可以看出,模块类分为两部分:
- 存储类
- 日志类
存储类
IStoreLog
的声明
//////////////////////////////////////////////////////////////////////////
// Abstract class set three default function which must support any child.
// Any child must support buffered and non-buffered store
class IStoreLog
{
public:
virtual ~IStoreLog( void ){}; // virtual destructor
virtual int FlushData() = 0;
virtual int WriteString( const std::string &Message ) = 0;
virtual int SetBufferLimit( long lSize ) = 0;
};
存储类具有数据缓冲和刷新功能。默认情况下,存储类必须将其数据存储在自己的缓冲区中,只有在调用FlushData
函数时,它才会将缓冲区数据刷新到磁盘或其他地方。您知道,存储类的缓冲区在大多数情况下受到系统资源的限制,因此当缓冲区达到极限时,它将自动刷新数据。要设置缓冲区限制,请使用SetBufferLimit
函数。默认情况下,Storage
类实现必须分配一个缓冲区,并且只有在调用SetBufferLimit
函数时才会更改其大小。
要将string
存储到存储中,请使用WriteString
函数。Storage
类应没有任何格式化,并按原样存储RAW数据。
日志类
在本节中,我们有两个类:CLog
- 我们的主类,以及CFuncLog
- 辅助类。CLog
类声明在clog.h文件中,其实现位于clog.cpp文件中。CFuncLog
类声明在cfuncLog.h、cfunclog.cpp文件中。
Logger类具有特殊的函数,可以简化日志记录。在记录日志时,您可以配置跟踪输出:如果您不需要时间,只需使用SetLogTime
函数将CLog
类的标志设置为false
,时间就不会添加到输出中。此外,您可以通过调用SetTimeFormat
来更改时间的输出格式。默认情况下,该类使用Long
时间格式。在头文件中可以找到两种最常用的时间格式的定义。第一个是默认的类格式,第二个是没有毫秒输出的短格式。
#define DEF_TIME_LONG_STR "%02u:%02u:%02u ms:%03u"
#define DEF_TIME_SHORT_STR "%02u:%02u:%02u"
警告:时间格式化字符串必须始终包含4个或更少的printf格式模板,否则将导致堆栈错误。
此外,对于CLog
类,还可以设置以下属性:
- 消息输出格式 -
SetMessageFormat
和GetMessageFormat
函数 - 自动刷新 -
SetAutoFlush
和GetAutoFlush
函数。true
表示在每次跟踪消息后刷新存储缓冲区。当应用程序处于Alpha测试阶段并且代码中存在一些GPF(通用保护故障)时,此功能非常有用。第二种模式在需要通过日志记录来控制应用程序状态并且时间不是非常关键时很有用——这是日志系统的顶级性能模式。
对于CLog
类中的日志记录,有三个函数:
LogRawString
- 将RAWstring
无格式地跟踪到存储类LogString
- 带有特殊级别和格式的消息跟踪。有两个同名函数,唯一的区别是输出string
的格式,第一个是std::string
,第二个是简单的char *
。LogFormatString
- 格式化函数 - 包装printf
函数
在许多情况下,开发人员需要三种以上的消息类别,因此在CLog
类中有一个虚拟函数LevelText
。作为输入参数,它将获得所需的LEVEL编号。函数将返回一个带有类别名称的string
。默认情况下,类别名称类设置限制为12个符号,但这可以通过SetMessageFormat
函数模板string
进行更改。
要在应用程序中使用日志记录,请将以下头文件包含到项目中:
#include "clog.h"
#include "cfunclog.h"
#include "cwinlog.h" // include it if you want logging into GUI window
#include "cfilelog.h" // include it if you want logging into files
...
CFuncLog
类不是必需的 - 它只是为了简化最常用功能的日志记录,例如:函数的进入和离开。所有必要的格式化和日志记录操作都在CLog
文件中实现。因此,您可以选择:使用它或不使用它。
注意:CWinLog
类将跟踪的消息存储在窗口中,并且不是多进程安全的。否则,对于多进程日志记录,只能使用CFileLog
类。使用CFileLog
时,所有跟踪的消息都存储在文件中。所有文件操作都由操作系统同步。
如何使用模块
- 创建
CLog
类的实例CLog *m_pLog = new CLog( new CFileLog( "c:\\log.log" ), LOG_MAX_LEVEL, true );
CLog
构造函数的第一个参数必须是指向支持IStoreLog
接口的类的指针。在模块中,有IStoreLog
虚拟类的两个实现:CWinLog
和CFileLog
。正如您所理解的,
CWinLog
类创建一个GDI窗口,您可以在其中显示跟踪的消息,而第二个日志类将日志记录到文件中。作为输出文件,可以使用任何操作系统设备、命名管道或使用
CreateFile
API函数语法等的其他东西。第二个参数是消息的上限。必须将其设置为所需的上限值。
因此,如果将其设置为
0
,则日志输出中将只有ERROR消息。如果将其设置为1
,则日志中将包含ERROR和WARNING消息...等等。第三个参数告诉
CLog
类实例,它是否是IStoreLog
类实例的父级。默认情况下,此值为true
。因此,CLog
类在销毁时会删除IStoreLog
类实现的实例。 - 如果您想记录函数的进入和离开,请这样做:
CRepTestApp::CRepTestApp() { CFuncLog log( m_pLog, "CRepTestApp::CRepTestApp" ); ... }
这样的代码将在日志中添加函数代码的进入和离开。在这里,我使用了自动变量的特性。在构造时,将向日志添加
"enter ..."
消息,在销毁时,将向日志添加"leave ..."
消息。要向日志添加内容,您可以输入这样的代码:int something = 100; log << something; ...
这样的代码将在日志输出中添加100。
警告:
operator <<
以RAW格式存储日志值。要正确将消息添加到日志,请使用
CFuncLog
类的LogString
函数。
如果您想将日志存储到其他地方,则必须编写IStoreLog
类的实现,并使用其实例作为构造函数参数。CAutoCritic
和CLogSimpleLock
类是Windows临界区API的包装器。这些类实现在一个独立的具有LOGGER
命名空间的 النهائية文件中,并且可以被其他IStoreLog
类实现自由使用。
历史
第一个Logger模块于2000年实现,之后经过多次重写。我发现,在许多情况下,Logger
系统是商业项目的必需品,因此我花了一些时间编写了这样一个易于使用且可扩展的日志/跟踪子系统。