65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (12投票s)

2002年2月24日

CPOL

5分钟阅读

viewsIcon

172976

downloadIcon

6290

易于使用的日志记录和跟踪类。

Sample Image - gui_window.gif

日志模块

此日志模块包含七个类:CLogCFuncLogIStoreLogCWinLogCFileLogCAutoCriticCLogSimpleLock

日志模块的主类是CLog类,在大多数情况下,它应该是应用程序中的单例。不过,单例模式并不是其硬性要求。

第二有用的类是CFuncLog。此类用于在函数进入和离开时记录日志。此外,此类还为开发人员提供了一种方便记录任何数据的方式。该类重载了<<运算符,因此添加要记录的内容非常容易。

日志模块的UML设计

UML Diagram of Classes  
图1. UML设计类继承图

从图1可以看出,模块类分为两部分:

  1. 存储类
  2. 日志类

存储类

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.hcfunclog.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类,还可以设置以下属性:

  • 消息输出格式 - SetMessageFormatGetMessageFormat函数
  • 自动刷新 - SetAutoFlushGetAutoFlush函数。true表示在每次跟踪消息后刷新存储缓冲区。当应用程序处于Alpha测试阶段并且代码中存在一些GPF(通用保护故障)时,此功能非常有用。第二种模式在需要通过日志记录来控制应用程序状态并且时间不是非常关键时很有用——这是日志系统的顶级性能模式。

对于CLog类中的日志记录,有三个函数:

  • LogRawString - 将RAW string无格式地跟踪到存储类
  • 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时,所有跟踪的消息都存储在文件中。所有文件操作都由操作系统同步。

如何使用模块

  1. 创建CLog类的实例
    CLog *m_pLog = new CLog( new CFileLog( "c:\\log.log" ), LOG_MAX_LEVEL, true ); 

    CLog构造函数的第一个参数必须是指向支持IStoreLog接口的类的指针。在模块中,有IStoreLog虚拟类的两个实现:CWinLogCFileLog

    正如您所理解的,CWinLog类创建一个GDI窗口,您可以在其中显示跟踪的消息,而第二个日志类将日志记录到文件中。

    作为输出文件,可以使用任何操作系统设备、命名管道或使用CreateFile API函数语法等的其他东西。

    第二个参数是消息的上限。必须将其设置为所需的上限值。

    因此,如果将其设置为0,则日志输出中将只有ERROR消息。如果将其设置为1,则日志中将包含ERROR和WARNING消息...等等。

    第三个参数告诉CLog类实例,它是否是IStoreLog类实例的父级。默认情况下,此值为true。因此,CLog类在销毁时会删除IStoreLog类实现的实例。

  2. 如果您想记录函数的进入和离开,请这样做:
    CRepTestApp::CRepTestApp()
    {
      CFuncLog log( m_pLog, "CRepTestApp::CRepTestApp" );
      ...
    }

    这样的代码将在日志中添加函数代码的进入和离开。在这里,我使用了自动变量的特性。在构造时,将向日志添加"enter ..."消息,在销毁时,将向日志添加"leave ..."消息。要向日志添加内容,您可以输入这样的代码:

    int something = 100;
    log << something;
    ...

    这样的代码将在日志输出中添加100。

    警告:operator <<以RAW格式存储日志值。

    要正确将消息添加到日志,请使用CFuncLog类的LogString函数。

如果您想将日志存储到其他地方,则必须编写IStoreLog类的实现,并使用其实例作为构造函数参数。CAutoCriticCLogSimpleLock类是Windows临界区API的包装器。这些类实现在一个独立的具有LOGGER命名空间的 النهائية文件中,并且可以被其他IStoreLog类实现自由使用。

历史

第一个Logger模块于2000年实现,之后经过多次重写。我发现,在许多情况下,Logger系统是商业项目的必需品,因此我花了一些时间编写了这样一个易于使用且可扩展的日志/跟踪子系统。

© . All rights reserved.