C++plog库,轻量级日志框架(日志库)

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++plog库,轻量级日志框架(日志库)相关的知识,希望对你有一定的参考价值。

文章目录


因为合作方cw提供的库里有个plog,所以查询一下plog的使用方法

具体详细使用方法可以参考:C/C++ plog日志简单用法

主要头文件及使用方法解释

plog/Log.h

这是 plog 库的核心头文件,其中定义了 plog::Logger 类和一些与日志相关的函数和宏定义。使用这个头文件可以满足大部分的日志需求。

plog/Appenders/ColorConsoleAppender.h

这个头文件定义了一个彩色控制台输出的日志附加器,可以将日志以彩色输出到控制台。如果你需要在控制台上打印带有颜色的日志,可以使用这个头文件。

plog/Appenders/RollingFileAppender.h

这个头文件定义了一个滚动文件输出的日志附加器,可以将日志写入到滚动的日志文件中。如果你需要将日志保存到文件中,并且希望文件不会无限制增长,可以使用这个头文件。

plog/Formatters/TxtFormatter.h

这个头文件定义了一个简单的文本格式化器,可以将日志以文本格式输出。如果你不需要复杂的日志格式,可以使用这个头文件。

你可以根据自己的需要选择引入相应的头文件,例如:

#include <plog/Log.h>  // 使用 plog 的核心功能
#include <plog/Appenders/ColorConsoleAppender.h>  // 如果需要彩色输出
#include <plog/Appenders/RollingFileAppender.h>   // 如果需要输出到滚动文件
#include <plog/Formatters/TxtFormatter.h>          // 如果需要简单文本格式输出

一般情况下,只需要引入 plog/Log.h 头文件即可使用 plog 的基本功能,如果需要额外的功能,再引入相应的头文件即可。

三个核心概念

Formatter:格式化程序

用于将日志消息转换为特定格式的字符串,以便记录和输出。plog提供了几种内置格式化程序,如TXT、CSV、FuncMessage和MessageOnly,用户还可以自定义自己的格式化程序。

格式化程序举例

除了下面几种 Formatter,Plog 还提供了一些其他的 Formatter,如 BsonFormatter、XMLFormatter 等。用户可以根据实际需求选择适合的 Formatter 进行使用。

TxtFormatter

TxtFormatter 是 Plog 的默认 Formatter。它将日志信息格式化为一个文本字符串,并添加一些元信息(如时间戳、日志级别等)。TxtFormatter 格式化的文本字符串可以被写入文本文件或控制台输出。TxtFormatter 可以通过设置不同的参数(如是否包含时间戳、是否包含线程 ID 等)进行定制化。

JsonFormatter

JsonFormatter 将日志信息格式化为 JSON 格式的字符串。它可以让日志信息更方便地与其他系统进行交互。JsonFormatter 的输出结果可以被写入文本文件或控制台输出。JsonFormatter 可以通过设置不同的参数(如是否包含时间戳、是否包含线程 ID 等)进行定制化。

CsvFormatter

CsvFormatter 将日志信息格式化为 CSV(Comma-Separated Values)格式的字符串。它可以让日志信息更方便地进行导出和分析。CsvFormatter 的输出结果可以被写入文本文件或控制台输出。CsvFormatter 可以通过设置不同的参数(如是否包含时间戳、是否包含线程 ID 等)进行定制化。

SyslogFormatter

SyslogFormatter 将日志信息格式化为 Syslog 协议规定的格式。Syslog 是一种常见的日志协议,可以让日志信息被发送到远程 syslog 服务器。SyslogFormatter 可以通过设置不同的参数(如 Syslog 服务器地址、Syslog 设备标识符等)进行定制化。

Appender:输出器

用于将格式化后的日志消息输出到目标位置,例如控制台、文件、系统日志等。plog提供了几种内置输出器,如RollingFile、Console、ColorConsole、android、EventLog、DebugOutput和DynamicAppender,用户还可以自定义自己的输出器。

输出器举例

每个Appender都有自己的优点和用途。ConsoleAppender可用于快速测试和调试,RollingFileAppender可用于将日志记录保留在磁盘上,DailyRollingFileAppender可用于按日期存档日志记录,UdpAppender可用于将日志记录发送到远程服务器,而SyslogAppender可用于与类UNIX系统的syslog集成。

可以根据需要使用这些Appender中的任意一个或多个。通常,您可以在一个应用程序中同时使用多个Appender。例如,您可以将日志记录输出到文件和控制台,或将日志记录发送到远程服务器和syslog。

ConsoleAppender

将日志输出到控制台,支持彩色输出。

RollingFileAppender

将日志记录输出到文件,并自动轮转日志文件。支持按文件大小、文件数和日期自动轮转。

DailyRollingFileAppender

将日志记录输出到文件,并根据日期轮转日志文件。

UdpAppender

将日志记录输出到UDP socket。

SyslogAppender

将日志记录输出到syslog(适用于类UNIX系统)。

NullAppender

不将日志记录输出到任何地方。

Logger:记录器

plog支持多个日志级别,可以使用不同级别的记录器输出不同级别的日志。在plog中,有以下几个预定义的日志级别(按照严重程度递增排列):

  • verbose:最低级别的日志,通常用于输出详细的调试信息。
  • debug:用于调试,输出程序的运行状态信息。
  • info:输出普通信息。
  • warning:输出警告信息。
  • error:输出错误信息。
  • fatal:最高级别的日志,输出致命错误信息,通常会导致程序崩溃。

在使用plog时,可以通过设置记录器的级别来控制输出的日志级别。例如,如果只想输出warning级别及以上的日志,可以将记录器的级别设置为warning,这样所有warning、error和fatal级别的日志都会被输出,而info、debug和verbose级别的日志则会被忽略。

使用指定级别记录器过滤日志示例1(支持多个记录器输出,每个记录器单独配置输出方式)

#include <plog/Log.h>
#include "plog/Initializers/RollingFileInitializer.h"

enum // Define log instances. Default is 0 and is omitted from this enum.

    debugLogger = 1,
    infoLogger = 2
;

int main() 
    // 创建文件输出的记录器
    plog::RollingFileAppender<plog::TxtFormatter> fileAppender("log.txt", 1024 * 1024, 5);

    // 创建debug级别的记录器,同时将日志输出到文件
    plog::init<debugLogger>(plog::debug, &fileAppender);

    // 创建info级别的记录器,同时将日志输出到文件
    plog::init<infoLogger>(plog::info, &fileAppender);


    //注:info级别比debug高

    // 1. 使用debug级别的记录器输出debug级别的日志
    PLOG_DEBUG_(debugLogger) << "PLOG_DEBUG message >> debugLogger";

    // 2. 使用info级别的记录器输出debug级别的日志
    PLOG_DEBUG_(infoLogger) << "PLOG_DEBUG message >> infoLogger";  //没输出到文件

    // 3. 使用debug级别的记录器输出info级别的日志
    PLOG_INFO_(debugLogger) << "PLOG_INFO message >> debugLogger";

    // 4. 使用info级别的记录器输出info级别的日志
    PLOG_INFO_(infoLogger) << "PLOG_INFO message >> infoLogger";

    return 0;


编译运行结果:

使用指定级别记录器过滤日志示例2(最常用最简单用法,只需初始化一次,其他源文件导入头文件即可用)(★★★★★)

#include <plog/Log.h>
#include "plog/Initializers/RollingFileInitializer.h"

int main()

    plog::init(plog::warning, "logfile.txt");

    PLOG_VERBOSE << "verbose log";          // 不会输出
    PLOG_DEBUG << "debug log";              // 不会输出
    PLOG_INFO << "info log";                // 不会输出
    PLOG_WARNING << "warning log";          // 会输出
    PLOG_ERROR << "error log";              // 会输出
    PLOG_FATAL << "fatal log";              // 会输出

    return 0;

编译运行结果:

使用方法

简单入门

下载源码:https://github.com/SergiusTheBest/plog/releases/tag/1.1.9

单个源文件打印日志

然后在系统上新建文件夹,把上面include拷贝进去,目录结构大致这样:

然后源文件和编译文件长这样(源文件代码我是从上面README.md拷的):

(testPlog.cpp)

#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 //初始化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	//也可以使用LOG_XXX宏,但它可能与其他日志库冲突(建议不用下面几种,除非有特殊情况)
    LOGD << "Hello log!"; // short macro
    LOG_DEBUG << "Hello log!"; // long macro
    LOG(plog::debug) << "Hello log!"; // function-style macro

    return 0;

(build.sh)

g++ -std=c++14 testPlog.cpp -I./include 

给build.sh添加可执行权限后,运行它编译,多出个a.out文件:

./build.sh

然后运行a.out,目录下多出个日志文件:


查看Hello.txt内容:

日志输出内容解析2023-02-27 10:19:20.384 DEBUG [5461] [main@11] Hello log!

plog输出的内容“2023-02-27 10:19:20.384 DEBUG [5461] [main@11] Hello log!" 可以解释为:

  • 2023-02-27 10:19:20.384 是记录日志的时间戳,格式为“年-月-日 时:分:秒.毫秒”。
  • DEBUG 是日志的级别,表示这是一条 DEBUG 级别的日志。
  • [5461] 表示当前线程的 ID,用方括号括起来。
  • [main@11] 表示当前函数的名称和行号,用方括号括起来。在这个示例中,函数名是 main,行号是 11。
  • Hello log! 是实际的日志内容,即我们调用 LOG_DEBUG 输出的字符串。

多个源文件打印到同一日志文件(只需初始化一次)

目录结构:

(testPlog.cpp)

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

#include "myFunc.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


    func();

    return 0;


(myFunc.cpp)

#include "myFunc.h"

int func()

    PLOGD << "Hello log!"; // short macro
    PLOG_DEBUG << "Hello log!"; // long macro
    PLOG(plog::debug) << "Hello log!"; // function-style macro

    return 0;


(myFunc.h)

#ifndef FUNCTION_H_
#define FUNCTION_H_

#include <plog/Log.h>

int func();

#endif // FUNCTION_H_

(build.sh)

g++ -std=c++14 *.cpp -I./include 

还是跟上面一样,./build.sh && ./a.out编译运行,打开结果日志文件Hello.txt查看结果:

多个源文件打印到不同日志文件(multiple loggers)

这里没用多个源文件哈,但效果也差不多,初始化时,将枚举值作为参数传入,打印时也将枚举值带上,就能输出到指定文件了

(testPlog.cpp)

//
// MultiInstance - shows how to use multiple logger instances, each instance has its own independent configuration.
//

#include <plog/Log.h>
#include <plog/Initializers/RollingFileInitializer.h>

enum // Define log instances. Default is 0 and is omitted from this enum.

    SecondLog = 1
;

int main()

    plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance.
    plog::init<SecondLog>(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance.

    // Write some messages to the default log.
    PLOGD << "Hello default log!";
    PLOG_DEBUG << "Hello default log!";
    PLOG(plog::debug) << "Hello default log!";

    // Write some messages to the 2nd log.
    PLOGD_(SecondLog) << "Hello second log!";
    PLOG_DEBUG_(SecondLog) << "Hello second log!";
    PLOG_(SecondLog, plog::debug) << "Hello second log!";

    return 0;


(build.sh)

g++ -std=c++14 *.cpp -I./include 

编译运行:

./build.sh

结果:

日志打印到多个输出器(multiple appenders)

目录结构:

(testPlog.cpp)

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

int main()

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

    PLOGD << "Hello log!"; // short macro
    PLOG_DEBUG << "Hello log!"; // long macro
    PLOG(plog::debug) << "Hello log!"; // function-style macro

    return 0;


(build.sh)

g++ -std=c++14 *.cpp -I./include 

编译运行:

./build.sh

结果:

可以看到,相同的日志被输出到了多个地方,1是打印到了控制台,2是MultiAppender.csv文件,3是MyTxtFormatter.txt

输出彩色日志到控制台

//
// ColorConsole - shows how to use a color console appender.
//

#include <plog/Log.h>
#include <plog/Init.h>
#include <plog/Formatters/TxtFormatter.h>
#include <plog/Appenders/ColorConsoleAppender.h>

int main()

    static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
    plog::init(plog::verbose, &consoleAppender);

    // Log severity levels are printed in different colors.
    PLOG_VERBOSE << "This is a VERBOSE message";
    PLOG_DEBUG << "This is a DEBUG message";
    PLOG_INFO << "This is an INFO message";
    PLOG_WARNING << "This is a WARNING message";
    PLOG_ERROR << "This is an ERROR message";
    PLOG_FATAL << "This is a FATAL message";

    return 0;


编译运行结果:

其他说明

当代码中包含多个初始化文件时,调用plog怎么知道输出到哪个初始化文件?

当在多个源文件中使用PLOGD等宏时,plog会自动查找最近的plog::init初始化函数并将日志写入该函数指定的日志文件。如果在多个源文件中使用相同的plog::init初始化函数,则所有日志消息都将写入同一个文件。

在plog库中,日志输出器的初始化函数是通过C++静态初始化的方式进行注册的。在程序启动时,这些初始化函数会被按照它们的定义顺序执行,将日志输出器注册到全局日志器中。在编译时,编译器会将所有的初始化函数按照它们的定义顺序加入到可执行文件的初始化段(.init_array),在程序运行时会自动执行这些函数。

当程序执行到某个PLOG宏的时候,plog会从当前函数所在的源文件向上遍历所有调用栈帧,查找是否有已经注册的日志输出器,如果有,则使用最近的一个输出器进行输出。这个查找过程并不会通过文件系统来实现,而是通过静态初始化过程中生成的初始化段来完成。因此,plog不会查找文件系统来确定最近的日志输出器,而是通过静态链接关系来查找。如果没有找到已注册的日志输出器,则会使用默认的输出器。

因此,可以在不同的源文件中通过不同的初始化函数来初始化不同的日志输出器,并在运行时通过PLOG宏来输出日志,plog会根据静态链接关系找到最近的输出器来进行输出。

根据我的测试,当代码中包含多个初始化文件时,而打印时又没指定是哪一个,打印会把所有文件,都输出一次

当plog::init()函数中的maxFileSize和maxFiles参数任一为0,取消滚动打印功能(它们默认就是0)

Logger& init(Severity maxSeverity, const char/wchar_t* fileName, size_t maxFileSize = 0, int maxFiles = 0);
  • maxFileSize - the maximum log file size in bytes(单个日志文件最大字节数)
  • maxFiles - a number of log files to keep(滚动打印维持的最大日志文件数量)

延迟流评估是什么?(Lazy stream evaluation)

延迟流评估(Lazy stream evaluation)是一种日志记录技术,可以将日志消息的格式化延迟到实际需要输出消息时再进行,以提高性能和降低资源占用。

在使用延迟流评估技术时,当用户使用日志记录器记录一条消息时,不会立即将消息转换为字符串格式,而是将消息的内容保存到一个缓存区中,直到真正需要输出消息时,才将缓存区中的内容格式化为字符串,然后输出到目标位置。

使用延迟流评估技术可以有效减少不必要的字符串格式化操作,从而提高程序的性能和响应速度。同时,由于延迟流评估只在需要输出消息时才进行格式化,因此可以避免无效的日志记录对系统性能和资源的影响。

在plog日志库中,延迟流评估是一种可选功能,用户可以根据需要选择是否启用它。默认情况下,plog启用延迟流评估,并提供了相应的支持。

自动捕获’this’指针功能有什么用?

自动捕获’this’指针是一种日志记录技术,用于自动记录当前对象的指针,以便在日志消息中输出有关对象的信息,从而更好地跟踪和调试程序。

在使用自动捕获’this’指针技术时,当用户使用日志记录器记录一条消息时,plog库会自动捕获当前对象的指针,并将其添加到日志消息中,以便用户在查看日志时能够识别与对象相关的消息,并更好地理解程序的行为和状态。

自动捕获’this’指针技术可以帮助用户更好地跟踪和调试程序,尤其是在多线程环境下或对象较多的复杂程序中。同时,由于自动捕获’this’指针是自动完成的,因此可以减少用户在代码中插入日志记录语句的工作量,提高开发效率。

需要注意的是,自动捕获’this’指针只在支持C++11的编译器中受支持,而且只有在MSVC编译器下才能进行自动捕获。在其他编译器或平台下,用户需要手动指定对象指针并将其添加到日志消息中。

以上是关于C++plog库,轻量级日志框架(日志库)的主要内容,如果未能解决你的问题,请参考以下文章

C/C++ plog日志简单用法

C/C++ plog日志简单用法

C/C++ plog日志简单用法

调试利器!一款轻量级日志库 log.c

调试利器!一款轻量级日志库 log.c

分享一个简单且轻量级的日志库:log.c