[C++]-日志记录库SPDLog简介

2023-11-05


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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[C++]-日志记录库SPDLog简介 的相关文章

  • 为什么我应该使用内联代码? [复制]

    这个问题在这里已经有答案了 我是一名 C C 开发人员 这里有几个始终困扰我的问题 常规 代码和内联代码之间有很大区别吗 主要区别是什么 内联代码只是宏的一种 形式 吗 选择内联代码时必须进行什么样的权衡 Thanks 表现 正如之前的答案
  • C语言实现延时函数

    我想使用空循环实现延迟函数 但是完成一次循环所需的时间取决于编译器和机器 我希望我的程序自行确定时间并将程序延迟指定的时间 谁能给我任何想法如何做到这一点 注意 有一个名为delay 的函数可以将系统暂停指定的毫秒 是否可以在不使用此功能的
  • C++:字符串流有什么好处?

    谁能告诉我一些在 C 中使用字符串流的实际例子 即使用流插入和流提取运算符输入和输出到字符串流 您可以使用字符串流来转换任何实现operator lt lt 到一个字符串 include
  • .NET 中的 Class.forName() 等效项?

    动态获取对象类型然后创建它的新实例的 C 方法是什么 例如 如何在 C 中实现以下 Java 代码的结果 MyClass x MyClass Class forName classes MyChildClass newInstance Lo
  • 值类型如何实现引用类型

    我遇到了一个值类型正在实现 ref 的场景 类型 只是想知道这怎么可能 幕后发生了什么 结构体是值类型 接口是引用 类型但结构可以实现接口而不会出现任何错误 有什么想法吗 提前致谢 实际上 它同时以两种不同的方式进行 首先 任何值类型都可以
  • 带有嵌入 Flash 视频的 PDF 示例?

    有谁知道我在哪里可以查看嵌入 Flash 视频的 PDF 示例 我知道问这个问题很愚蠢 因为你会认为任何面向技术的用户都应该能够使用谷歌找到一个 但我真的找不到 我的另一个问题是 使用 C 中的 API 将 Flash 视频嵌入 PDF 文
  • 如何生成可变参数包?

    给定不相关的输入是否可以生成非类型参数包 我的意思是 我想改变这一点 template
  • 将 std::pair const 转换为 std::pair const 安全吗?

    理论上或实践上 安全吗reinterpret cast a std pair
  • Web浏览器控件:如何捕获文档事件?

    我正在使用 WPF 的 WebBrowser 控件加载一个简单的网页 在这个页面上我有一个锚点或一个按钮 我想在我的应用程序后面的代码中 即在 C 中 捕获该按钮的单击事件 WebBrowser 控件是否有办法捕获加载页面元素上的单击事件
  • 可以通过模板间接访问基类中的私有类型

    我试图在编译时根据类型是否在给定范围内公开可用来选择要使用的类型 最好直接看代码 include
  • 编译器消息“警告:格式‘%s’需要类型‘char *’,但参数 2 具有类型‘char (*)’”

    我正在尝试运行一个简单的 C 程序 但收到此错误 警告 格式 s 需要类型 char 但参数 2 的类型为 char 20 我在跑步Mac OS X v10 8 https en wikipedia org wiki OS X Mounta
  • 使用信号和槽更新指针

    我对 Qt 很陌生 请帮我解决这个问题 我正在使用线程在后台执行密集操作 同时我想更新 UI 所以我使用 SIGNALS 和 SLOTS 为了更新 UI 我发出一个信号并更新 UI 让我们考虑下面的示例代码 struct sample QS
  • 将旧的 Unity 代码升级到 Unity 5

    在触发按钮上播放动画的代码似乎不起作用 我在 Youtube 上看到了一个视频 内容很简单animation Play 它可以在该视频上运行 但我无法让它在我的计算机上运行 我做错了什么还是团结改变了它 请帮助我在网上找不到解决方案 所有
  • 当一对迭代器初始化时,向量是否知道先保留?

    考虑以下代码 struct MyData MyData const BYTE pData size t uSize bucket pData pData uSize std vector
  • 我的代码哪里有泄漏?

    下面是我的代码 它打开一个 XML 文件 old xml 过滤无效字符并写入另一个 XML 文件 abc xml 最后 我将再次加载 XML abc xml 当执行以下行时 出现异常 表示 xml 文件被另一个进程使用 xDoc Load
  • 链接到ntdll.lib并调用ntdll.dll内部的函数

    我最近正在对私有 API 进行一些研究 我尝试调用诸如NtOpenFile在 ntdll dll 中LoadLibrary and GetProcAddress在运行时 幸运的是 它成功了 今天早上我在电脑上进行了文件搜索 发现ntdll
  • 从 AuthorizeAttribute 继承的属性不起作用

    我目前正在尝试根据用户角色在新的 ASP MVC 5 应用程序中实现安全性 目标是防止用户在没有特定角色 或更高角色 的情况下访问某些控制器或控制器方法 根据到目前为止我所读到的问题 我创建了一个继承 AuthorizeAttribute
  • 如何在 stl 模板中使用导出类 (__declspec(dllexport))?

    我正在使用导出的类 class declspec dllexport myclass private template declspec dllexport class std map
  • Selenium - 模式对话框存在 - 如何接受信息?

    我有以下问题 在页面上提交一些日期后 我有一个如图所示的模式对话框 我想单击 ENTER 来浏览该模式 但它不起作用 我有以下代码 driver FindElement By CssSelector input submit Click A
  • 为什么 INT64_MIN 的定义不同?为什么他们的行为不同?

    The stdint h我公司的标题是 define INT64 MIN 9223372036854775808LL 但在我项目的一些代码中 一位程序员写道 undef INT64 MIN define INT64 MIN 92233720

随机推荐

  • windows的Wsl和Ununtu22.0.4,安装到D盘

    1 开启windows的WSL与虚拟平台 支持 首先在Win11开始菜单搜索 Windows 功能 打开功能配置界面 勾选Linux子系统以及虚拟机平台2个选项 配置后 需要按照提示 重启电脑 2 Windows安装wsl 通过如下命令查看
  • ElasticSearch学习02——Kibana安装

    ElasticSearch学习02 Windows下Kibana安装 Kibana是界面化的查询数据的工具 下载时尽量下载与ElasicSearch一致的版本 1 下载对应版本的Kibana 有了ElasticSearch安装的经验 我们发
  • Linux·主流嵌入式操作系统(RTOS)

    满足实时控制要求的嵌入式操作系统 RTOS 操作系统 以下介绍14种主流的RTOS 分别为 Clinux C OS II eCos FreeRTOS mbed OS RTX Vxworks QNX NuttX 而国产的嵌入式操作系统包括都江
  • Python分治法实现大整数相乘

    思路是将大整数 A B 通过分治法拆分成 A1 A2 B 如果A1数位长度仍然大于某个阈值 以下代码为8位数 则继续拆分 B也一样 辛辛苦苦写完后 发现题友直接使用python自带运算AB也能通过 查证得知Python整数大小没有限制 如果
  • 从原理图更新指定元件的封装到PCB——Altium Designer 18.0笔记

    从原理图更新指定元件的封装到PCB Altium Designer 18 0笔记 步骤1 在原理图编辑器下执行 Update 命令 步骤2 勾选所需的更改 步骤3 执行更改 步骤1 在原理图编辑器下执行 Update 命令 如果我们在原理图
  • 信息系统高级项目管理师英语词汇(二)-常见项目管理词汇

    常见项目管理词汇 Day1 数据 Data 工作绩效数据 Work Performance Data 工作绩效信息 Work Performance Information 工作绩效报告 Work Performance Report 项目
  • 数据处理技巧(8):MATLAB读取txt文本数据并转换成列向量

    MATLAB读取txt文本数据并转换成列向量 1 目标 2 处理步骤 将逗号替换成空格 补齐数据 MATLAB程序 运行查看结果 3 相关链接 1 目标 需要把txt中的数据以列向量的形式保存到excel表格中 Txt中的数据如下 观察数据
  • BigDecimal为何能解决浮点数运算时精度丢失问题(底层)

    1 为什么浮点数运算有精度丢失风险 原因 计算机在存储浮点数时 指数和尾数能占用的bit位是固定的 十进制小数在转二进制小数时乘2取整 直到不存在小数为止 如果在运算时超过尾数限制的bit位长度 就会被截断 所以就导致小数精度发生损失 解决
  • 来自资深程序员的 3 条锦囊:永远不要辞职,除非……

    今晨到公司比较早 于是随手打开了 Medium 浏览了会儿 看到了一篇文章很有意思的一个点 作者说 在成为程序员的早期 总是少不了攀比 比如做着同样的工作 但朋友所得的报酬比自己高 有朋友是大 V 有着非常多的粉丝 也有朋友热衷炒股投资挣得
  • 射频滤波器分析报告(声表面波滤波器/BAW/超声)

    目录 一 射频芯片技术与产品概述 二 5G时代滤波器需求潜力巨大 三 全球滤波器市场现状 3 1 基站 3 2 手机端 四 射频芯片国内发展情况 4 1 国内射频芯片概况 4 2 国内射频滤波器发展情况 4 3 BAW的重重困难 4 4 终
  • C#读取Excel文件

    1 OleDb方式 using System Data OleDb Office 2007及以上版本需要使用 Microsoft ACE OLEDB 12 0 的连接字符串 Office 2003及以下版本需要使用 Microsoft Je
  • 区块链项目源码大全

    区块链项目源码大全 比特币BTC源码 https github com bitcoin 以太坊ETH源码 https github com ethereum 以太经典ETC源码 https github com ethereumprojec
  • C++六种for循环

    C for 循环的5种用法 通过这里的案例 熟悉用法 还有Qt库的foreach for each in std for each for in for C 11 for 下面是例子 有时候会搞混忘记 还有QT库的 foreach 中间不加
  • 全网服务器数据备份解决方案案例实践-老男孩-专题视频课程

    全网服务器数据备份解决方案案例实践 12851人已学习 课程介绍 1 针对公司重要数据备份混乱状况和领导提出备份全网数据的解决方案 2 通过本地打包备份 然后rsync结合crond应用把全网数据统一备份到一个固定存储服务器 然后在存储服务
  • html5 canvas全屏酷炫星光闪烁3D视差背景动画js特效

    下载地址 基于html5 canvas绘制的全屏酷炫星光闪烁3D视差背景动画特效 还有鼠标交互效果哦 dd
  • Android:Retrofit使用详解(超简单)

    前言 Retrofit设计思想 开发者不用关注网络通信的细节 只需要在接口文件中声明一系列方法和返回值 然后通过注解的方式指定该方法对应哪个服务器接口 以及需要提供哪些参数 当我们在程序中调用该方法时 Retrofit会自动向对应的服务器接
  • Qt:构建套件(Kits)

    如下为Qt 5 11 3安装包中含有的Qt组件 组件中的MSVC 2015 32 bit MSVC 2015 64 bit MSVC 2017 64 bit MinGW 5 3 0 32 bit和Tools中的MinGW 5 3 0分别对应
  • S0到S5状态讲解

    ACPI 高级配置和电源管理接口 的六种工作状态Advanced Configuration and Power Management InterfaceACPI表示高级配置和电源管理接口 Advanced Configuration an
  • 蓝桥杯2021年第十二届真题第一场-双向排序

    题目 题目链接 题解 yxc讲解 y总讲已经很好了 但是不得不说 无论是他的基础课还是提高课什么的 都不会去带着你分析代码 这确实让人很头大 这里我就说一下我当时疑惑的点 为什么加if l gt r break 该语句所处的循环相当于在按着
  • [C++]-日志记录库SPDLog简介

    文章目录 spdlog库 日志记录槽sink 日志记录器logger 输出格式pattern 对齐方式 截断 字符串格式化fmt Format Specification spdlog使用 异常处理 logger 基础用法 stdout日志