C++高性能日志库spdlog使用指南

大草原的小灰灰 2024-08-20 10:05:02 阅读 51

spdlog库简介

spdlog 是一个高性能的 C++ 日志库,它设计时充分考虑了速度和易用性,具有以下特点

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

使用步骤

spdlog库的使用也非常简单,只需要下载源代码,然后把根目录下的include目录下的文件拷贝到我们的工程下,在工程中包含相应的头文件即可。

实例演示

控制台打印

首先看下最简单的使用,如何在控制台打印以及如何进行参数化打印。main.cpp

<code> #include <spdlog/spdlog.h>

#include <string.h>

#include <iostream>

int main()

{

// 普通打印

spdlog::info("Welcome to info spdlog!");

// 格式化打印

// 打印字符串

spdlog::info("Hello World {}", "spdlog!");

// 打印数字

spdlog::error("spdlog errCode : {}", -10020);

// 指定打印数字的占位符

spdlog::warn("spdlog format char {:08d}", 12);

// 格式化打印不同进制的数据

spdlog::critical("Support for int:{0:d} hex:{0:x} oct:{0:o} bin:{0:b}", 42);

// 打印浮点型数据

spdlog::info("float args are {:03.2f}", 1.23456);

// 打印多个参数

spdlog::info("string args are {0} {1}..", "too", "supported");

spdlog::info("number args are {0} {1} {2}..", 10020, 10040, -100);

system("pause");

}

执行结果

在这里插入图片描述

这里参数化打印的实现是通过 {} 实现的,跟常见函数的参数化打印有点不一样。

在文件中打印日志

接下来就看下如何在文件中打印日志main.cpp

<code> #include <spdlog/spdlog.h>

#include <spdlog/sinks/basic_file_sink.h>

#include <string.h>

#include <iostream>

int main()

{

try

{

// 参数1 日志标识符, 参数2 日志文件名

std::shared_ptr<spdlog::logger> mylogger = spdlog::basic_logger_mt("spdlog", "spdlog.log");

// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]

mylogger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");

mylogger->set_level(spdlog::level::debug);

spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区

mylogger->trace("Welcome to info spdlog!");

mylogger->debug("Welcome to info spdlog!");

mylogger->info("Welcome to info spdlog!");

mylogger->warn("Welcome to info spdlog!");

mylogger->error("Welcome to info spdlog!");

mylogger->critical("Welcome to info spdlog!");

// 刷新

mylogger->flush_on(spdlog::level::debug);

}

catch (const spdlog::spdlog_ex& ex)

{

std::cout << "Log initialization failed: " << ex.what() << std::endl;

}

system("pause");

}

执行结果。执行程序后就会在当前目录下生成一个spdlog.log文件,可以看下打印内容

在这里插入图片描述

在这里插入图片描述

多个文件中打印

一般都需要在多个文件中打印,下面都通过两个文件演示下main.cpp

<code> #include <spdlog/spdlog.h>

#include <spdlog/sinks/basic_file_sink.h>

#include <string.h>

#include <iostream>

#include "myfunc.h"

int main()

{

try

{

// 基本文件

std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "spdlog.txt");

// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]

my_logger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");

my_logger->set_level(spdlog::level::debug);

spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区

my_logger->trace("Welcome to info spdlog!");

my_logger->debug("Welcome to info spdlog!");

my_logger->info("Welcome to info spdlog!");

my_logger->error("Welcome to info spdlog!");

myFuncTest();

my_logger->flush();

system("pause");

}

catch (const spdlog::spdlog_ex& ex)

{

std::cout << "Log initialization failed: " << ex.what() << std::endl;

}

}

myfunc.cpp

#include "myfunc.h"

#include <spdlog/spdlog.h>

void myFuncTest(){

std::shared_ptr<spdlog::logger> mylogger = spdlog::get("spdlog");

mylogger->trace("Welcome to myFuncTest!");

mylogger->debug("Welcome to myFuncTest!");

mylogger->info("Welcome to myFuncTest!");

mylogger->error("Welcome to myFuncTest!");

}

在main函数中定义了 mylogger 对象后,在其他文件中通过 spdlog::get 获取到这个对象,就可以打印了。打印结果

在这里插入图片描述

打印文件名和行号

main.cpp

<code> #define SPDLOG_ACTIVE_LEVEL SPDLOG_LOGGER_TRACE

#include <spdlog/spdlog.h>

#include <spdlog/sinks/basic_file_sink.h>

#include <string.h>

#include <iostream>

int main()

{

try

{

std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "basic.txt");

// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [文件名 函数名:行号] [数据]

my_logger->set_pattern("[%n] [%Y-%m-%d %H:%M:%S.%e] [%l] [%t] [%s %!:%#] %v");

my_logger->set_level(spdlog::level::debug);

spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区

SPDLOG_LOGGER_TRACE(my_logger, "Welcome to trace spdlog");

SPDLOG_LOGGER_DEBUG(my_logger, "Welcome to debug spdlog");

SPDLOG_LOGGER_INFO(my_logger, "Welcome to info spdlog");

SPDLOG_LOGGER_WARN(my_logger, "Welcome to warn spdlog");

SPDLOG_LOGGER_ERROR(my_logger, "Welcome to error spdlog");

my_logger->flush();

}

catch (const spdlog::spdlog_ex& ex)

{

std::cout << "Log initialization failed: " << ex.what() << std::endl;

}

}

特别说明

如果需要打印文件名、函数名和行号,必须在文件开头定义以下宏。如果要在多个文件中打印,也必须在文件开头定义这个宏。否则只会打印默认级别,set_level 设置的日志级别不会生效。

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LOGGER_TRACE

并且要使用 SPDLOG_LOGGER* 系列函数打印。 打印结果

在这里插入图片描述

在线程中打印

main.cpp

<code> #include <spdlog/spdlog.h>

#include <spdlog/sinks/basic_file_sink.h>

#include <string.h>

#include <iostream>

#include <windows.h>

#include <process.h>

unsigned int __stdcall ThreadFun(PVOID lpParam) {

std::shared_ptr<spdlog::logger> mylogger = spdlog::get("spdlog");

mylogger->trace("Welcome to ThreadFun!");

mylogger->debug("Welcome to ThreadFun!");

mylogger->info("Welcome to ThreadFun!");

mylogger->error("Welcome to ThreadFun!");

return 0;

}

int main()

{

try

{

// 基本文件

std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "spdlog.txt");

// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]

my_logger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");

my_logger->set_level(spdlog::level::debug);

spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区

my_logger->trace("Welcome to trace spdlog!");

my_logger->debug("Welcome to debug spdlog!");

my_logger->info("Welcome to info spdlog!");

my_logger->error("Welcome to error spdlog!");

HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);

//永久等待线程运行结束

WaitForSingleObject(handle1, INFINITE);

WaitForSingleObject(handle2, INFINITE);

WaitForSingleObject(handle3, INFINITE);

// 刷新缓冲区

my_logger->flush();

system("pause");

}

catch (const spdlog::spdlog_ex& ex)

{

std::cout << "Log initialization failed: " << ex.what() << std::endl;

}

}

打印结果

在这里插入图片描述

日志大小回滚

可通过以下这个初始化函数实现日志以大小进行回滚。

<code> // 包含此头文件

#include <spdlog/sinks/rotating_file_sink.h>

// 参数3:日志大小(字节)

// 参数4:回滚数

std::shared_ptr<spdlog::logger> file_logger = spdlog::rotating_logger_mt("spdlog", "spdlog.log", 1024, 5);

打印结果

在这里插入图片描述

按日期回滚

可通过以下两个初始化函数分别实现按天和按小时进行日志回滚。

<code> // 包含头文件

#include <spdlog/sinks/daily_file_sink.h>

#include <spdlog/sinks/hourly_file_sink.h>

// 按天回滚

std::shared_ptr<spdlog::logger> dailylogger = spdlog::daily_logger_mt("daily", "daily.log");

// 按小时回滚

std::shared_ptr<spdlog::logger> my_logger = spdlog::hourly_logger_mt("hourly", "hourly.log");

打印结果

在这里插入图片描述

参考

C++日志记录库SPDLog简介



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。