当前位置: 首页 > 工具软件 > spdlog > 使用案例 >

spdlog用法

梁丘赞
2023-12-01

转自:spdlog学习笔记

说明:所有内容翻译自spdlog的wiki,受英语水平所限,有所错误或失真在所难免,如果您有更好的建议,请在博文下留言。

线程安全


spdlog:: 命名空间下的是线程安全的,当loggers在不同的线程同时执行时,下述函数不应该被调用:

spdlog::set_error_handler(log_err_handler); // or logger->set_error_handler(log_err_handler);
  
  

    logger在其它线程执行过程中,添加或移除sink是线程不安全的

    logger->sinks().push_back(new_sink); // Don't do this if other thread is already using this logger
      
      

      要创建线程安全的loggers,使用带 _mt 后缀的工厂函数,例如:

      auto logger = spdlog::basic_logger_mt(...);
        
        

        要创建单线程的loggers,使用带 _st 后缀的工厂函数,例如:

        auto logger = spdlog::basic_logger_st(...);
          
          

          对于sinks,以 _mt 后缀结尾的是线程安全的,比如:daily_file_sink_mt

          _st 后缀结尾的是非线程安全的,比如:daily_file_sink_st

          快速开始


          #include "spdlog/spdlog.h"
           int main()
           {
            //Use the default logger (stdout, multi-threaded, colored)
            spdlog::info("Hello, {}!", "World");
           }
            
            
          • 1
          • 2
          • 3
          • 4
          • 5

          spdlog是个只有头文件的库,只需要将头文件拷贝到你的工程就可以使用了,编译器需要支持C++11
          它使用一个类似python的格式API库fmt:

          logger->info("Hello {} {} !!", "param1", 123.4);
            
            

            spdlog支持使用最小集的方式,意味着你只用包含你实际需要的头文件,而不是全部,比如说你只需要使用 rotating logger,那么你只需要

            #include <spdlog/sinks/rotating_file_sink.h>
              
              

              对于异步特性,你还需要

              #include <spdlog/asynch.h>
                
                

                基本用法示例

                #include <iostream>
                #include "spdlog/spdlog.h"
                #include "spdlog/sinks/basic_file_sink.h" // support for basic file logging
                #include "spdlog/sinks/rotating_file_sink.h" // support for rotating file logging
                

                int main(int, char* [])
                {
                try
                {
                // Create basic file logger (not rotated)
                auto my_logger = spdlog::basic_logger_mt(“basic_logger”, “logs/basic.txt”);

                	<span class="token comment">// create a file rotating logger with 5mb size max and 3 rotated files</span>
                	<span class="token keyword">auto</span> file_logger <span class="token operator">=</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">rotating_logger_mt</span><span class="token punctuation">(</span><span class="token string">"file_logger"</span><span class="token punctuation">,</span> <span class="token string">"myfilename"</span><span class="token punctuation">,</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&amp;</span> ex<span class="token punctuation">)</span>
                <span class="token punctuation">{<!-- --></span>
                	std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator">&lt;&lt;</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                

                }

                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12
                • 13
                • 14
                • 15
                • 16
                • 17
                • 18
                • 19

                使用工厂函数创建异步logger

                #include "spdlog/async.h" //support for async logging.
                #include "spdlog/sinks/basic_file_sink.h"
                

                int main(int, char* [])
                {
                try
                {
                auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(“async_file_logger”, “logs/async_log.txt”);
                for (int i = 1; i < 101; ++i)
                {
                async_file->info(“Async message #{}”, i);
                }
                // Under VisualStudio, this must be called before main finishes to workaround a known VS issue
                spdlog::drop_all();
                }
                catch (const spdlog::spdlog_ex& ex)
                {
                std::cout << "Log initialization failed: " << ex.what() << std::endl;
                }
                }

                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12
                • 13
                • 14
                • 15
                • 16
                • 17
                • 18
                • 19

                创建异步logger并改变线程池设置

                #include "spdlog/async.h" //support for async logging.
                #include "spdlog/sinks/basic_file_sink.h"
                int main(int, char* [])
                {
                    try
                    {                                        
                        auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logfile", 23, 59);
                       // default thread pool settings can be modified *before* creating the async logger:
                        spdlog::init_thread_pool(10000, 1); // queue with 10K items and 1 backing thread.
                        auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");       
                        spdlog::drop_all(); 
                    }
                    catch (const spdlog::spdlog_ex& ex)
                    {
                        std::cout << "Log initialization failed: " << ex.what() << std::endl;
                    }
                }
                 
                 
                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12
                • 13
                • 14
                • 15
                • 16

                创建一个由多个loggers共享同一个输出文件的sink

                #include <iostream>
                #include "spdlog/spdlog.h"
                #include "spdlog/sinks/daily_file_sink.h"
                int main(int, char* [])
                {
                    try
                    {
                        auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logfile", 23, 59);
                        // create synchronous  loggers
                        auto net_logger = std::make_shared<spdlog::logger>("net", daily_sink);
                        auto hw_logger  = std::make_shared<spdlog::logger>("hw",  daily_sink);
                        auto db_logger  = std::make_shared<spdlog::logger>("db",  daily_sink);      
                
                    net_logger<span class="token operator">-&gt;</span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>critical<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// independent levels</span>
                    hw_logger<span class="token operator">-&gt;</span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>debug<span class="token punctuation">)</span><span class="token punctuation">;</span>
                     
                    <span class="token comment">// globally register the loggers so so the can be accessed using spdlog::get(logger_name)</span>
                    spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">register_logger</span><span class="token punctuation">(</span>net_logger<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&amp;</span> ex<span class="token punctuation">)</span>
                <span class="token punctuation">{<!-- --></span>
                    std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator">&lt;&lt;</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                

                }

                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12
                • 13
                • 14
                • 15
                • 16
                • 17
                • 18
                • 19
                • 20
                • 21
                • 22
                • 23

                创建一个对应多个sinklogger,每一个sink都有独有的格式和日志级别

                //
                // Logger with console and file output.
                // the console will show only warnings or worse, while the file will log all messages.
                // 
                #include <iostream>
                #include "spdlog/spdlog.h"
                #include "spdlog/sinks/stdout_color_sinks.h" // or "../stdout_sinks.h" if no colors needed
                #include "spdlog/sinks/basic_file_sink.h"
                int main(int, char* [])
                {
                    try
                    {
                        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");
                
                    <span class="token keyword">auto</span> file_sink <span class="token operator">=</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>make_shared<span class="token operator">&lt;</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>sinks<span class="token punctuation">:</span><span class="token punctuation">:</span>basic_file_sink_mt<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token string">"logs/multisink.txt"</span><span class="token punctuation">,</span> true<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    file_sink<span class="token operator">-&gt;</span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>trace<span class="token punctuation">)</span><span class="token punctuation">;</span>
                
                    spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>logger <span class="token function">logger</span><span class="token punctuation">(</span><span class="token string">"multi_sink"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>console_sink<span class="token punctuation">,</span> file_sink<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    logger<span class="token punctuation">.</span><span class="token function">set_level</span><span class="token punctuation">(</span>spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>level<span class="token punctuation">:</span><span class="token punctuation">:</span>debug<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    logger<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"this should appear in both console and file"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    logger<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">"this message should not appear in the console, only in the file"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                catch <span class="token punctuation">(</span><span class="token keyword">const</span> spdlog<span class="token punctuation">:</span><span class="token punctuation">:</span>spdlog_ex<span class="token operator">&amp;</span> ex<span class="token punctuation">)</span>
                <span class="token punctuation">{<!-- --></span>
                    std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Log initialization failed: "</span> <span class="token operator">&lt;&lt;</span> ex<span class="token punctuation">.</span><span class="token function">what</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>endl<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                

                }

                • 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

                日志宏定义

                在包含 *spdlog.h"之前,添加 SPDLOG_ACTIVE_LEVEL 宏定义可以设置期望的日志级别

                #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
                SPDLOG_LOGGER_TRACE(file_logger , "Some trace message that will not be evaluated.{} ,{}", 1, 3.23);
                SPDLOG_LOGGER_DEBUG(file_logger , "Some Debug message that will be evaluated.. {} ,{}", 1, 3.23);
                SPDLOG_DEBUG("Some debug message to default logger that will be evaluated");
                 
                 
                • 1
                • 2
                • 3

                记录用户定义的对象

                #include "spdlog/spdlog.h"
                #include "spdlog/fmt/ostr.h" // must be included
                #include "spdlog/sinks/stdout_sinks.h"
                

                class some_class { };
                std::ostream& operator<<(std::ostream& os, const some_class& c)
                {
                return os << “some_class”;
                }

                void custom_class_example()
                {
                some_class c;
                auto console = spdlog::stdout_logger_mt(“console”);
                console->info(“custom class with operator<<: {}…”, c);
                }

                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12
                • 13
                • 14
                • 15

                创建 logger


                每一个logger中包含一个存有一个或多个 std::shared_ptr<spdlog::sink>的 vector

                logger在记录每一条日志时(如果是有效的级别),将会调用每一个std::shared_ptr<spdlog::sink>中的sink(log_msg)函数

                spdlog_mt(多线程)和_st(单线程)后缀标识sink是否是线程安全

                单线程的sink不可以在多线程中使用,它的速度会更快,因为没有锁竞争

                使用工厂函数创建loggers

                //Create and return a shared_ptr to a multithreaded console logger.
                #include "spdlog/sinks/stdout_color_sinks.h"
                auto console = spdlog::stdout_color_mt("some_unique_name");
                 
                 
                • 1
                • 2

                这样就创建了一个console logger,并以"some_unique_name"作为它的id,将自身注册到spdlog,返回自身的智能指针

                使用spdlog::get("...")访问loggers

                loggers可以在任何地方使用线程安全的spdlog::get("logger_name")来进行访问,返回智能指针

                注意spdlog::get可能会拖慢你的程序,因为它内部维护了一把锁,所以要谨慎使用。比较推荐的用法是保存返回的shared_ptr<spdlog::logger>,直接使用它,至少在频繁访问的代码中。

                一个很好的方法是建立一个std::shared_ptr<spdlog::logger>私有成员变量,并在构造函数中初始化:

                class MyClass
                {
                private:
                   std::shared_ptr<spdlog::logger> _logger;
                public:
                   MyClass()
                   {
                     //set _logger to some existing logger
                     _logger = spdlog::get("some_logger");
                     //or create directly
                     //_logger = spdlog::rotating_file_logger_mt("my_logger", ...);
                   }
                };
                 
                 
                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9
                • 10
                • 11
                • 12

                注意2:手动创建的logger不会被自动注册,并且也不会通过get(...)调用查找到

                注册手动创建的logger对象需要使用register_logger(...)函数:

                spdlog::register_logger(my_logger);
                ...
                auto the_same_logger = spdlog::get("mylogger");
                 
                 
                • 1
                • 2

                创建 rotating file logger

                //Create rotating file multi-threaded logger
                #include "spdlog/sinks/rotating_file_sink.h"
                auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
                ...
                auto same_logger= spdlog::get("file_logger");
                 
                 
                • 1
                • 2
                • 3
                • 4

                创建异步logger

                #include "spdlog/async.h"
                void async_example()
                {
                    // default thread pool settings can be modified *before* creating the async logger:
                    // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
                    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
                    // alternatively:
                    // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
                

                }

                • 1
                • 2
                • 3
                • 4
                • 5
                • 6
                • 7
                • 8
                • 9

                对于异步日志记录,spdlog使用具有专用消息队列的共享全局线程池。

                为此,它在消息队列中创建固定数量的预分配的插槽(64位中每个插槽大约256个字节),并且可以使用spdlog::init_thread_pool(queue_size,backing_threads_count)进行修改。

                当尝试记录一条日志时,并且队列已满,那么调用默认会被阻塞,并且默认直到一个插槽可用时,或者立即移除队列中最旧的日志信息,并追加最新的日志信息(如果loggerasync_overflow_policy==overrun_oldest构造)

                手动创建loggers

                auto sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
                auto my_logger= std::make_shared<spdlog::logger>("mylogger", sink);
                 
                 
                • 1

                创建拥有多个sinklogger

                std::vector<spdlog::sink_ptr> sinks;
                sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_st>());
                sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_st>("logfile", 23, 59));
                auto combined_logger = std::make_shared<spdlog::logger>("name", begin(sinks), end(sinks));
                //register it if you need to access it globally
                spdlog::register_logger(combined_logger);
                 
                 
                • 1
                • 2
                • 3
                • 4
                • 5

                创建多个具有同一个输出文件的file logger

                auto sharedFileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("fileName.txt");
                auto firstLogger = std::make_shared<spdlog::logger>("firstLoggerName", sharedFileSink);
                auto secondLogger = std::make_unique<spdlog::logger>("secondLoggerName", sharedFileSink);
                 
                 
                • 1
                • 2

                自定义格式


                每一个loggersink都有一个格式化器,用来格式化消息为目标格式

                spdlog默认的日志格式为:

                [2019-04-18 13:31:59.678] [info] [my_loggername] Some message
                
                 
                 
                • 1

                有两种方式可以自定义logger的格式:

                • 设置模式字符串(推荐
                set_pattern(pattern_string);
                 
                 
                  • 或者实现自定义格式器,实现formatter接口,并调用
                  set_formatter(std::make_shared<my_custom_formatter>());
                   
                   

                    使用```set_pattern(…)`自定义格式

                    格式应用到所有被注册的logger

                    spdlog::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
                     
                     

                      或者一个特定的logger对象:

                      some_logger->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
                       
                       

                        或者一个特定的sink对象:

                        some_logger->sinks()[0]->set_pattern(">>>>>>>>> %H:%M:%S %z %v <<<<<<<<<");
                        some_logger->sinks()[1]->set_pattern("..");
                         
                         
                        • 1

                        性能

                        无论何时用户调用set_pattern(...),库都会将新模式设置为内部有效标识 - 这种方式使得即便在复杂模式下,性能仍然很高(每次记录日志时不会重新解析模式)

                        模式标记

                        flagmeaningexample
                        %vThe actual text to log“some user text”
                        %tThread id“1232”
                        %PProcess id“3456”
                        %nLogger’s name“some logger name”
                        %lThe log level of the message“debug”, “info”, etc
                        %LShort log level of the message“D”, “I”, etc
                        %aAbbreviated weekday name“Thu”
                        %AFull weekday name“Thursday”
                        %bAbbreviated month name“Aug”
                        %BFull month name“August”
                        %cDate and time representation“Thu Aug 23 15:35:46 2014”
                        %CYear in 2 digits“14”
                        %YYear in 4 digits“2014”
                        %D or %xShort MM/DD/YY date“08/23/14”
                        %mMonth 1-12“11”
                        %dDay of month 1-31“29”
                        %HHours in 24 format 0-23“23”
                        %IHours in 12 format 1-12“11”
                        %MMinutes 0-59“59”
                        %SSeconds 0-59“58”
                        %eMillisecond part of the current second 0-999“678”
                        %fMicrosecond part of the current second 0-999999“056789”
                        %FNanosecond part of the current second 0-999999999“256789123”
                        %pAM/PM“AM”
                        %r12 hour clock“02:55:02 pm”
                        %R24-hour HH:MM time, equivalent to %H:%M“23:55”
                        %T or %XISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S“23:55:59”
                        %zISO 8601 offset from UTC in timezone ([+/-]HH:MM)“+02:00”
                        %ESeconds since the epoch“1528834770”
                        %iMessage sequence number (disabled by default - edit ‘tweakme.h’ to enable)“1154”
                        %%The % sign“%”
                        %+spdlog’s default format“[2014-10-31 23:46:59.678] [mylogger] [info] Some message”
                        %^start color range“[mylogger] [info(green)] Some message”
                        %$end color range (for example %1%$ %v)[+++] Some message
                        %@Source file and line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.)my_file.cpp:123
                        %sSource file (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.)my_file.cpp
                        %#Source line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.)123
                        %!Source function (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc. see tweakme for pretty-print)my_func

                        对齐

                        每一个模式标记可以通过预先添加一个宽度标记来对齐(上限128)

                        使用-(左对齐)或者=(中间对齐)去控制向哪边对齐:

                        alignmeaningexampleresult
                        %<width><flag>Align to the right%8l"    info"
                        %-<width><flag>Align to the left%-8l"info    "
                        %=<width><flag>Align to the center%=8l"  info  "

                        sink


                        sink是实际将日志写入目标位置的对象。每一个sink仅应负责写一个目标文件(比如 file,console,db),并且每一个sink有专属的私有格式化器formatter实例。

                        可用的sink

                        rotating_file_sink

                        达到最大文件大小时,关闭文件,重命名文件并创建新文件。 最大文件大小和最大文件数都可以在构造函数中配置。

                        注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹

                        // create a thread safe sink which will keep its file size to a maximum of 5MB and a maximum of 3 rotated files.
                        #include "spdlog/sinks/rotating_file_sink.h"
                        ...
                        auto file_logger = spdlog::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
                         
                         
                        • 1
                        • 2
                        • 3

                        或者手动创建sink并将它传递给logger

                        #include "spdlog/sinks/rotating_file_sink.h"
                        ...
                        auto rotating = make_shared<spdlog::sinks::rotating_file_sink_mt> ("log_filename", "log", 1024*1024, 5, false);
                        auto file_logger = make_shared<spdlog::logger>("my_logger", rotating);
                         
                         
                        • 1
                        • 2
                        • 3

                        daily_file_sink

                        每天在一个特别的时间创建一个新的日志文件,并在文件名字上添加一个时间戳

                        注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹

                        #include "spdlog/sinks/daily_file_sink.h"
                        ..
                        auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily", 14, 55);
                         
                         
                        • 1
                        • 2

                        将会在每天的14:55创建一个新的日志文件,并创建一个线程安全的sink

                        simple_file_sink

                        无任何限制的向一个日志文件中写入

                        注意:用户应该负责去创建任何他们需要的文件夹。spdlog除了文件不会尝试创建任何文件夹

                        #include "spdlog/sinks/basic_file_sink.h"
                        ...
                        auto logger = spdlog::basic_logger_mt("mylogger", "log.txt");
                         
                         
                        • 1
                        • 2

                        stdout_sink/stderr_sink with colors

                        #include "spdlog/sinks/stdout_color_sinks.h"
                        ...
                        auto console = spdlog::stdout_color_mt("console");
                        auto err_console = spdlog::color_logger_mt("console");
                         
                         
                        • 1
                        • 2
                        • 3

                        或者直接创建sink

                        auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                         
                         

                          ostream_sink

                          #include "spdlog/sinks/ostream_sink.h "
                          ...
                          std::ostringstream oss;
                          auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_mt> (oss);
                          auto logger = std::make_shared<spdlog::logger>("my_logger", ostream_sink);
                           
                           
                          • 1
                          • 2
                          • 3
                          • 4

                          null_sink

                          会丢弃所有到它的日志

                          #include "spdlog/sinks/null_sink.h"
                          ...
                          auto logger = spdlog::create<spdlog::sinks::null_sink_st>("null_logger");
                           
                           
                          • 1
                          • 2

                          syslog_sink

                          POSIX syslog(3) 发送日志到syslog

                          #include "spdlog/sinks/syslog_sink.h"
                          ...
                          auto syslog_logger = spdlog::syslog_logger("syslog", "my_ident");
                           
                           
                          • 1
                          • 2

                          dist_sink

                          将日志消息分发到其他接收器列表

                          #include "spdlog/sinks/syslog_sink.h"
                          ...
                          auto dist_sink = make_shared<spdlog::sinks::dist_sink_st>();
                          auto sink1 = make_shared<spdlog::sinks::stdout_sink_st>();
                          auto sink2 = make_shared<spdlog::sinks::simple_file_sink_st>("mylog.log");
                          

                          dist_sink->add_sink(sink1);
                          dist_sink->add_sink(sink2);

                          • 1
                          • 2
                          • 3
                          • 4
                          • 5
                          • 6
                          • 7

                          msvc_sink

                          Windows debug sink (使用OutputDebugStringA窗口输出日志)

                          #include "spdlog/sinks/msvc_sink.h"
                          auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
                          auto logger = std::make_shared<spdlog::logger>("msvc_logger", sink);
                           
                           
                          • 1
                          • 2

                          实现自己的sink

                          实现自己的sink,你需要实现sink类的接口

                          一种推荐的方式是继承自base_sink

                          该类已经处理了线程锁,使得实现一个线程安全的sink非常容易

                          #include "spdlog/sinks/base_sink.h"
                          

                          template<typename Mutex>
                          class my_sink : public spdlog::sinks::base_sink <Mutex>
                          {
                          ...
                          protected:
                          void sink_it_(const spdlog::details::log_msg& msg) override
                          {

                          <span class="token comment">// log_msg is a struct containing the log entry info like level, timestamp, thread id etc.</span>
                          <span class="token comment">// msg.raw contains pre formatted log</span>
                          
                          <span class="token comment">// If needed (very likely but not mandatory), the sink formats the message before sending it to its final destination:</span>
                          fmt<span class="token punctuation">:</span><span class="token punctuation">:</span>memory_buffer formatted<span class="token punctuation">;</span>
                          sink<span class="token punctuation">:</span><span class="token punctuation">:</span>formatter_<span class="token operator">-&gt;</span><span class="token function">format</span><span class="token punctuation">(</span>msg<span class="token punctuation">,</span> formatted<span class="token punctuation">)</span><span class="token punctuation">;</span>
                          std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator">&lt;&lt;</span> fmt<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">to_string</span><span class="token punctuation">(</span>formatted<span class="token punctuation">)</span><span class="token punctuation">;</span>
                          <span class="token punctuation">}</span>
                          
                          <span class="token keyword">void</span> <span class="token function">flush_</span><span class="token punctuation">(</span><span class="token punctuation">)</span> override 
                          <span class="token punctuation">{<!-- --></span>
                             std<span class="token punctuation">:</span><span class="token punctuation">:</span>cout <span class="token operator">&lt;&lt;</span> std<span class="token punctuation">:</span><span class="token punctuation">:</span>flush<span class="token punctuation">;</span>
                          <span class="token punctuation">}</span>
                          

                          };

                          #include “spdlog/details/null_mutex.h”
                          #include <mutex>
                          using my_sink_mt = my_sink<std::mutex>;
                          using my_sink_st = my_sink<spdlog::details::null_mutex>;

                          • 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

                          创建sink后,将其添加至logger

                          由于在spdlog v1.x 版本中有一个函数返回一个非常引用的skins vector,它允许你手动的向skins中添加。对于这个skins vector没有锁保护,因此它不是线程安全的。

                          inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
                          {
                              return sinks_;
                          }
                           
                           
                          • 1
                          • 2
                          • 3

                          logger及注册


                          spdlog包含一个进程内全局的注册机制,对于本进程内所有创建的loggers

                          目的是在项目的任何地方轻松访问loggers,而不会传递它们。

                          spdlog::get("logger1")->info("hello");
                          .. 
                          .. 
                          some other source file..
                          ..
                          auto l = spdlog::get("logger1");
                          l->info("hello again");
                           
                           
                          • 1
                          • 2
                          • 3
                          • 4
                          • 5
                          • 6

                          如果未找到logger,会返回一个空智能指针。你应该判断智能指针的有效性

                          注册新的loggers

                          一般情况下没必要去注册loggers,因为它们已经自动注册了

                          手动创建的loggers,需要自己去注册,使用 register_logger(std::shared_ptr<logger>) 函数:

                          spdlog::register_logger(some_logger);
                           
                           

                            将使用some_logger的name来注册它自己

                            注册冲突

                            当尝试注册一个名字已经被注册过的logger时,spdlog会抛出一个 spdlog::spdlog_ex异常

                            从注册器中移除loggers

                            drop()函数可以用来从注册器中移除一个logger

                            如果logger智能指针没有其它引用时,该logger将会被关闭并且释放它相关的资源

                            spdlog::drop("logger_name");
                            //or remove them all
                            spdlog::drop_all()
                             
                             
                            • 1
                            • 2

                            异步日志


                            创建异步logger有多种方式。你需要 #include "spdlog/async.h

                            使用 <spdlog::async_logger>模板参数

                            #include "spdlog/async.h"
                            void async_example()
                            {
                                // default thread pool settings can be modified *before* creating the async logger:
                                // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
                                auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
                            }
                             
                             
                            • 1
                            • 2
                            • 3
                            • 4
                            • 5
                            • 6

                            使用 spdlog::create_async<sink>

                            auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
                             
                             

                              使用 spdlog::create_async_nb<sink>

                              创建即便队列满也永远不会阻塞的logger

                              auto async_file = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
                               
                               

                                直接构造,并使用全局线程池

                                auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, spdlog::thread_pool(), async_overflow_policy::block);
                                 
                                 

                                  直接构造,使用自定义线程池

                                  spdlog::init_thread_pool(queue_size, n_threads);
                                  auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, spdlog::thread_pool(), async_overflow_policy::block);
                                   
                                   
                                  • 1

                                  直接构造,使用自定义线程池

                                  auto tp = std::make_shared<details::thread_pool>(queue_size, n_threads);
                                  auto logger = std::make_shared<spdlog::async_logger>("as", some_sink, tp, async_overflow_policy::block);
                                   
                                   
                                  • 1

                                  注意:上例中的tp对象的生命期一定要长于logger对象,因为logger需要一个tp对象的weak_ptr

                                  队列满时的决策

                                  当队列满了的时候有两种可选方式:

                                  • 阻塞调用直到有空间可用(默认行为)
                                  • 移除并替换队列中最旧的信息,不用等待可用空间

                                  使用 create_async_nb 工厂函数或者 在logger构造时使用spdlog::async_overflow_policy

                                  auto logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
                                  // or directly:
                                   auto logger = std::make_shared<async_logger>("as", test_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::overrun_oldest);
                                   
                                   
                                  • 1
                                  • 2

                                  spdlog的线程池

                                  默认情况下,spdlog创建一个全局的线程池,队列大小为8192,一个工作线程服务于所有的async loggers

                                  这意味着创建和销毁 async loggers非常廉价,因为它们不拥有或者创建任何后台线程或队列–它们被共享的线程池对象创建和管理

                                  队列中所有的槽都是在线程池构造时预分配的(64位系统中每个槽占256字节)

                                  线程池的大小和线程可以被重置:

                                  spdlog::init_thread_pool(queue_size, n_threads);
                                   
                                   

                                    注意:这将会销毁之前的全局线程池对象tp,并创建一个新的线程池–这也意味着所有使用旧的线程池tp的loggers都将停止工作,因此建议在任何async loggers被创建之前调用该函数

                                    如果不同的loggers必须要使用不同的队列,那么可以创建不同的线程池,并传递给loggers:

                                    auto tp = std::make_shared<details::thread_pool>(128, 1);
                                     auto logger = std::make_shared<async_logger>("as", some_sink, tp, async_overflow_policy::overrun_oldest);
                                    

                                    auto tp2 = std::make_shared<details::thread_pool>(1024, 4); // create pool with queue of 1024 slots and 4 backing threads
                                    auto logger2 = std::make_shared<async_logger>(“as2”, some_sink, tp2, async_overflow_policy::block);

                                    • 1
                                    • 2
                                    • 3
                                    • 4

                                    Windows上的问题

                                    在VS运行时存在一个bug,在退出时会导致应用程序死锁。如果你使用异步日志记录,一定要确保在main()函数退出时调用spdlog::shutdown()函数

                                    Flush策略


                                    默认情况下,spdlog允许底层libc在它认为合适时进行刷新,以获得良好的性能。 您可以使用以下选项覆盖它:

                                    手动flush

                                    使用logger->flush()函数让logger去flush它的内容,logger将依次调用包含的每一个sink上的flush()函数

                                    注意:如果使用async loggerlogger->flush()会发送一个消息到队列,请求flush操作,因此函数会立即返回。这跟一些旧版本的spdlog是有区别的(老版本会同步等待直到flush完成,并接收到消息)

                                    基于严重性的flush

                                    可以设置一个最小日志等级来触发自动flush

                                    下例中,只要error或者更严重的日志被记录时就会触发flush:

                                    my_logger->flush_on(spdlog::level::err);
                                     
                                     

                                      基于间隔的flush

                                      spdlog支持设置flush间隔。由一个单独的工作线程定期调用每一个logger的flush()实现

                                      下例中,对所有已注册的loggers定期5秒调用flush():

                                      spdlog::flush_every(std::chrono::seconds(5));
                                       
                                       

                                        注意:仅应在线程安全的loggers上使用该特性,因为定期flush是从不同的线程发生的

                                        默认的logger


                                        为方便起见,spdlog创建了一个默认的全局记录器(stdout,colors和multithreaded)。

                                        通过直接调用 spdlog::info(..), spdlog::debug(..), etc

                                        下例可以替换任何其它logger为默认logger:

                                        spdlog::set_default_logger(some_other_logger);
                                        spdlog::info("Use the new default logger");
                                         
                                         
                                        • 1

                                        错误处理


                                        spdlog在记录过程中不会抛出异常

                                        在构造logger或sink时可能会抛出异常,因为它认为出了严重错误

                                        如果在日志记录过程中发生了错误,spdlog会打印错误信息到stderr

                                        为了避免满屏幕大量打印错误信息,限制速率为每个logger 1 条消息/分钟

                                        该行为可以被改变,通过调用spdlog::set_error_handler(new_handler_fun) 或者 logger->set_error_handler(new_handler_fun)

                                        修改全局错误处理句柄

                                        spdlog::set_error_handler([](const std::string& msg) {
                                                std::cerr << "my err handler: " << msg << std::endl;
                                            });
                                         
                                         
                                        • 1
                                        • 2

                                        对于特定的logger

                                        critical_logger->set_error_handler([](const std::string& msg) {
                                                throw std::runtime_error(msg);
                                            });
                                         
                                         
                                        • 1
                                        • 2

                                        默认错误处理句柄

                                        default_err_handler会使用下面语法打印错误

                                        fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
                                         
                                         

                                          如何在dll中使用spdlog


                                          由于spdlog是仅有头文件的库,构建共享库和在主程序中使用它将不会在它们之间共享注册器信息

                                          就是说调用类似于 spdlog::set_level(spdlog::level::level_enum::info) 将不会改变dll中的loggers

                                          解决办法

                                          在主程序和dll中都注册logger

                                          /*
                                           * Disclaimer:
                                           *   This was not compiled but extracted from documentation and some code.
                                           */
                                          

                                          // mylibrary.h
                                          // In library, we skip the symbol exporting part

                                          #include <memory>
                                          #include <vector>
                                          #include <spdlog/logger.h>
                                          #include <spdlog/sinks/stdout_color_sinks.h>

                                          namespace library
                                          {
                                          static const std::string logger_name = “example”;

                                          std::shared_ptr<spdlog::logger> setup_logger(std::vector<spdlog::sink_ptr> sinks)
                                          {
                                          auto logger = spdlog::get(logger_name);
                                          if(not logger)
                                          {
                                          if(sinks.size() > 0)
                                          {
                                          logger = std::make_shared<spdlog::logger>(logger_name,
                                          std::begin(sinks),
                                          std::end(sinks));
                                          spdlog::register_logger(logger);
                                          }
                                          else
                                          {
                                          logger = spdlog::stdout_color_mt(logger_name);
                                          }
                                          }

                                          <span class="token keyword">return</span> logger<span class="token punctuation">;</span>
                                          

                                          }

                                          void test(std::string message)
                                          {
                                          auto logger = spdlog::get(logger_name);
                                          if(logger)
                                          {
                                          logger->debug("{}::{}", FUNCTION, message);
                                          }
                                          }

                                          }

                                          • 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

                                          // In the main program
                                          
                                          #include <mylibrary.h>
                                          #include <spdlog/logger.h>
                                          #include <spdlog/sinks/daily_file_sink.h>
                                          #include <spdlog/sinks/stdout_sinks.h>
                                          
                                          int main()
                                          {
                                              // We assume that we load the library here
                                              ...
                                          
                                              // Let's use the library
                                              std::vector<spdlog::sink_ptr> sinks;
                                              sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_st>());
                                              sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_st>("logfile", 23, 59));
                                          
                                              auto logger = library::setup_logger(sinks);
                                          
                                              spdlog::set_level(spdlog::level::level_enum::debug); // No effect for the library.
                                              library::test("Hello World!"); // No logging
                                          
                                              spdlog::register_logger(logger);
                                          
                                              // Now this will also affect the library logger
                                              spdlog::set_level(spdlog::level::level_enum::debug);
                                          
                                              library::test("Hello World!"); // Hurray !
                                          
                                              return 0;
                                          }
                                           
                                           
                                          • 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

                                          1. +++ ↩︎

                                           类似资料: