c++ 日志输出库 spdlog 简介

参考文章:

log库spdlog简介及使用 - 网络资源是无限的 - CSDN博客 log库spdlog简介及使用_fengbingchun的博客-CSDN博客_spdlog

spdLog的使用 - 烟消bug云散的专栏 - CSDN博客 spdLog的使用_skiworld的博客-CSDN博客_spdlog使用

官方参考文档: QuickStart · gabime/spdlog Wiki · GitHub

1. QuickStart · gabime/spdlog Wiki · GitHub

1、下载源码

代码地址在 https://github.com/gabime/spdlog

c++ 日志输出库 spdlog 简介_第1张图片

点击downLoad下载即可。

2、example解析

下载压缩包并解压:使用visual studio 打开vcxproj后缀的项目文件(我用的是VS2013)

c++ 日志输出库 spdlog 简介_第2张图片

在解决方案中找到example.cpp,这个源文件例举了spdlog的各种用法:

首先需要包含spdlog的头文件

#include "spdlog/spdlog.h"

并且要声明spdlog的命名空间

namespace spd = spdlog;

(1)控制台(console)输出日志

使用控制台输出日志的话,需要这两个头文件:

#include
#include

代码如下:

/ Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);


// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");

spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");

auto console = spd::stdout_color_mt("console"); 中“console”为logger名称,可以随意命名。

warn,critical,info 为不同等级的log,输出在控制台会以不同颜色表示。

c++ 日志输出库 spdlog 简介_第3张图片

注意,logger使用完,程序关闭之前需要调用drop函数释放logger对象,否则如果程序没有关闭,就无法再建立同样名称的logger。

在example.cpp中main函数的最后调用了

// Release and close all loggers
spdlog::drop_all();

如果只想关闭console的log,可以这样写:

spd::drop("basic_logger");

(2)basic log

不带滚动,日志文件会一直被写入,不断变大。

// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");

(3)rotating log

滚动日志,当日志文件超出规定大小时,会删除当前日志文件中所有内容,重新开始写入。

从函数声明可以看出,参数max_file_size 规定了文件的最大值,文件内容超过此值就会清空。

rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)

参数max_files 规定了滚动文件的个数。当logger_name存满时,将其名称更改为logger_name.1,再新建一个logger_name文件来存储新的日志。再次存满时,把logger_name.1改名为logger_name.2,logger_name改名为logger_name.1,新建一个logger_name来存放新的日志。max_files 数量为几,就可以有几个logger_name文件用来滚动。

下面的例子运行后生成了三个log文件。

// Create a file rotating logger with 5mb size max and 3 rotated files
//auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 256, 2);
for (int i = 0; i < 10; ++i)
    rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);

c++ 日志输出库 spdlog 简介_第4张图片

每个文件内容如下,后缀数字越大,日志内容越早:

image

image

image

(4)daily log

每天会新建一个日志文件,新建日志文件的时间可自己设定。

// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);

上述代码输出的日志,如果程序不退出的话,就是每天2:30 am创建新的文件。如果一天多次运行这个程序,就会有多个日志文件,如下图:

image

为了把每天的log写到同一个文件中去,参考spdLog的使用_skiworld的博客-CSDN博客_spdlog使用

可以这样写:

//创建文件名类似于: log_2018-01-17.txt  
typedef spdlog::sinks::daily_file_sink dateonly_daily_file_sink_mt;
auto m_logger = spdlog::create("m_logger", "logs/dateonly.txt", 0, 0);
m_logger->info("test daily info");
m_logger->error("test daily error");

(5)flush 将buffer刷入文件

遇到指定级别的日志会立马将缓存输出到文件中,如果不立刻写入,当程序发生崩溃或产生异常而退出时,有些重要log可能还没等写入到文件中。日志的各个级别如下面代码所示:

typedef enum
{
    trace = 0,
    debug = 1,
    info = 2,
    warn = 3,
    err = 4,
    critical = 5,
    off = 6
} level_enum
// trigger flush if the log severity is error or higher       
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
daily_logger->error("Error happended! ");

1、set_pattern 自定义日志格式

官方参考:https://github.com/gabime/spdlog/wiki/3.-Custom-formatting

可以为所有的log制定格式,也可以为指定的log制定格式,注意下面代码中rotating_logger为指针变量。

auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 256, 2);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
//Customize msg format for a specific logger object:
rotating_logger->set_pattern("[%H:%M:%S %f] [thread %t] %v ***");

详细的格式说明在github上有,在此截图如下:

c++ 日志输出库 spdlog 简介_第5张图片

c++ 日志输出库 spdlog 简介_第6张图片

2、set_level 设置日志级别

低于设置级别的日志将不会被输出。各level排序,数值越大级别越高:

// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");

第一行日志debug级别低于设定的级别info,在level为info时不会被输出。

第二行日志debug级别与设定的级别相同,所以可以显示出来。

image

typedef enum
{
    trace = 0,
    debug = 1,
    info = 2,
    warn = 3,
    err = 4,
    critical = 5,
    off = 6
} level_enum;

3、编译阶段修改日志输出级别 SPDLOG_TRACE 和 SPDLOG_DEBUG

官方参考:8. Tweaking · gabime/spdlog Wiki · GitHub

当定义了宏定义 SPDLOG_TRACE_ON 时,可以用SPDLOG_TRACE语句输出trace级别的log,SPDLOG_DEBUG_ON也是同理。

#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);

需要注意的是,如果不使用set_level命令设置log的输出级别,默认级别就是info级别,此时即使定义了这两个宏,debug和trace信息也不会输出。所以使用时需要先用set_level把级别设为trace才可以。

console->set_level(spd::level::trace); // Set specific logger's log level
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);

以下是输出结果:

image

注意trace和debug的输出不太一样,打开spdlog.h,查看SPDLOG_TRACE的定义,可以发现trace中还输出了文件的位置(“__FILE__”)。

#ifdef SPDLOG_TRACE_ON
#  define SPDLOG_STR_H(x) #x
#  define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#  ifdef _MSC_VER
#    define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#  else
#    define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#  endif
#else
#  define SPDLOG_TRACE(logger, ...) (void)0
#endif

#ifdef SPDLOG_DEBUG_ON
#  define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#else
#  define SPDLOG_DEBUG(logger, ...) (void)0
#endif

4、同步和异步设置  Asynchronous logging

官方说明:https://github.com/gabime/spdlog/wiki/6.-Asynchronous-logging

默认情况下是不开启异步模式的,开启异步模式方式如下:

size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);

队列大小:

队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。由此可根据系统的log输出总量来确定队列大小。

c++ 日志输出库 spdlog 简介_第7张图片

队列机制:

异步模式下,所有输出的日志将先存入队列,再由工作者线程从队列中取出日志并输出。当队列存满时,需要根据设置好的队列存满策略来处置新来的日志(阻塞消息或者丢弃消息)。如果工作者线程中抛出了异常,向队列写入下一条日志时异常会再次抛出,可以在写入队列时捕捉工作者线程的异常。

c++ 日志输出库 spdlog 简介_第8张图片

队列存满时(Full queue policy)两种应对方式:

(1)阻塞新来的日志,直到队列中有剩余空间。这是默认的处理方式。

(2)丢弃新来的日志。如下:

spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::discard_log_msg)

两种应对方式如下:

// Async overflow policy - block by default.
//
enum class async_overflow_policy
{
    block_retry, // Block / yield / sleep until message can be enqueued
    discard_log_msg // Discard the message it enqueue fails
};

5、处理spdlog的异常  set_error_handler

官方说明:Error handling · gabime/spdlog Wiki · GitHub

当输出日志时发生异常时,spdlog会向std::err 打印一条语句,为了避免输出的异常语句刷屏,打印频率被限制在每分钟一条。

下面函数执行时,由于最后一条输出log的语句中四个参数只给了一个,所以spdlog调用了异常处理函数,输出异常。

void err_handler_example()
{
    //can be set globaly or per logger(logger->set_error_handler(..))
    spdlog::set_error_handler([](const std::string& msg)
    {
        std::cerr << "my err handler: " << msg << std::endl;
    });
    spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}

image

6、apply_all 使所有的logger同时输出

所有注册过的logger都会输出End of example 这句话,代表程序结束。

// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr l)
{
    l->info("End of example.");
});

7、drop -- 释放logger

在程序结束时,应该调用drop_all() 释放所有logger。

There is a bug in VS runtime that cause the application dead lock when it exits. If you use async logging, please make sure to call spdlog::drop_all() before main() exit. If some loggers are not in the registry, those should be released manually as well. stackoverflow: std::thread join hangs if called after main exits when using vs2012 rc

// Release and close all loggers
spdlog::drop_all();

或者单独drop某个logger

spd::drop("console");
spd::drop("basic_logger");

你可能感兴趣的:(C++学习,c++)