[C++]-日志记录库SPDLog简介[通俗易懂]

[C++]-日志记录库SPDLog简介[通俗易懂]文章目录spdlog库日志记录槽sink日志记录器logger输出格式pattern对齐方式截断字符串格式化fmtFormatSpecificationspdlog使用异常处理logger基础用法stdout日志文件日志基本文件循环文件每日文件示例spdlog是一款开源的、快速的日志库。spdlog库spdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog,API说明:https://spdlog.docsforge.c

大家好,又见面了,我是你们的朋友全栈君。

spdlog是一款开源的、快速的日志库。

spdlog库

spdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog,API说明:https://spdlog.docsforge.com/v1.x/1.quickstart/):

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

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

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

日志记录槽sink

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

  • set_pattern(const std::string&):设置日志输出的内容格式。
  • set_level(level_enum): 设置日志输出的最低等级。
  • log(log_msg):由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

通过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_*宏,且要激活对应的级别(哪些级别以上的日志会被记录):

// 记录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
%<width><flag> 右对齐 %8l ” info”
%-<width><flag> 左对齐 %-8l “info “
%=<width><flag> 居中 %=8l ” info “

截断

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

align meaning example result
%<width>!<flag> 右对齐且截断 %3!l “inf”
%-<width>!<flag> 左对齐且截断 %-2!l “in”
%=<width>!<flag> 居中且截断 %=1!l “i”

字符串格式化fmt

spdlog中字符串格式化使用fmt(https://github.com/fmtlib/fmt)库。

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

  • arg_id:参数标识;

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

Format Specification

格式化符说明:

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只对数字有效,根据本地设置来输出:如,

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 指针

示例:

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默认日志输出级别是INFO。

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

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

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

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

  • 丢弃新来的日志,需要如下设定策略:

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

异常处理

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

    //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

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

#    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)

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

基础用法

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

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

//#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日志

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

#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)");
}

文件日志

基本文件

最简单的日志文件:

#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;
    }
}

循环文件

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

#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);
}

每日文件

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

#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);
}

示例

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

#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://javaforall.net/152339.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月23日 下午1:36
下一篇 2022年6月23日 下午1:46


相关推荐

  • react-router@4.0 使用方法和源码分析

    react-router@4.0 使用方法和源码分析

    2021年7月8日
    72
  • DeepFaceLab2.0 :超详细入门教程!

    DeepFaceLab2.0 :超详细入门教程!在这出门慌得一 B 在家闲的 D 疼的日子 静下心来玩玩换脸黑科技也是一个不错的选择 新年新气象 DFL 也迎来了 2 0 版本 虽然当前作者还在不断的修 bug 但是很多小伙伴肯定已经迫不及待的要去尝试新版本了 今天我就针对 2 0 版本写一个详细的入门教程 争取做到只要会电脑的就能学会 话不多少直接进入正题 演示的软件版本为当天新鲜出炉的版 1 获取软件以及安装软件获取方法

    2026年3月18日
    2
  • 模糊隶属度函数

    模糊隶属度函数模糊系统常规系统会在你输入后得到下一个时刻的输出 但在现实世界中会有很多情况不确定 但可以给出不同状态的集合 且是模糊集合 则称为模糊系统 粗糙集隶属度函数的选择主要由模糊集合决定 若某个下面用一个简单的例子作介绍 模糊 pidmatlab simulink 仿真详细步骤第一部分创建一个模糊逻辑 fis 文件 第一步 打开模糊推理系统编辑器步骤 在 CommondWindo 键入 fuzzy 回车打开如下窗口 既模糊推理系统编辑器第二步 使用模糊推理系统编辑器本例用到两个输入 两

    2026年3月19日
    1
  • linux的rpm卸载,linux rpm卸载参数

    linux的rpm卸载,linux rpm卸载参数linuxrpm 卸载参数本站整理发布时间 2009 03 1015 38 48 作者 jb51 net 我要评论 rpm 卸载参数 test 卸载测试 nodeps 不检查依赖 noscripts 不执行脚本程序 notriggers 不执行触发程序 justdb 仅修改数据库 force 强制 RPM 卸载软件包 并不是简单地将原来安装的文

    2026年3月18日
    2
  • 智能视频识别技术的发展现状「建议收藏」

    智能视频识别技术的发展现状「建议收藏」一、智能视频分析技术应用现状  作为强化视频监控系统应用的一门主要技术——视频智能分析技术,近几年一直得到业界的广泛的关注,其通过对视频内容的分析,将客户所关注的目标从监控背景中分离出来,按照目标的移动方向、速度、时间等参数和某些行为特征进行关联,从而达到主动监控防御的目的。按说这一技术的大规模推广应用对于提高当前治安监控系统的利用效率将起到很大的作用,但实际上却没有得到有效的推广,所谓“叫好不叫座”。笔者认为,造成这一现象的主要原因有以下几个方面:【您可以是大型系统集成商、可以是相关贸易的经销商.

    2022年4月30日
    79
  • Kafka动态调整topic分区partition

    Kafka动态调整topic分区partition我们在使用kafka时,初期创建时所指定topic属性需要修改,如何动态修改kafka属性?kafka提供了命令行工具—kafka-topics.sh.kafka-topics.sh工具介绍kafka-topics.sh工具也是我们用来创建topic、查看topic详情的工具。直接运行kafka-topics.sh可以产出它是用来创建、删除、查看以及更新topicroot@ubuntu……

    2022年10月17日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号