spdlog介绍 spdlog 是一个高性能 、超快速 、零配置 的C++日志库,它旨在提供简洁的API和丰富的功能,同时保持高性能 的日志记录。它支持多种输出目标 、格式化选项 、线程安全 以及异步 日志记录
为什么用spdlog 它有如下优点
高性能 :spdlog 专为速度而设计,即使在高负载情况下也能保持良好的性能。  
零配置 :无需复杂的配置,只需包含头文件即可在项目中使用。  
异步日志 :支持异步日志记录,减少对主线程的影响。  
格式化 :支持自定义日志消息的格式化,包括时间戳、线程 ID、日志级别等。  
多平台 :跨平台兼容,支持 Windows、Linux、macOS 等操作系统。  
丰富的 API :提供丰富的日志级别和操作符重载,方便记录各种类型的日志。 
 
spdlog安装 前置依赖库安装 spdlog依赖fmt库,如果没有,需要提前安装
1 2 3 sudo apt update -y sudo apt install -y  libfmt-dev 
 
同时编译的时候要加上选项 -lfmt 
apt安装 1 2 sudo apt update sudo apt install -y libspdlog-dev 
 
源码安装 1 2 3 4 5 git clone https://github.com/gabime/spdlog.git cd spdlog/  mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=/usr .. make && sudo make install 
 
spdlog使用 目前用到的头文件均来自于:
1 2 3 4 5 #include  <spdlog/spdlog.h>    #include  <spdlog/logger.h>  #include  <spdlog/async_logger.h>      #include  <spdlog/async.h>         
 
编译时要添加 -lpthread 和 -fmt 库链接 
日志输出等级枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace  spdlog{namespace  level {enum  level_enum {     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 }; } } 
 
日志输出格式自定义 假定我们现在有一个日志器对象的指针logger,我们可以调用它的set_pattern接口来设置格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 logger->set_pattern ("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v" ); 
 
日志记录器类的接口声明 spdlog库提供了日志记录器的工厂模式 ,我们可以直接构造日志器对象sdplog::logger,也可以通过工厂构造。
下面是它的接口声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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)  ;  };  } 
 
异步日志记录器类 上面的那个只是基类,而现在这个spdlog::async_logger支持异步写日志
接口声明如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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);  };    
 
日志记录器工厂类 异步日志器工厂模式由spdlog/async.h提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 namespace  spdlog{template <async_overflow_policy OverflowPolicy = async_overflow_policy::block>struct  async_factory_impl{    template <typename  Sink, typename ... SinkArgs>     static  std::shared_ptr<async_logger> create (std::string logger_name, SinkArgs &&...args); }; using  async_factory = async_factory_impl<async_overflow_policy::block>;using  async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;template <typename  Sink, typename ... SinkArgs>inline  std::shared_ptr<spdlog::logger> create_async (std::string logger_name, SinkArgs &&...sink_args)  ;template <typename  Sink, typename ... SinkArgs>inline  std::shared_ptr<spdlog::logger> create_async_nb (std::string logger_name, SinkArgs &&...sink_args)  ;inline  void  init_thread_pool (size_t  q_size, size_t  thread_count, std::function<void ()> on_thread_start)  ;inline  void  init_thread_pool (size_t  q_size, size_t  thread_count)  ;inline  std::shared_ptr<spdlog::details::thread_pool> thread_pool ()  {    return  details::registry::instance ().get_tp (); } } 
 
各种同步日志器工厂模式由spdlog/sinks/下的特殊头文件提供
比如下面的工厂来自头文件<spdlog/sinks/stdout_color_sinks.h>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 namespace  spdlog{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> stdout_color_st (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> stderr_color_st (const  std::string &logger_name, color_mode mode = color_mode::automatic)  ;}  
 
日志落地类 日志落地类的实现方式有很多,其中还区分了单线程无锁版本和多线程锁保护版本,这里不多赘述,只展示基类
*_st:single thread 单线程版本,不用加锁,效率更高。 
*_mt:multi thread 多线程版本,用于多线程程序是线程安全的 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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)  ;     level::level_enum level ()  const  ;     bool  should_log (level::level_enum msg_level)  const  ; protected :         level_t  level_{level::trace}; }; }  }  
 
全局接口 1 2 3 4 5 6 void  set_level (level::level_enum log_level)  ; void  flush_every (std::chrono::seconds interval)  void  flush_on (level::level_enum log_level)  ;
 
代码样例 我们来测试如下要点
多落地方向同步输出
 
设置输出等级下限 
异步输出日志 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include  <spdlog/spdlog.h>   #include  <spdlog/sinks/stdout_color_sinks.h>   #include  <spdlog/sinks/basic_file_sink.h>   #include  <spdlog/async.h>     void  multi_sink_example ()   {          auto  console_sink = std::make_shared <spdlog::sinks::stdout_color_sink_mt>();           console_sink->set_level (spdlog::level::warn);      console_sink->set_pattern ("[multi_sink_example] [%^%l%$] %v" );           auto  file_sink = std::make_shared <spdlog::sinks::basic_file_sink_mt>(          "logs/multisink.txt" , true );           file_sink->set_level (spdlog::level::trace);             spdlog::logger logger ("multi_sink" , {console_sink, file_sink})  ;          logger.set_level (spdlog::level::debug);      logger.set_pattern ("%Y-%m-%d %H:%M:%S [%l] %v" );      logger.warn ("this should appear in both console and file" );      logger.info ("this message should not appear in the console, only in the file" );  }    void  async_example ()   {          spdlog::init_thread_pool (32768 , 1 );          auto  async_logger = spdlog::basic_logger_mt <spdlog::async_factory>(          "async_file_logger" , "logs/async_log.txt" );      async_logger->set_pattern ("%Y-%m-%d %H:%M:%S [%l] %v" );      for  (int  i = 1 ; i < 101 ; ++i) {                            async_logger->info ("Async message #{} {}" , i, "hello" );      }  }    int  main ()   {     multi_sink_example ();           return  0 ;  } 
 
运行结果如下 
多落地方向 
 
异步输出 
 
二次封装spdlog 因为spdlog的日志输出对文件名和行号并不是很友好,以及因为spdlog本身实现了线程安全,如果使用默认日志器每次进行单例获取,效率会有降低,因此进行二次封装,简化使用。 
我们主要实现如下几点功能:
日志的初始化封装接口 
日志的输出接口封装宏  
仅使用唯一的全局日志器对象 
 
logger.hpp
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #pragma  once #include  <spdlog/spdlog.h>  #include  <spdlog/sinks/stdout_color_sinks.h>  #include  <spdlog/sinks/basic_file_sink.h>  #include  <spdlog/async.h>  #include  <iostream>  namespace  btyGoose{std::shared_ptr<spdlog::logger> g_default_logger; void  init_logger (bool  mode, const  std::string &file, int32_t  level)  {    if  (mode == false ) {                  g_default_logger = spdlog::stdout_color_mt ("default-logger" );         g_default_logger->set_level (spdlog::level::level_enum::trace);         g_default_logger->flush_on (spdlog::level::level_enum::trace);         g_default_logger->set_pattern ("[%n][%H:%M:%S][%t]%^[%-8l]%$%v" );     }else  {                  g_default_logger = spdlog::basic_logger_mt ("default-logger" , file);         g_default_logger->set_level ((spdlog::level::level_enum)level);         g_default_logger->flush_on ((spdlog::level::level_enum)level);         g_default_logger->set_pattern ("[%n][%H:%M:%S][%t][%-8l]%v" );     }      } #define  LOG_TRACE(format, ...) btyGoose::g_default_logger->trace(std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) #define  LOG_DEBUG(format, ...) btyGoose::g_default_logger->debug(std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) #define  LOG_INFO(format, ...) btyGoose::g_default_logger->info(std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) #define  LOG_WARN(format, ...) btyGoose::g_default_logger->warn(std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) #define  LOG_ERROR(format, ...) btyGoose::g_default_logger->error (std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) #define  LOG_FATAL(format, ...) btyGoose::g_default_logger->critical(std::string("[{}:{}] " ) + format, __FILE__, __LINE__, ##__VA_ARGS__) } 
 
小小地测试一下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include  "logger.hpp"  #include  <string>  #include  <string.h>  const  std::string log_file ("log_file.txt" )  ;const  int32_t  level = spdlog::level::warn;const  bool  release_mode = false ;int  main ()   {     btyGoose::init_logger (release_mode,log_file,level);     LOG_TRACE ("trace" );     LOG_DEBUG ("I have {} bugs" ,555 );     LOG_WARN ("this is a warn" );     LOG_ERROR ("error! error str: {}" ," I am a error str" );     LOG_FATAL ("fatal, fail to solve" );     return  0 ;  } 
 
输出结果