【C++】plog

GitHub地址:Plog - portable, simple and extensible C++ logging library

介绍

Plog is一个C++日志库,旨在保持尽可能的简单、小巧和灵活。它被创建作为现有大型库的替代品,并提供了一些独特的功能,如CSV日志格式和宽字符串支持。

主打一个小,灵活吗?

先来一段Demo

#include <plog/Log.h> // Step1: include the headers
#include "plog/Initializers/RollingFileInitializer.h"

int main()
{
    plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger

    // Step3: write log messages using a special macro
    // There are several log macros, use the macro you liked the most

    PLOGD << "Hello log!"; // short macro
    PLOG_DEBUG << "Hello log!"; // long macro
    PLOG(plog::debug) << "Hello log!"; // function-style macro
    
    // Also you can use LOG_XXX macro but it may clash with other logging libraries
    LOGD << "Hello log!"; // short macro
    LOG_DEBUG << "Hello log!"; // long macro
    LOG(plog::debug) << "Hello log!"; // function-style macro

    return 0;
}

输出:

2015-05-18 23:12:43.921 DEBUG [21428] [main@13] Hello log!
2015-05-18 23:12:43.968 DEBUG [21428] [main@14] Hello log!
2015-05-18 23:12:43.968 DEBUG [21428] [main@15] Hello log!

日志有的都有了,怎么设置日志的格式,模式, 怎么输出到文件呢?

Here is the translation of the provided information:

  • 非常小(略多于1000行代码)
  • 易于使用
  • 仅包含头文件
  • 没有第三方依赖
  • 跨平台支持:Windows、Linux、FreeBSD、macOS、Android、RTEMS(gcc、clang、msvc、mingw、mingw-w64、icc、c++builder)
  • 线程安全和类型安全
  • 格式化器:TXT、CSV、FuncMessage、MessageOnly
  • 输出器:RollingFile、Console、ColorConsole、Android、EventLog、DebugOutput、DynamicAppender
  • 自动捕获 ‘this’ 指针(仅在 msvc 上支持)
  • 惰性流评估
  • 兼容 Unicode,文件以 UTF-8 存储,支持 Utf8Everywhere
  • 不要求 C++11
  • 可扩展
  • 没有对 windows.h 的依赖
  • 可使用 UTC 或本地时间
  • 可以以 HEX 或 ASCII 打印缓冲区
  • 可以打印 std 容器
  • 使用现代 CMake

1000多行就实现了,还是有点厉害啊,有机会学习一波

使用说明

Step1 导入头文件

你需要执行以下步骤:

  1. 将 plog/include 添加到项目的包含路径中。
  2. 在你的 cpp/h 文件中添加 #include <plog/Log.h>(如果你使用了预编译头文件,将这个 include 添加到那里是一个不错的选择)。

Step2 初始化

Logger& init(
	Severity maxSeverity, 
	const char/wchar_t* fileName, 
	size_t maxFileSize = 0, 
	int maxFiles = 0
);

-maxSeverity 是日志记录器的严重性上限。所有日志消息都有其自己的严重性,如果它高于设定的上限,那么这些消息将被丢弃。Plog 定义了以下严重性级别:

enum Severity
{
    none = 0, // will always be printed
    fatal = 1,
    error = 2,
    warning = 3,
    info = 4,
    debug = 5,
    verbose = 6
};

日志格式会根据 fileName 文件扩展名自动确定:

  • .csv => CSV 格式
  • 其他任何扩展名 => TXT 格式

滚动行为由 maxFileSizemaxFiles 参数控制:

  • maxFileSize - 日志文件的最大大小(以字节为单位)
  • maxFiles - 保留的日志文件数量

如果其中一个参数的值为零,则禁用日志滚动。

示例:

plog::init(plog::warning, "c:\\logs\\log.csv", 1000000, 5);

在这里,日志记录器被初始化为将所有最高严重性为警告的消息写入以 CSV 格式存储的文件。最大日志文件大小设置为 1,000,000 字节,同时保留 5 个日志文件。

Step3 打log

Long macros:

PLOG_VERBOSE << "verbose";
PLOG_DEBUG << "debug";
PLOG_INFO << "info";
PLOG_WARNING << "warning";
PLOG_ERROR << "error";
PLOG_FATAL << "fatal";
PLOG_NONE << "none";

Short macros:

PLOGV << "verbose";
PLOGD << "debug";
PLOGI << "info";
PLOGW << "warning";
PLOGE << "error";
PLOGF << "fatal";
PLOGN << "none";

Function-style macros:

PLOG(severity) << "msg";

Conditional logging macros

PLOG_VERBOSE_IF(cond) << "verbose";
PLOG_DEBUG_IF(cond) << "debug";
PLOG_INFO_IF(cond) << "info";
PLOG_WARNING_IF(cond) << "warning";
PLOG_ERROR_IF(cond) << "error";
PLOG_FATAL_IF(cond) << "fatal";
PLOG_NONE_IF(cond) << "none";

PLOGV_IF(cond) << "verbose";
PLOGD_IF(cond) << "debug";
PLOGI_IF(cond) << "info";
PLOGW_IF(cond) << "warning";
PLOGE_IF(cond) << "error";
PLOGF_IF(cond) << "fatal";
PLOGN_IF(cond) << "none";

Appender

有时候我们想在终端和文件同时输出,就需要用到Appender的概念了。你需要构造一个带有格式化器的 Appender,并将其传递给 plog::init 函数。

Logger& init(Severity maxSeverity = none, IAppender* appender = NULL);
static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender;
plog::init(plog::debug, &consoleAppender);

如何使用多个Appender? 可以在一个单独的日志记录器中拥有多个 Appender。在这种情况下,日志消息将被写入所有这些 Appender。使用以下方法来实现这一点:

Logger& Logger::addAppender(IAppender* appender);

示例:

 // Create the 1st appender.
static plog::RollingFileAppender<plog::CsvFormatter> fileAppender("MultiAppender.csv", 8000, 3);
 // Create the 2nd appender.
static plog::ConsoleAppender<plog::TxtFormatter> consoleAppender;
// Initialize the logger with the both appenders.
plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); 

跨模块共享log

对于由多个二进制模块组成的应用程序,plog 实例可以是本地的(每个模块都有自己的实例)或共享的(所有模块使用相同的实例)。在共享的情况下,你只需在一个模块中初始化 plog,其他模块将重用该实例。
在 Windows 上进行共享,一个模块应该使用 PLOG_EXPORT,而其他模块应该使用 PLOG_IMPORT。此外,在 Linux/Unix 上要小心,如果不指定共享行为,它将由编译器设置(-fvisibility)来确定。

其实我觉得共享 Appender 可能操作起来比较方便,

比如在 exe 里面有一个日志:

plog::Severity maxSeverity = plog::debug;
std::string logPath = "mylog.log";
//max 5MB, loop 3 files
static plog::RollingFileAppender<plog::TxtFormatter> fileAppender(logPath.c_str(), 1024 * 1024 * 5, 3);

// Create the 2nd appender.
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
// Initialize the logger with the both appenders.
plog::init(maxSeverity, &fileAppender).addAppender(&consoleAppender); 

mylib.init(maxSeverity , plog::get())

在lib 中定义:

void init(int logSeverity, plog::IAppender* appender)
{
	if (plog::get() == nullptr) {
		if (appender != nullptr) {
			if (logSeverity >= 0 && logSeverity <= 6) {
				plog::init((plog::Severity)logSeverity, appender);
			}
			else {
				plog::init(plog::Severity::verbose, appender);
			}
		}
	}
}

这样可能在实际开发中,可能比较好操作一点。