51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

C++日志记录库spdlog介绍

spdlog简介 {#spdlog简介}

spdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog),具有以下特点:

  • 配置特别简单,仅包含头文件即可;
  • 写日志方式简单明了;
  • 可实现自动按日期创建日志文件/定时创建日志文件;
  • 可自定义日志格式;
  • 可以输出当前输出日志所在的文件及函数;
  • 可自定义文档大小;
  • 可将不同级别的信息输出到不同日志文件;
  • 多平台等。

spdlog中各对象都分为多线程与单线程版本:

  • *_st:单线程版本,不用加锁,效率更高。
  • *_mt:多线程版本,用于多线程程序是线程安全的。

日志记录槽sink {#日志记录槽sink}

spdlog定义了几种sinks用于不同场景(也可自定义)下的日志输出,sink中主要包含:

  • set_pattern(const std::string&):设置日志输出的内容格式。
  • set_level(level_enum): 设置日志输出的最低等级。
  • log(log_msg):由logger自动调用,外部不会主动调用。

日志记录器logger {#日志记录器logger}

一个logger对象中存储有多个sink,当调用logger的日志输出函数时,logger会调用自身存储的所有sink对象的log(log_msg) 函数进行输出。logger中主要包括:

  • set_pattern(const std::string&):设置logger包含的所有sink的日志输出内容格式。
  • set_level(level_enum):设置logger日志输出最低等级,如果logger包含的sink没有设置日志等级的话,则会为其设置日志等级。
  • log(level_enum level,log_msg content):按照level等级进行输出content,logger其中日志输出最低等级小于或等于level的sink会进行执行输出操作。
  • trace(content,arg1,arg2...):按照trace等级进行输出,输出内容由content与后面的参数格式化而成。同类的函数还包括:debug/info/warn...。

输出格式pattern {#输出格式pattern}

通过set_pattern可设定日志格式,如set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v");

输出标记flag:

| flag | meaning | example | |-------|-----------------------------------------------------------------------|------------------------------------------------------------------| | %v | 日志内容 | "my log test content" | | %t | 线程ID | "123" | | %P | 进程ID | "234" | | %n | 记录器Logger名 | "basicLogger" | | %l | 日志级别 | "debug", "info", etc | | %L | 日志级别简称 | "D", "I", etc | | %a | 星期几(简称) | "Thu" | | %A | 星期几 | "Thursday" | | %b | 月份简称 | "Aug" | | %B | 月份 | "August" | | %c | 日期时间 | "Thu Aug 23 15:35:46 2014" | | %C | 年(两位) | "14" | | %Y | 年 | "2014" | | %D %x | 日期简写 | "08/23/14" | | %m | 月份(数字) | "11" | | %d | 日(数组) | "29" | | %H | 小时(24制) | "23" | | %I | 小时(12制) | "11" | | %M | 分钟 | "59" | | %S | 秒 | "58" | | %e | 毫秒 | "678" | | %f | 微秒 | "056789" | | %F | 纳秒 | "256789123" | | %p | AM/PM | "AM" | | %r | 时间(12制) | "02:55:02 pm" | | %R | 时分(24制) | "23:55" | | %T %X | 时间(24制) | "23:55:59" | | %z | 时区(偏移) | "+02:00" | | %E | epoch(秒) | "1528834770" | | %% | 百分号 | "%" | | %+ | 默认格式 | "[2014-10-31 23:46:59.678] [mylogger] [info] Some message" | | %^ | start color range (can be used only once) | "[mylogger] [info(green)] Some message" | | %$ | end color range (for example %^[+++]%$ %v) (can be used only once) | [+++] Some message | | %@ | 文件名与行数 | my_file.cpp:123 | | %s | 文件名 | my_file.cpp | | %g | 文件名(含路径) | /some/dir/my_file.cpp | | %# | 行数 | 123 | | %! | 函数名 | my_func | | %o | 相对上一条记录的时间间隔(毫秒) | 456 | | %i | 相对上一条记录的时间间隔(微秒) | 456 | | %u | 相对上一条记录的时间间隔(纳秒) | 11456 | | %O | 相对上一条记录的时间间隔(秒) | 4 |

日志输出中要携带文件名、行数或函数名时,必须使用SPDLOG_LOGGER_*宏,且要激活对应的级别(哪些级别以上的日志会被记录):

|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 | // 记录INFO及以上级别日志 #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #include "spdlog/spdlog.h" SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456); SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12); |

对齐方式 {#对齐方式}

每个flag都可携带对齐方式(最多支持64字符),

| align | meaning | example | result | |-------|---------|---------|--------| | % | 右对齐 | %8l | info | | %- | 左对齐 | %-8l | info | | %= | 居中 | %=8l | info |

截断 {#截断}

通过!可设定对应输出的最大长度:

| align | meaning | example | result | |-------|---------|---------|--------| | % ! | 右对齐且截断 | %3!l | "inf" | | %- ! | 左对齐且截断 | %-2!l | "in" | | %= ! | 居中且截断 | %=1!l | "i" |

字符串格式化fmt {#字符串格式化fmt}

spdlog中字符串格式化使用fmt库。

格式化方式:{ [arg_id] [: (format_spec | chrono_format_spec)] }

  • arg_id:参数标识;
    • 忽略(为空时),依次对应每一个参数;
    • 索引(数字,从0开始),引用第几个索引;
    • 名称,命名参数;
  • format_spec:参数格式化方式(类型、对齐、填充等);

Format Specification {#Format-Specification}

格式化符说明:

|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 | format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type] fill ::= <a character other than '{' or '}'> align ::= "<" | ">" | "^" // 左、右、居中对齐 sign ::= "+" | "-" | " " width ::= integer | {[arg_id]} // 宽度:数字或指定的参数 precision ::= integer | {[arg_id]} // 精度:数字或指定的参数 type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "o" | "p" | "s" | "x" | "X" |

#不同的转换下有不同的意义:

  • 整数时,表示前面添加进制前缀,如0x, 0b等;
  • 浮点数时:总是有小数点(即使没有小数部分);

L只对数字有效,根据本地设置来输出:如,

|-------------|-------------------------------------------------------------------------------------------------------| | 1 2 | auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890); // s == "1,234,567,890" |

格式化类型 {#格式化类型}

格式化类型:

| type | meaning | |------|-------------------------| | s | 字符串 | | c | 字符 | | b/B | 二进制 | | d | 数字(十进制) | | o | 八进制 | | x/X | 十六进制 | | a/A | 十六进制浮点数(p表示指数) | | e/E | 科学计数 | | f/F | 浮点数(包括NAN,INF),固定小数位数输出 | | g/G | 浮点数输出 | | p | 指针 |

示例:

|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 | fmt::format("{:*^30}", "centered"); // use '*' as a fill char // Result: "***********centered***********" fmt::format("{:#04x}", 0); // Result: "0x00" fmt::print( "┌{0:─^{2}}┐\n" "│{1: ^{2}}│\n" "└{0:─^{2}}┘\n", "", "Hello, world!", 20); ┌────────────────────┐ │ Hello, world! │ └────────────────────┘ |

spdlog使用 {#spdlog使用}

spdlog默认日志输出级别是INFO。

默认情况下,日志是同步模式的,可通过以下方法开启异步模式:

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

在异步模式下,日志先存入队列(队列占用的内存 = 设置的队列大小 * slot的大小, 64位系统下slot大小为104字节。),再由工作者线程从队列中取出并输出。当队列满时,会根据设定策略处理:

  • 阻塞新来的日志,直到队列中有剩余空间(默认处理方式);
  • 丢弃新来的日志,需要如下设定策略:

|-----------|-----------------------------------------------------------------------------------------| | 1 | spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::discard_log_msg); |

异常处理 {#异常处理}

当输出日志时发生异常时,spdlog会向std::err 打印一条语句,为了避免输出的异常语句刷屏,打印频率被限制在每分钟一条。可通过set_error_handler来设定异常处理函数:

|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 | //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; }); |

logger {#logger}

默认情况下,spdlog的默认logger为输出到stdout:

|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 | #ifdef _WIN32 auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>(); #else auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>(); #endif |

在使用完logger后,要关闭掉以释放(否则无再建立同名logger)

|-------------|------------------------------------------------------------------------------------| | 1 2 | spdlog::drop_all(); // 关闭所有logger spd::drop("basic_logger"); // 关闭指定logger |

基础用法 {#基础用法}

spdlog中使用{}(里面可指定格式)作为格式化符

以下方式把日志输出到默认logger上:

|---------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #include "spdlog/spdlog.h" int main() { spdlog::info("{:<30}", "left aligned"); spdlog::warn("Easy padding in numbers like {:08d}", 12); spdlog::error("Some error message with arg: {}", 1); spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); spdlog::set_level(spdlog::level::debug); // Set global log level to debug spdlog::debug("This message should be displayed.."); // change log pattern spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); // Compile time log levels // define SPDLOG_ACTIVE_LEVEL to desired level SPDLOG_TRACE("Some trace message with param {}", 42); SPDLOG_DEBUG("Some debug message"); } |

stdout日志 {#stdout日志}

以彩色方式输出到标准输出设备上:

|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 | #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" void stdout_example() { // create color multi threaded logger auto console = spdlog::stdout_color_mt("console"); auto err_logger = spdlog::stderr_color_mt("stderr"); spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); } |

文件日志 {#文件日志}

基本文件 {#基本文件}

最简单的日志文件:

|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 | #include "spdlog/sinks/basic_file_sink.h" void basic_logfile_example() { try { auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); } catch (const spdlog::spdlog_ex &ex) { std::cout << "Log init failed: " << ex.what() << std::endl; } } |

循环文件 {#循环文件}

日志文件超过指定大小后,自动生成一个新的;并且只保留最多指定数量的日志文件:

|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 | #include "spdlog/sinks/rotating_file_sink.h" void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files auto max_size = 1024*1024 * 5; auto max_files = 3; auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files); } |

每日文件 {#每日文件}

每天指定时间生成一个新的日志文件:

|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 | #include "spdlog/sinks/daily_file_sink.h" void daily_example() { // Create a daily logger - a new file is created every day on 2:30am auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); } |

示例 {#示例}

设定默认日志记录文件并在不同地方获取使用:

|---------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #include "spdlog/spdlog.h" #include "spdlog/sinks/rotating_file_sink.h" void writeLog(int n) { for (int i = 0; i < n; ++i) { // 获取logger后输出日志 auto myLogger = spdlog::get("baseLogger"); myLogger->info("{}: Hello, {}!", i + 1, "World"); myLogger->info("Welcome to spdlog!"); myLogger->error("Some error message with arg: {}", 1); // 带文件名与行号的日志输出 SPDLOG_LOGGER_INFO(myLogger, "Support for floats {:03.2f}", 1.23456); SPDLOG_LOGGER_WARN(myLogger, "Easy padding in numbers like {:08d}", 12); // 输出到默认日志中 spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); spdlog::error("Some error message with arg: {}", 1); spdlog::warn("Easy padding in numbers like {:08d}", 12); spdlog::info("Support for floats {:03.2f}", 1.23456); } } void testSPDLog() { // 设定日志最大100k,且最多保留10个 auto myLogger = spdlog::rotating_logger_mt("baseLogger", "logs/basic.log", 1024 * 100, 10); spdlog::set_default_logger(myLogger); myLogger->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v"); // 非通过宏输出的日志%@输出为空 myLogger->set_level(spdlog::level::info); myLogger->info("Hello, {}!", "World"); writeLog(10); } |

参考链接:https://zhuanlan.zhihu.com/p/649444681


赞(2)
未经允许不得转载:工具盒子 » C++日志记录库spdlog介绍