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

C++ 的简单日志记录器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (32投票s)

2013 年 4 月 30 日

CPOL

4分钟阅读

viewsIcon

122264

downloadIcon

4712

一个简单且可扩展的 C++ 日志记录器。

概述 

最近,我一直在寻找一种相对容易使用的 C++ 应用程序日志记录功能。市面上有很多优秀的日志框架。我寻找的不是使用或复制代码,而是日志记录的方法。然后我了解了 Log 4 C++,它是一个不错的框架。我在此介绍的这个日志类就是基于 Log 4 C++ 的思想,因为它与 Log 4 C++ 的使用模式相似。大多数日志框架提供大量的调整和格式化选项,而我这个小类并没有这些,正因如此,我觉得它非常易于使用。整个代码包含在一个单独的 .h 文件中,易于理解,因此可以轻松修改以满足个人需求。请注意,代码使用了一些 C++11 特性。但是,将其移植到旧规范应该相对容易;例如,将 'enum class' 替换为 'enum'。访问 http://en.wikipedia.org/wiki/C%2B%2B11 获取更多信息。示例代码在 Visual Studio 2012 中编译通过。

设计   

我们需要关注的主要类是 CLogger 类。它位于 'framework:: Diagnostics' 命名空间下。CLogger 借助另一个类来实现线程同步功能。目前,有两个类可以帮助实现这一点,它们都位于 framework::Threading 命名空间下。其中一个称为 CIntraProcessLock。如果将在同一进程中的多个线程之间共享 CLogger 实例,请使用它。如果您不跨线程共享日志记录器对象,请使用 CNoLock 类。创建日志记录器实例时,您需要至少提供其中一个类,或者提供任何其他提供语义正确的 Lock()Unlock() 功能的类。一旦创建了 CLogger 实例,我们就需要添加 ostream 派生对象,它们负责执行将内容写入某处的操作。最后,要记录内容,请使用 WRITELOG 宏。

用法 

创建日志记录器。示例

using namespace framework::Diagnostics;
using namespace framework::Threading;
CLogger<CNolock> logger(LogLevel::Info, _T("MyApp"));

构造函数具有以下参数

  • LogLevel - 可以是 framework::Diagnostics::LogLevel 枚举的 enum 值之一。此值影响记录的内容。添加输出流时使用此值。
  • Name - 分配给日志记录器对象的名称。此名称可以出现在每行日志中。我通常为每个模块创建一个日志记录器对象,并将模块名称在此处指定。 
  • logItems - 这是一个位掩码,列出了我们希望输出到每行日志的项目。值应取自 framework::Diagnostics::LogItem 枚举。默认情况下,记录以下项目
日志项 默认选中 
文件名
行号
函数名
日期时间
线程ID
日志器名称
日志级别

假设您只想记录日期-时间以及实际的日志语句,您可以像这样创建一个日志记录器对象

CLogger<CNoLock> logger(LogLevel::Info, _T("MyApp"), static_cast<int>(LogItem::DateTime));

请注意,无法阻止实际日志语句被写入,因此没有用于它的 LogItem

下一步是添加输出流。对于您添加的每个输出流,您可以指定日志级别。例如,我们可以添加 'cout' 并指定日志级别为 Error。这意味着将记录指定级别及所有更高等级的内容。

同样,如果添加了一个文件流并指定 Info 作为日志级别,那么所有高于 Info 的级别以及 Info 本身都将被记录,实际上意味着记录所有内容,因为所有其他级别都高于 Info(请参阅 LogLevel enum)。示例

logger.AddOutputStream(std::wcout, false, LogLevel::Error);
logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), 
       true, framework::Diagnostics::LogLevel::Info);

addOutputStream 具有以下参数

  • os - 传递任何 ostream 派生类,例如 coutwcout
  • own - 如果为 trueCLogger 实例将使用 delete 运算符删除传入的 ostream 对象。这通常在创建新的文件流实例并将其传入时很有用(请参见上面的代码)。
  • LogLevel - 对于此设备(输出流),将写入所有等于或高于指定级别的日志条目。如果未指定,将使用 CLogger 对象上指定的日志级别来决定记录什么。最后,要执行日志记录本身,建议使用 WRITELOG, LOGINFO, LOGDEBUG, LOGWARN 或 LOGERROR 宏。示例
WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting"));
WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T("Something may have gone wrong"));
WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong"));
WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program Ending"));

// Or use shortcut macros like LOGINFO, LOGDEBUG, LOGWARN, LOGERROR. E.g.
LOGINFO(logger, _T("Program Ending"));

// If you have a pointer to a logger object e.g:
CLogger<cnolock>* loggerPtr = &logger; 
LOGINFOP(loggerPtr, _T("Program Ending"));

</cnolock>

如果您有一个指向 logger 对象的指针,可以使用相同的宏,后缀为 'P'(P 代表指针),即 WRITELOGP, LOGINFOP, LOGDEBUGP, LOGWARNPLOGERRORP

整个代码都在一个地方

#include "threading.h"
#include "Logger.h"
#include <fstream>

using namespace framework::Diagnostics;
using namespace framework::Threading;

int _tmain(int argc, _TCHAR* argv[])
{
    CLogger<CNolock> logger(LogLevel::Info, _T("MyApp"));

    logger.AddOutputStream(std::wcout, false, LogLevel::Error);
    logger.AddOutputStream(new std::wofstream("c:\\temp\\myapp.log"), 
           true, framework::Diagnostics::LogLevel::Info);
        
    WRITELOG(logger, framework::Diagnostics::LogLevel::Info, _T("Program starting"));
    WRITELOG(logger, framework::Diagnostics::LogLevel::Warn, _T
            ("Something may have gone wrong"));
    WRITELOG(logger, framework::Diagnostics::LogLevel::Error, _T("Something did go wrong"));
    CLogger<CNoLock>* loggerPtr = &logger; // An easy way to use pointer to a logger object
	LOGINFOP(loggerPtr, _T("Program Ending"));
    return 0;
}

 

 

谢谢   

用于执行线程同步的模板类的使用基于策略类(请参阅 Andrei Alexander 的《Modern C++ Design》)。Log 4 C++ 启发了该类的整体设计。

感谢我的妻子 Meena 一直以来的支持,以及我的儿子 Neel,他展示了拥有快乐生活有多么简单。

历史

  • 2015 年 6 月 6 日:初始版本
© . All rights reserved.