spdlog日志库--基础介绍

news2024/12/24 9:58:30

文章目录

  • 1. 简介
    • 1.1. spdlog代码特点
    • 1.2. 说明
    • 1.3. spdlog架构
  • 2. spdlog的安装
    • 2.1. 使用包管理器安装
    • 2.2. 使用源码安装
    • 2.3. 仅使用头文件
  • 3. 相关概念
    • 3.0 常用的头文件
    • 3.1. level_enum
    • 3.2. sink
    • 3.3. logger
    • 3.4 格式输出
    • 3.5 对齐方式
    • 3.6 截断
    • 3.7 字符串格式化fmt
  • 4. 特性
    • 4.1. 特性——同步、异步
    • 4.2. 特性——单、多线程处理模式
    • 4.3. 特性——个性化输出格式(pattern)
    • 4.4. 特性——刷新机制
    • 4.5. 特性——异常处理
    • 4.6. 特性——日志类型
  • 5. 日志输出
    • 5.1. 基本文件
    • 5.2. 循环文件
    • 5.3. 每日文件
  • 参考链接

1. 简介

spdlog是开源、高性能、快速、跨平台,支持header-only/compiled的C++日志库,spdlog是基于C++11实现的一款纯头文件的日志管理库。
Git地址
API说明
应用示例
WiKi

1.1. spdlog代码特点

spdlog主要基于C++11开发(若编译环境支持C++20,则将使用std::fmt取代第三方fmt库)。
spdlog中大量使用移动语义、完美转发以减少对象拷贝,又利用内联、模板等技术尽量减少了抽象的代价。
同时广泛使用了智能指针等降低了内存管理的复杂性,通过spdlog可以深入的了解C++11的优雅实现。

1.2. 说明

  • 高效与快速:Spdlog 专注于提供极致的性能,在大量日志记录场景下也能保持较低的延迟和较高的吞吐量。
  • 轻量化设计:Spdlog 是头文件(header-only)库,这意味着用户只需要包含相应的头文件即可开始使用,无需编译链接额外的库文件,即:将spdlog/include目录下的文件拷贝到你的项目中,仅包含头文件即可。
  • 跨平台支持:它支持多种操作系统,包括但不限于 Windows、Linux 和 macOS(Linux / Windows on 32/64 bits),并且在这些平台上都能够良好运行。
  • 丰富的日志级别:Spdlog 支持常见的日志级别,如 TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL 等,用户可以根据需要选择不同级别的日志输出。
  • 格式化与定位信息,可自定义日志格式:通过集成 fmt(fmtlib) 库,Spdlog 允许用户自定义日志消息的格式,可以轻松地包含时间戳、线程ID、文件名、行号以及函数名等上下文信息。
  • 多目标输出:可以将日志输出到控制台、普通文本文件、循环写入文件(rotating log files)、每日生成新文件(daily logs)、系统日志等目标,同时也支持异步写入以提高性能。
  • 线程安全:对于多线程环境,Spdlog 提供了线程安全的日志接口,确保在并发环境下日志记录的正确性和完整性。
    spdlog中各对象都分为多线程与单线程版本:
    • _st:单线程版本,不用加锁,效率更高。
    • *_mt:多线程版本,用于多线程程序是线程安全的。
  • 异步模式:提供可选的异步日志记录机制,能够将日志操作放入后台线程执行,从而避免阻塞主线程。
  • 条件日志:根据预定义的条件开关,可以动态启用或禁用特定级别的日志输出,例如: 可自定义文档大小;可实现自动按日期创建日志文件/定时创建日志文件。

1.3. spdlog架构

spdlog可以分成三级结构,从上而下是logger registry、logger、sink。
在这里插入图片描述

  1. logger/async_logger(日志记录器)
    日志处理的入口,负责格式化日志信息、日志信息的整理合并(如日志级别、文件名、函数名、文件行号等),最终封装至log_msg对象中,再将log_msg对象投递给下游处理。是用户直接操作的对象,通过操作logger进行日志逻辑的生成。

    logger与aync_logger区别在于:
    1)logger是同步处理,会由调用日志记录的线程直接将封装后的log_msg对象投递给下游的sink。
    2)aync_logger则是异步处理,调用日志记录的线程仅负责将封装后的log_msg对象放入线程安全队列,后续由线程池从线程安全队列中不断处理队列中的日志对象。

一个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…。
  1. sink(日志记录槽)
    受logger控制,执行具体的动作(动作包括写入日志文件/输出到控制台),即:负责接收log_msg对象,并通过formatter将对象中记录的信息转换为字符串,最终将字符串输出到目标位置(控制台、日志文件等)。
    spdlog定义了几种sinks用于不同场景(也可自定义)下的日志输出,sink中主要包含:
set_pattern(const std::string&):设置日志输出的内容格式。
set_level(level_enum): 设置日志输出的最低等级。
log(log_msg):由logger自动调用,外部不会主动调用。
  1. formatter
    负责将log_msg对象中的信息转换成字符串。
    通过set_pattern可设定日志格式,如set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v");

  2. registry(logger registry 日志管理器)
    负责管理所有的logger(创建、销毁、获取等),并且通过registry还可对所有的logger做全局设置。例如可通过spdlog::get()访问已创建的logger。

  3. 总结
    简单来说,就是一个logger registry管多个logger,一个logger管多个sink。
    logger registry中的logger是通过name进行对应的。后面使用的时候可以直接通过名称获取对应的日志对象。
    有了这种层级结构,在代码调用的时候,logger的每个操作都会下顺到sink层面,调用sink的对象。比如像一些set_pattern()set_level()

    说到底,日志库的目的就是把日志信息写到指定地方。从上面对于结构的功能描述,sink才是真正操作日志进行写操作的结构。
    那sink可以把日志信息写到哪里呢?主要有三个去向:

     1) 控制台输出(stdout)——默认输出方式
     2) 日志文件
     3) 数据库或其他外部实体
    

    从使用的角度上讲,用文件存储日志比直接存储在数据库中更加常用一点。

2. spdlog的安装

2.1. 使用包管理器安装

  • Debian: sudo apt install libspdlog-dev
  • Homebrew: brew install spdlog
  • MacPorts: sudo port install spdlog
  • FreeBSD: pkg install spdlog
  • Fedora: dnf install spdlog
  • Gentoo: emerge dev-libs/spdlog
  • Arch Linux: pacman -S spdlog
  • openSUSE: sudo zypper in spdlog-devel
  • vcpkg: vcpkg install spdlog
  • conan: spdlog/[>=1.4.1]
  • conda: conda install -c conda-forge spdlog
  • build2: depends: spdlog ^1.8.2

2.2. 使用源码安装

$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j

2.3. 仅使用头文件

将spdlog/include目录下的文件拷贝到你的项目中即可。

3. 相关概念

3.0 常用的头文件

#include "include/spdlog/spdlog.h"
#include "include/spdlog/sinks/stdout_color_sinks.h"
//根据放置库的位置,编写对应的头文件: #include "spdlog/sinks/basic_file_sink.h"
#include "include/spdlog/sinks/basic_file_sink.h"
#include "include/spdlog/sinks/rotating_file_sink.h"
#include "include/spdlog/sinks/daily_file_sink.h"
#include "include/spdlog/sinks/dist_sink.h"
#include <afxcontrolbars.h>

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE		//需要输出文件名和行号,必须输出这个宏
using namespace spdlog;
#include <memory>
#include <string>
#include <conio.h>

#include "spdlog/spdlog.h"
#include "spdlog/fmt/bin_to_hex.h"
#include "spdlog/fmt/bundled/core.h"
#include "spdlog/fmt/bundled/args.h"
#include "spdlog/fmt/bundled/format.h"
// 设置为智能指针类型,用于在 C++ 中管理 spdlog::logger 类型对象的生命周期
extern std::shared_ptr<spdlog::logger> myLogger;
std::shared_ptr<spdlog::logger> myLogger;

3.1. level_enum

日志级别,定义如下:

enum class level_enum {
	trace = SPDLOG_LEVEL_TRACE 0
	debug = SPDLOG_LEVEL_DEBUG 1
	info  = SPDLOG_LEVEL_INFO  2(默认输出等级)
	warn = SPDLOG_LEVEL_WARN 3
	err = SPDLOG_LEVEL_ERROR   4
	critical = SPDLOG_LEVEL_CRITICAL 5
	off  = SPDLOG_LEVEL_OFF 6
}

设置某个等级后,小于该等级的日志将不会被记录。

3.2. sink

日志记录器槽,用于设置日志的输出目的地,如控制台、文件等。
常用函数:

1. virtual void log(const details::log_msg &msg) = 0; //记录日志,有logger自动调用 
2. virtual void flush() = 0;         				  // 刷新日志  
//用于设置日志消息的格式模板。通过指定格式模板,你可以控制日志消息的外观,包括日期、时间、日志级别、线程ID、日志内容等等。
//例如:(`set_pattern("[%Y-%m-%d %H:%M:%S.%e][%l](%@): %v")`)
3. virtual void set_pattern(const std::string &pattern) = 0;  
// 设置自定义的格式化器  
4. virtual void set_formatter(std::unique_ptr<spdlog::formatter>sink_formatter) = 0; 
5. void set_level(level::level_enum log_level);    //设置输出日志的最低级别  
6. level::level_enum level() const;     // 获取日志级别  
7. bool should_log(level::level_enum msg_level) const; //判断是否需要记录日志

set_formatter方法用于设置自定义的日志消息格式化器。通过设置自定义格式化器,你可以完全掌控日志消息的外观,而不仅仅是使用预定义的格式模板。

3.3. logger

日志记录器,用于记录日志。
一个logger对象中存储有多个sink,当调用logger的日志输出函数时,logger会调用自身存储的所有sink对象的log(log_msg) 函数进行输出。与自带的sink对应,spdlog也自带了几种logger。
logger类主要使用的函数包括:

template<typename T>
void trace(const T &msg) // 记录trace级别的日志
{
    log(level::trace, msg);
}

template<typename T>
void debug(const T &msg) // 记录debug级别的日志
{
    log(level::debug, msg);
}

template<typename T>
void info(const T &msg)
{
    log(level::info, msg);
}

template<typename T>
void warn(const T &msg)
{
    log(level::warn, msg);
}

template<typename T>
void error(const T &msg)
{
    log(level::err, msg);
}

template<typename T>
void critical(const T &msg)
{
    log(level::critical, msg);
}


// return true logging is enabled for the given level.
bool should_log(level::level_enum msg_level) const
{
    return msg_level >= level_.load(std::memory_order_relaxed);
}

// return true if backtrace logging is enabled.
bool should_backtrace() const
{
    return tracer_.enabled();
}

void set_level(level::level_enum log_level); // 设置日志级别

level::level_enum level() const;

const std::string &name() const;

// set formatting for the sinks in this logger.
// each sink will get a separate instance of the formatter object.
void set_formatter(std::unique_ptr<formatter> f);

// set formatting for the sinks in this logger.
// equivalent to
//     set_formatter(make_unique<pattern_formatter>(pattern, time_type))
// Note: each sink will get a new instance of a formatter object, replacing the old one.
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);

// backtrace support.
// efficiently store all debug/trace messages in a circular buffer until needed for debugging.
void enable_backtrace(size_t n_messages);
void disable_backtrace();
void dump_backtrace();

// flush functions
void flush();
void flush_on(level::level_enum log_level);
level::level_enum flush_level() const;

// sinks
const std::vector<sink_ptr> &sinks() const;

std::vector<sink_ptr> &sinks();

// error handler
void set_error_handler(err_handler);

// create new logger with same sinks and configuration.
virtual std::shared_ptr<logger> clone(std::string logger_name);

3.4 格式输出

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

flagmeaningexample
%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”
%pAM/PM“AM”
%r时间(12制)“02:55:02 pm”
%R时分(24制)“23:55”
%T %X时间(24制)“23:55:59”
%z时区(偏移)“+02:00”
%Eepoch(秒)“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);

3.5 对齐方式

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

alignmeaningexampleresult
%右对齐%8l" info"
%-左对齐%-8l"info "
%=居中%=8l" info "

3.6 截断

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

alignmeaningexampleresult
%!右对齐且截断%3!l“inf”
%-!左对齐且截断%-2!l“in”
%=!居中且截断%=1!l“i”

3.7 字符串格式化fmt

spdlog中字符串格式化使用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"

格式化类型:

typemeaning
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!    │
└────────────────────┘

4. 特性

4.1. 特性——同步、异步

这里的同步/异步指日志信息是否直接输出/写入文件,直接写就是同步,稍后写就是异步。spdlog默认的状态就是同步了,同步也没什么好说的。
这里介绍下异步的逻辑实现:
  异步状态下,日志会先存入队列,然后由线程从队列中取数据,当队列满的时候会有淘汰策略。如果工作线程中抛出了异常,向队列写入下一条日志时异常会再次抛出,可以在写入队列时捕捉工作者线程的异常,淘汰策略一般两种:

  1. 阻塞新来的的日志,直到队列有剩余空间(默认处理方式)
  2. 把新的日志丢掉(需要设定:spdlog::set_async_mode(队列大小,spdlog::async_overflow_policy::discard_log_msg))

4.2. 特性——单、多线程处理模式

spdlog中提供了单线程和多线程模式,由使用者在对象创建中自己指定。

  1. st:单线程版本,不用加锁,效率高,但不保证线程安全
  2. mt:多线程版本,保证多线程并发情况线程安全,但效率稍低

4.3. 特性——个性化输出格式(pattern)

一般情况下,希望输出的日志信息中带有各类基本信息。
但具体情况要具体分析,spdlog中提供了个性化的输出方式,可以自己指定模式进行输出。
基本上就是各类“%参数”的组合。具体内容可以参考:Custom formatting 。

4.4. 特性——刷新机制

spdlog使用中需要设置刷新规则,以防止诸如程序闪退时日志未及时写入导致无法通过日志内容分析程序崩溃原因。
spdlog刷新设置主要有两种设置方式,一种是全局设置,一种是对于重要的日志信息之后,立即写入。
两种刷新方式:

  1. 程序正常退出时写入(默认)
  2. 程序运行中,在指定位置进行写入(实时刷新日志,便于锁定错误所在位置)

全局设置支持设置刷新周期和刷新级别两种方式。flush_on()flush_every()
要想使用实时刷新日志,spdlog提供了两种方法:

方法一:logger对象->flush_on(设定等级),flush_on是一次性刷新,执行到此时按照设定等级进行日志刷新。
方法二:logger对象->flush_every(周期时间),flush_every是设置刷新周期,定时进行刷新。刷新的级别采取默认了。

示例:

//定时刷新日志缓冲区
spdlog::flush_every(std::chrono::seconds(3));
// trigger flush if the log severity is error or higher    如果日志严重性为错误或更高,则触发刷新
daily_logger->flush_on(spdlog::level::err);
// 刷新缓冲区
my_logger->flush();

4.5. 特性——异常处理

对于日志库来说,当异常发生时,应该要输出异常出现的位置。spdlog会向std::err打印一条语句(终端可显示)。为了防止异常语句刷屏,打印的频率固定在每分钟一条。
实例:

	try
	{
		//设置循环输出的日志
		my_logger= spdlog::rotating_logger_mt("hil", "log.txt", 1024 * 1024 * 5, 2);
		my_logger->set_level(spdlog::level::debug);

		//启动在调用hillo->trace的时候把日志放到一个ringbuffer(环形内存)里面,最多64条日志
		my_logger->enable_backtrace(64);
		//定时刷新
		spdlog::flush_every(std::chrono::seconds(3));
	}
	catch (const spdlog::spdlog_ex& ex)
	{
		return 0;
	}
	
#define FLUSHLOG() do{if(my_logger){my_logger->flush();}}while(0)
#define DUMP_LOGTRACE() do{if(my_logger){my_logger->dump_backtrace();}}while(0)

4.6. 特性——日志类型

根据输出类型基本可分为输出到终端和文件两种类型,其中输出文件类型可进一步分为按文件大小和日期滚动刷新。

  • 按日期滚动刷新是指每隔24小时,到达指定的时间后会创建新的日志文件;
  • 按文件大小滚动刷新是指日志文件尺寸达到设定值后,会将当前文件添加后缀进行重命名,然后创建新的空日志文件进行写入。

此外,spdlog还支持自定义日志写入规则,可通过自定义sink类实现。

日志格式设置
spdlog支持自定义日志格式,但是自定义的logger往往不支持自定义格式,需要使用spdlog提供的宏定义调用,可以实现自定义输出格式。

相关术语

术语释义
logger用于记录日志的对象
rotate日志文件的切换
registry注册处,从统一一个位置来获得logger
sink实际执行落日志到文件或DB动作的类
mtmultiple-thread 的缩写,带_mt后缀的是多线程安全的
stsingle-thread的缩写,带_st的函数非线程安全的
exexception的缩写,spdlog::spdlog_ex
slot插槽,在spdlog线程池构造时,预分配queue slot
tweaking(稍稍改进),自己可以指定一些参数。
flush刷日志
bundled捆绑的,spdlog/include/spdlog/fmt/bundled/ 使用的外部库的代码

5. 日志输出

spdlog,作为一个高性能的C++日志库,提供了多种灵活的日志输出方式,以满足不同场景下的需求。以下是spdlog几种正常的输出方式的详细说明:

  1. 控制台输出(stdout)
    说明:这是spdlog的默认输出方式之一,即将日志信息直接打印到控制台(通常是终端或命令行界面)。
    特点:便于开发者即时查看日志信息,特别是在开发过程中进行调试时。
    示例:通过包含spdlog/spdlog.h头文件并使用spdlog::info等函数直接输出日志到控制台。
  2. 文件输出
    基本文件日志(spdlog::basic_logger):
    说明:创建一个只向单个文件写入日志的日志器。
    特点:所有日志都会在该文件中累加,适用于不需要频繁轮转日志文件的场景。
    示例:使用spdlog::basic_logger_mt(多线程安全版本)创建一个日志器,并指定文件名。
    循环日志(spdlog::rotating_logger):
    说明:当日志文件达到一定大小时,会自动创建一个新的日志文件继续写入,而旧的日志文件会被保留(但数量有限制)。
    特点:适用于日志文件可能会快速增长的场景,可以有效管理磁盘空间。
    示例:使用spdlog::rotating_logger_mt函数,指定文件名、最大文件大小和最大文件数量。
    按天轮转日志(spdlog::daily_logger):
    说明:每天在指定的时间点创建一个新的日志文件,并开始向新文件写入日志。
    特点:便于按天管理日志,便于后续分析和归档。
    示例:使用spdlog::daily_logger_mt函数,指定文件名和每天的轮转时间(小时和分钟)。
  3. 系统日志输出
    说明:虽然spdlog本身可能不直接提供将日志输出到系统日志(如syslog)的功能,但可以通过集成或封装其他库(如syslog-ng、rsyslog等)来实现。
    特点:系统日志通常由操作系统管理,适合需要与系统其他部分集成或符合特定日志管理规范的场景。
  4. 自定义输出
    说明:spdlog支持通过自定义sink(日志接收器)来实现自定义的日志输出方式。
    特点:高度灵活,可以根据实际需求将日志输出到数据库、网络、自定义消息队列等。
    示例:通过继承spdlog::sink类并实现相关接口,可以创建自定义的sink来接收日志信息。
  5. 异步输出
    说明:spdlog提供了异步日志记录机制,可以将日志操作放入后台线程执行,避免阻塞主线程。
    特点:提高程序性能,特别是在高并发或日志量大的场景下。
    示例:使用spdlog::async_logger或相关函数创建异步日志器。
    综上所述,spdlog提供了包括控制台输出、文件输出(基本文件日志、循环日志、按天轮转日志)、系统日志输出(通过集成)、自定义输出以及异步输出在内的多种日志输出方式。这些方式使得spdlog能够灵活适应不同场景下的日志记录需求。

5.1. 基本文件

最简单的日志文件:

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

5.2. 循环文件

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

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

5.3. 每日文件

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

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

参考链接

[C++]-日志记录库SPDLog简介
spdlog使用学习笔记
C++日志记录库SPDLog
C++日志记录库SPDLog_V
字符串资源_1
字符串资源_2

spdlog
spdlog 快速的 C++ 日志库
C++高性能日志库spdlog使用指南
C++日志记录库SPDLog简介
轻量级C++11日志库spdlog

spdlog使用_蜗牛单行道的博客-CSDN博客_spdlog
spdlog学习笔记_haojie_superstar的博客-CSDN博客_spdlog
[C++]-日志记录库SPDLog简介[通俗易懂] - 全栈程序员必看 (javaforall.cn)
spdlog简介_JontyZh的博客-CSDN博客_spdlog 输出格式
spdlog 基本结构分析 - 小胖西瓜 - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1983731.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

职业教育云计算实验实训室建设应用案例

云计算作为信息技术领域的一次革命&#xff0c;正在深刻改变着我们的工作和生活方式。随着企业对云计算技术的依赖日益加深&#xff0c;对具备云计算技能的专业人才的需求也日益迫切。职业院校面临着培养符合行业标准的云计算人才的挑战。唯众凭借其在教育技术领域的专业经验&a…

Web3时代的智能合约:区块链技术的革命性应用

随着区块链技术的发展&#xff0c;Web3时代已经悄然来临。智能合约作为这一时代的重要组成部分&#xff0c;正引领着技术应用的革命性变革。本文将深入解析智能合约的概念、工作原理、应用场景及其带来的挑战与机遇&#xff0c;全面展现其在Web3时代的重要作用。 一、智能合约…

Spring Cloud 整合 Nacos、Sentinel、OpenFigen 实战【微服务熔断降级实战】

前言&#xff1a; 上一篇我们分析了 Sentinel 的各种核心概念点以及 Sentinel 的执行流程&#xff0c;并分别演示了使用 Sentinel 编码和注解方式来管理资源的场景&#xff0c;加上我们前面学习的 Nacos&#xff0c;本篇来分享 Spring Cloud 整合 Nacos、Sentinel、OpenFigen …

密码学基础:搞懂Hash函数SHA1、SHA-2、SHA3(1)

目录 1.消息摘要(Hash) 2.SHA-1 3.SHA-2 4.小结 1.消息摘要(Hash) Hash函数是一种单向密码体制&#xff0c;把任意长度的输入经过变换得到一个固定长度的输出&#xff0c;同时它还具备单向性&#xff0c;只能从明文到密文&#xff0c;不能逆向&#xff0c;正是由于Hash函数…

Golang | Leetcode Golang题解之第326题3的幂

题目&#xff1a; 题解&#xff1a; func isPowerOfThree(n int) bool {return n > 0 && 1162261467%n 0 }

onlyoffice使用Https访问

开发服务器用的是http&#xff0c;一切正常使用&#xff0c;部署到服务器后&#xff0c;由于服务器使用了Https&#xff0c;导致访问onlyoffice时控制台报错。Mixed Content: The page at http://xxxxx// was loaded over HTTPS, but requested an insecure frame http://xxxxx…

Jpa-多表关联-OneToOne

Jpa-多表关联-OneToOne 准备JoinColumnOneToOne属性targetEntitycascade*PERSISTMERGEREMOVEREFRESH orphanRemovalfetchoptionalMappedBy* OneToOne在 hibernate中用于对表与表之间进行维护关联 准备 import com.alibaba.fastjson.JSON; import jakarta.persistence.*; impor…

K210烧录固件失败原因

1.检查固件信息&#xff0c;我这里用的是亚博智能提供的canmv固件 2.检查串口有无被占用&#xff0c;我就是因为打开了另一个正点原子的串口接收软件卡这么久 3.你要烧录canmv固件而非maixypy固件 若烧录maixypy要用maixipy来开发 4。可以看看换不同下载方式

辛迪·克劳馥与“惊艳”家人合影,装扮完美协调 幸福满满

我的意思是——辛迪克劳馥拥有一个令人叹为观止的家庭&#xff0c;这真的有什么好奇怪的吗&#xff1f;毕竟&#xff0c;她是世界知名的超级名模&#xff0c;她把基因和颧骨传给了她的孩子&#xff0c;让他们很容易追随她的脚步。所以&#xff0c;当我们看到她最近的家庭照片时…

面试笔记 8.5

面试常见: Jvm&#xff0c;高并发&#xff0c;多线程&#xff0c;数据库&#xff0c;redis&#xff0c;框架 1.N I/O有什么核心组件 Java NIO 基本原理以及三大核心组件_java nio核心组件有哪些-CSDN博客 Buffer 缓冲 Channel 一对一 Channel 读取数据 Selector对应线程…

【ML】multi head self-attention(自注意力机制)

【ML】multi head 自注意力机制self-attention 0. Transformer1. multi head self-attention2. positional encoding3. transform 可以应用的其他领域3.1 语音识别 变体 truncated self-attention3.2 self-attention for image3.3 self-attention v.s. CNN差异3.4 self-attenti…

CSP 2022 提高级第一轮 - CSP/S 2022初试题 程序阅读第三题解析

一、代码查看 1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int MAXL 1000; 7 8 int n, k, ans[MAXL]; 9 10 int main(void) 11 { 12 cin >> n >> k; 13 if (!n) cout << 0 <&l…

IO流学习总结

IO流体系 字节流 字节流&#xff1a; 字节输出流:FileOutputStream 程序---写--->文件 字节输入流:FileInputStream 程序<---读---文件 字节输出流(FileOutputStream) Testpublic void testIO01() throws IOException {/*new FileOutputStream文件不存在创建文件父…

最新CSS3纵向菜单的实现

纵向菜单 通过下面例子&#xff0c;你会知道把列表转换成菜单的关键技术 a中的#是URL的占位符可以点击&#xff0c;真正用途中写实际URL <nav class"list1"><ul><li><a href"#">Alternative</a></li><li><…

AI智能化赋能电商经济,守护消费净土,基于轻量级YOLOv8n开发构建公共生活景下的超大规模500余种商品商标logo智能化检测识别分析系统

在数字经济浪潮的推动下&#xff0c;全力发展新质生产力已成为当今社会发展的主旋律。各行各业正经历着前所未有的变革&#xff0c;其中&#xff0c;电商行业作为互联网经济的重要组成部分&#xff0c;更是以惊人的速度重塑着商业格局与消费模式。AI智能化技术的深度融合&#…

C与Python Socket性能比较

在比较 C 和 Python 的 Socket 性能时&#xff0c;主要考虑以下几个方面&#xff1a; 运行时性能&#xff1a; C 是编译型语言&#xff0c;生成的机器代码运行速度更快&#xff0c;通常能够提供更低的延迟和更高的吞吐量。Python 是解释型语言&#xff0c;运行时有一定的开销&…

分布式时序数据库TimeLyre 9.2发布:原生多模态、高性能计算、极速时序回放分析

在当今数据驱动的世界中&#xff0c;多模态数据已经成为企业的重要资产。随着数据规模和多样性的不断增加&#xff0c;企业不仅需要高效存储和处理这些数据&#xff0c;更需要从中提取有价值的洞察。工业领域在处理海量设备时序数据的同时&#xff0c;还需要联动分析警报信息、…

K8S资源之NameSpace

作用 隔离资源(默认不隔离网络) 查看所有的NS kubectl get ns创建NS kubectl create ns hello删除NS kubectl delete ns hello

GitHub无法识别Markdown的目录

可以直接下载编译好的二进制文件。 二进制文件 下载下来之后&#xff0c;发现没有后缀名无法识别&#xff0c;实际上这是个exe文件&#xff0c;所以只需要暴力地在后面加上.exe就可以开始愉快使用了。 首先将README.md文档复制到gh-md-toc.exe的根目录下。 接着按住shift键…

Java面试题——第三篇(JVM)

1. 什么情况下会发生栈内存溢出 栈是线程私有的&#xff0c;他的生命周期和线程相同&#xff0c;每个方法在执行的时候都会创建一个栈帧&#xff0c;用来存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程&#xff0c;就对应着一个栈帧…