[C++][第三方库][spdlog]详细讲解


1.介绍

  • spdlog是一个高性能、超快速、零配置的C++日志库,它旨在提供简洁的API和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录
  • Github
  • 特点
    • 高性能spdlog专为速度而设计,即使在高负载情况下也能保持良好的性能
    • 零配置:无需复杂的配置,只需包含头文件即可在项目中使用
    • 异步日志:支持异步日志记录,减少对主线程的影响
    • 格式化:支持自定义日志消息的格式化,包括时间戳、线程ID、日志级别等
    • 多平台:跨平台兼容,支持Windows、Linux、MacOS等操作系统
    • 丰富的API:提供丰富的日志级别和操作符重载,方便记录各种类型的日志

2.安装

  • 直接命令安装sudo apt install libspdlog-dev
  • 源码安装clone后,使用cmake安装即可,详细可参照官方文档

3.使用

1.包含头文件

#include <spdlog/spdlog.h>

2.日志输出等级枚举

namespace level 
{ 
    enum level_enum : int 
    { 
        trace = SPDLOG_LEVEL_TRACE, 
        debug = SPDLOG_LEVEL_DEBUG, 
        info = SPDLOG_LEVEL_INFO, 
        warn = SPDLOG_LEVEL_WARN, 
        err = SPDLOG_LEVEL_ERROR, 
        critical = SPDLOG_LEVEL_CRITICAL, 
        off = SPDLOG_LEVEL_OFF, 
        n_levels 
    }; 
} 

3.日志输出格式自定义

  • 可以自定义日志消息的格式
    logger->set_pattern("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v"); 
     
    %t - 线程ID(Thread ID)
    %n - 日志器名称(Logger name)
    %l - 日志级别名称(Level name),如 INFO, DEBUG, ERROR 等
    %v - 日志内容(message)
    %Y - 年(Year)
    %m - 月(Month)
    %d - 日(Day)
    %H - 小时(24-hour format)
    %M - 分钟(Minute)
    %S - 秒(Second)
    

4.日志记录器类

  • 创建一个基本的日志记录器,并设置日志级别和输出模式
    namespace spdlog 
    { 
    	class logger 
    	{ 
    	    logger(std::string name)logger(std::string name, sink_ptr single_sink) 
    	    logger(std::string name, sinks_init_list sinks) 
    	    
    	    void set_level(level::level_enum log_level); 
    	    void set_formatter(std::unique_ptr<formatter> f); 
    	    
    	    template<typename... Args> 
    	    void trace(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void debug(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void info(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void warn(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void error(fmt::format_string<Args...> fmt, Args &&...args) 
    	    
    	    template<typename... Args> 
    	    void critical(fmt::format_string<Args...> fmt, Args &&...args) 
    	     
    	    void flush(); //刷新日志 
    	    //策略刷新--触发指定等级日志的时候立即刷新日志的输出 
    	    void flush_on(level::level_enum log_level); 
    	}}
    

5.异步日志记录类

  • 为了异步记录日志,可以使用spdlog::async::logger
    class async_logger final : public logger 
    { 
        async_logger(std::string logger_name, 
                     sinks_init_list sinks_list, 
                     std::weak_ptr<details::thread_pool> tp, 
                     async_overflow_policy overflow_policy = 
                     async_overflow_policy::block);
                     
        async_logger(std::string logger_name, 
                     sink_ptr single_sink, 
                     std::weak_ptr<details::thread_pool> tp, 
                     async_overflow_policy overflow_policy = 
                     async_overflow_policy::block); 
                      
        //异步日志输出需要异步工作线程的支持,这里是线程池类 
        class SPDLOG_API thread_pool 
        { 
            thread_pool(size_t q_max_items, 
    		            size_t threads_n, 
    		            std::function<void()> on_thread_start, 
    		            std::function<void()> on_thread_stop); 
    		            
            thread_pool(size_t q_max_items, size_t threads_n,  
    		            std::function<void()> on_thread_start); 
    		            
            thread_pool(size_t q_max_items, size_t threads_n); 
        }; 
    } 
     
    std::shared_ptr<spdlog::details::thread_pool> thread_pool() 
    { 
        return details::registry::instance().get_tp(); 
    } 
     
    //默认线程池的初始化接口 
    inline void init_thread_pool(size_t q_size, size_t thread_count) 
     
    auto async_logger = spdlog::async_logger_mt("async_logger", 
    											"logs/async_log.txt"); 
    											
    async_logger->info("This is an asynchronous info message");
    

6.日志记录器工厂类

using async_factory = async_factory_impl<async_overflow_policy::block>; 
 
template<typename Sink, typename... SinkArgs> 
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, 
													SinkArgs &&...sink_args) 
 
 
// 创建一个彩色输出到标准输出的日志记录器,默认工厂创建同步日志记录器 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, 
									    color_mode mode = color_mode::automatic); 
//标准错误 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, 
										color_mode mode = color_mode::automatic); 
//指定文件  
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, 
										const filename_t &filename, 
										bool truncate = false, 
								const file_event_handlers &event_handlers = {}) 
//循环文件 
template<typename Factory = spdlog::synchronous_factory> 
std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name,  
										   const filename_t &filename,  
										   size_t max_file_size,  
										   size_t max_files,  
										   bool rotate_on_open = false) 
...

7.日志落地类

namespace spdlog 
{ 
	namespace sinks 
	{ 
	    class SPDLOG_API sink 
	    { 
	    public: 
	        virtual ~sink() = default; 
	        virtual void log(const details::log_msg &msg) = 0; 
	        virtual void flush() = 0; 
	        virtual void set_pattern(const std::string &pattern) = 0; 
	        virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
	     
	        void set_level(level::level_enum log_level); 
	    }; 
	     
	    using stdout_sink_mt; 
	    using stderr_sink_mt; 
	    using stdout_color_sink_mt; 
	    using stderr_color_sink_mt; 
	    //滚动日志文件-超过一定大小则自动重新创建新的日志文件 
	    sink_ptr rotating_file_sink(filename_t base_filename, 
			                        std::size_t max_size, 
			                        std::size_t max_files, 
			                        bool rotate_on_open = false, 
							   const file_event_handlers &event_handlers = {}); 
	    using rotating_file_sink_mt = rotating_file_sink<std::mutex>; 
	    //普通的文件落地对啊ing 
	    sink_ptr basic_file_sink(const filename_t &filename, 
			                     bool truncate = false, 
			                     const file_event_handlers &event_handlers = {}); 
	    using basic_file_sink_mt = basic_file_sink<std::mutex>; 
	     
	    using kafka_sink_mt = kafka_sink<std::mutex>; 
	    using mongo_sink_mt = mongo_sink<std::mutex>; 
	    using tcp_sink_mt = tcp_sink<std::mutex>; 
	    using udp_sink_mt = udp_sink<std::mutex>; 
	    ..... 
	    //*_st:单线程版本,不用加锁,效率更高。 
	    //*_mt:多线程版本,用于多线程程序是线程安全的。 
	} 
} 

8.全局接口

//输出等级设置接口 
void set_level(level::level_enum log_level); 
//日志刷新策略-每隔N秒刷新一次 
void flush_every(std::chrono::seconds interval) 
//日志刷新策略-触发指定等级立即刷新 
void flush_on(level::level_enum log_level); 

9.记录日志

  • 使用日志记录器记录不同级别的日志
    logger->trace("This is a trace message"); 
    logger->debug("This is a debug message"); 
    logger->info("This is an info message"); 
    logger->warn("This is a warning message"); 
    logger->error("This is an error message"); 
    logger->critical("This is a critical message"); 
    

4.使用示例

0.makefile

all:sync_stdout sync_file async

sync_stdout:sync_stdout.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

sync_file:sync_file.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

async:async.cc
	g++ -o $@ $^ -std=c++17 -lspdlog -lfmt

.PHONY:clean
clean:
	rm sync_stdout
	rm sync_file

1.同步日志并输出到标准输出

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/stdout_color_sinks.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建同步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	auto logger = spdlog::stdout_color_mt("default-logger");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    [15:26:28][27831][debug   ] Hello SnowK
    [15:26:28][27831][info    ] Hello SnowK
    [15:26:28][27831][warning ] Hello SnowK
    [15:26:28][27831][error   ] Hello SnowK
    [15:26:28][27831][critical] Hello SnowK
    spdlog 日志输出完毕
    

2.同步日志并输出到文件

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/basic_file_sink.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建同步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	auto logger = spdlog::basic_logger_mt("file_logger", "log");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    # stdout
    spdlog 日志输出完毕
    
    # log文件
    [15:34:45][28382][debug   ] Hello SnowK
    [15:34:45][28382][info    ] Hello SnowK
    [15:34:45][28382][warning ] Hello SnowK
    [15:34:45][28382][error   ] Hello SnowK
    [15:34:45][28382][critical] Hello SnowK
    

3.异步日志并输出到标准输出

  • main.cc
    #include <iostream>
    #include <spdlog/spdlog.h>
    #include <spdlog/sinks/stdout_color_sinks.h>
    #include <spdlog/async.h>
    
    int main()
    {   
    	// 1.设置全局的刷新策略
    	// (1) 每秒刷新
    	spdlog::flush_every(std::chrono::seconds(1));
    	// (2) 遇到debug以上等级的日志立即刷新
    	spdlog::flush_on(spdlog::level::level_enum::debug);
    	// (3) 设置全局的日志输出等级 -> 无所谓, 每个日治其可以独立进行设置
    	spdlog::set_level(spdlog::level::level_enum::debug);
    
    	// 2.创建异步日志器(标准输出/文件) --> 工厂接口默认创建的是同步日志器
    	spdlog::init_thread_pool(2048, 1);
    	auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async-logger");
    
    	// 设置日期的刷新策略, 以及设置日志器的输出等级
    	// logger->flush_on(spdlog::level::level_enum::debug);
    	// logger->set_level(spdlog::level::level_enum::debug);
    
    	// 3.设置日志输出格式
    	logger->set_pattern("[%H:%M:%S][%t][%-8l] %v");
    
    	// 4.进行简单的日志输出
    	logger->trace("Hello {}", "SnowK");
    	logger->debug("Hello {}", "SnowK");
    	logger->info("Hello {}", "SnowK");
    	logger->warn("Hello {}", "SnowK");
    	logger->error("Hello {}", "SnowK");
    	logger->critical("Hello {}", "SnowK");
    
    	std::cout << "spdlog 日志输出完毕" << std::endl;
    
    	return 0;
    }
    
  • 输出结果
    spdlog 日志输出完毕
    [15:41:47][28793][debug   ] Hello SnowK
    [15:41:47][28793][info    ] Hello SnowK
    [15:41:47][28793][warning ] Hello SnowK
    [15:41:47][28793][error   ] Hello SnowK
    [15:41:47][28793][critical] Hello SnowK
    

5.spdlog与glog组件对比

0.前言

  • glogspdlog都是流行的C++日志库,它们各自具有不同的特点和优势
  • 以下是对这两个库的对比分析,包括性能测试的结果和使用场景的考量

1.glog

  • glog是由Google开发的一个开源C++日志库,它提供了丰富的日志功能,包括多种日志级别、条件日志记录、日志文件管理、信号处理、自定义日志格式等
  • glog默认情况下是同步记录日志的,这意味着每次写日志操作都会阻塞直到日志数据被写入磁盘
  • 性能:根据张生荣的性能对比测试分析,glog在同步调用的场景下的性能较spdlog
    • 在一 台低配的服务器上,glog耗时1.027秒处理十万笔日志数据,而在固态硬盘上的耗时 为0.475

2.spdlog

  • spdlog是一个开源的、高性能的C++日志库,它支持异步日志记录,允许在不影响主线程的情况下进行日志写入
  • 性能:在同样的性能测试中,spdlog在同步调用的场景下比glog
    • 在低配服务器上的耗时为0.135秒,而在固态硬盘上的耗时为0.057秒
    • 此外,spdlog还提供了异步日志记录的功能,其简单异步模式的耗时为0.158秒

3.对比总结

  • 性能:从性能测试结果来看,spdlog在同步调用场景下的性能优于glog,当涉及到大量日志数据时,spdlog显示出更快的处理速度。
  • 异步日志spdlog支持异步日志记录,这在处理高负载应用程序时非常有用,可以减少日志操作对主线程的影响。
  • 易用性spdlog提供了更简单的集成和配置方式,只需包含头文件即可使用,而glog可能需要额外的编译和配置步骤。
  • 功能glog提供了一些特定的功能,如条件日志记录和信号处理,这些在某些场景下可能非常有用
  • 使用场景
    • glog可能更适合那些对日志性能要求不是特别高,但需要一些特定功能的场景
    • spdlog则适合需要高性能日志记录和异步日志能力的应用程序
    • 在选择日志库时,开发者应根据项目的具体需求和性能要求来决定使用哪个库
      • 如果项目对日志性能有较高要求,或者需要异步日志记录来避免阻塞主线程,spdlog可能是更好的选择
      • 如果项目需要一些特定的日志功能,或者已经在使用glog且没有显著的性能问题,那么继续使用glog也是合理的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DieSnowK

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值