Boost.log:当使用add_file_log()函数时,如何防止输出将被复制到所有添加的流? [英] Boost.log: How to prevent the output will be duplicated to all added streams when it uses the add_file_log() function?

查看:2009
本文介绍了Boost.log:当使用add_file_log()函数时,如何防止输出将被复制到所有添加的流?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 add_file_log()函数来初始化将日志记录存储到文本文件中的日志记录接收器。当我定义几个接收器,我观察到:




  • 为每个接收器创建一个文件。

  • 这是我的记录器:

    pre> class logger
    {
    public:
    logger(const logger&)= delete;
    logger(logger&&)= delete;
    logger& operator =(const logger&)= delete;
    logger& operator =(logger&&)= delete;

    静态记录器get_instance(
    const std :: string& file,
    bool console

    {
    boost :: log :: register_simple_formatter_factory<
    boost :: log :: trivial :: severity_level,
    char
    >(Severity);

    std :: string the_format =[%TimeStamp%](%LineID%)[%Severity%]:%Message%;

    if(!file.empty()){
    boost :: log :: add_file_log(
    boost :: log :: keywords :: file_name = file +_%
    boost :: log :: keywords :: rotation_size = 10 * 1024 * 1024,
    boost :: log :: keywords :: time_based_rotation =
    boost :: log: :sinks :: file :: rotation_at_time_point(0,0,0),
    boost :: log :: keywords :: auto_flush = true,
    // boost :: log :: keywords :: open_mode = (std :: ios :: out | std :: ios :: app),
    boost :: log :: keywords :: format = the_format
    );
    }

    boost :: log :: add_common_attributes();
    static logger instance {the_format,console};
    return instance;
    }

    void log(
    const std :: string& msg

    {
    BOOST_LOG_SEV(m_log_,boost :: log: :trivial :: info)<< msg;
    }

    private:
    boost :: log :: sources :: severity_logger<
    boost :: log :: trivial :: severity_level
    > m_log_;

    logger(
    const std :: string& format,
    bool console

    {
    if(console){
    boost :: log :: add_console_log(
    std :: clog,
    boost :: log :: keywords :: format = format
    );
    }
    }
    }; // logger

    这是我的 main() function:

      void test(
    const std :: string& file

    {
    logger& lg1 = logger :: get_instance(file,false);
    lg1.log(Hello);
    lg1.log(World);
    lg1.log(Bye);
    } // test

    int main()
    {
    unsigned char result = EXIT_SUCCESS;

    try
    {
    std :: string file1 =a.txt,
    file2 =b.txt
    logger& lg = logger :: get_instance(file1,false);

    for(int i = 1; i <= 10; i ++){
    lg.log(std :: to_string(i));
    if(i == 5){
    test(file2);
    }
    }
    }
    catch(std :: exception& e)
    {
    std :: cerr< Error:< e.what()< std :: endl;
    result = EXIT_FAILURE;
    }
    return result;
    }

    运行示例后,文件包含:



    a.txt_0.log

      [2016-Aug-31 11:49:48.584353](1)[信息]:1 
    [2016年8月31日11:49:48.585376](2)[信息]:2
    [2016年8月31日11: 49:48.585418](3)[info]:3
    [2016年8月31日11:49:48.585442](4)[信息]:4
    [2016-Aug-31 11:49: 48.585462](5)[信息]:5
    [2016年8月31日11:49:48.585505](6)[信息]:你好< -
    [2016年8月31日11: 49:48.585610](7)[info]:World < - 由第二个记录器生成
    [2016-Aug-31 11:49:48.585672](8)[info]:Bye < -
    [2016年8月31日11:49:48.585709](9)[信息]:6
    [2016年8月31日11:49:48.585744] [2016年8月31日11:49:48.585777](11)[信息]:8
    [2016年8月31日11:49:48.585813] -Aug-31 11:49:48.585842](13)[info]:10



    < b.txt_0.log

      [2016-Aug-31 11:49:48.585505] ]:Hello 
    [2016-Aug-31 11:49:48.585610](7)[info]:World
    [2016年8月31日11:49:48.585672] Bye
    [2016-Aug-31 11:49:48.585709](9)[info]:6 < -
    [2016-Aug-31 11:49:48.585744] ]:7 < -
    [2016-Aug-31 11:49:48.585777](11)[info]:8 < - 由第一个记录器生成
    [2016- 11:49:48.585813](12)[info]:9 < -
    [2016-Aug-31 11:49:48.585842](13)[info]:10 < -

    如何防止此行为?

    解决方案

    你似乎有一个误解Boost.Log



    sink 。源接收数据(例如字符串),并使用它创建条目。然后将条目给予核心,其将其分派给所有的宿。然后,汇可以过滤,格式化和输出到任何他们想要的地方,如 stdout 或一个文件。



    的示例是您正在使用的 severity_logger 。你可能习惯使用logger而不是source,但是logger不是很精确,因为logging是一个多阶段的过程。



    通常不必创建多个(logger)。相反,您可以添加多个全局 sink

      + -----------在这种情况下, --- + 
    + ---> |控制台槽| ----> stdout
    | + -------------- +
    |
    + -------- + + ------ + | + -------------- +
    |源| ---> |核心| --- + ---> |文件接收器| ----> log1.txt
    + -------- + + ------ + | + -------------- +
    |
    | + -------------- +
    + ---> |文件接收器| ----> log2.txt
    + -------------- +

    现在,你可以有多个logger,每个都有自己的线程模型,属性,字符类型等,但它们仍然会生成条目并将其提供给核心。



    让我们得到标题的方式:

      #include< string> 
    #include< fstream>
    #include< boost / log / sinks.hpp>
    #include< boost / log / utility / setup / formatter_parser.hpp>
    #include< boost / log /源/ severity_channel_logger.hpp>
    #include< boost / log / trivial.hpp>
    #include< boost / log / utility / setup / file.hpp>
    #include& lt; boost / log / utility / setup / common_attributes.hpp>
    #include< boost / log / utility / setup / console.hpp>
    #include< boost / log / expressions.hpp>
    #include< boost / log / attributes / scoped_attribute.hpp>

    命名空间bl = boost :: log;

    让我们开始:

      BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr,Tag,std :: string); 

    一个日志条目具有属性 。这些属性通常用于格式化(如[%TimeStamp%] [%Message%]),但我们将添加一个新属性,不同的文件。我使用logger_type =
    bl :: sources :: severity_logger< bl :: 来调用属性Tag。

     trivial :: severity_level> ;; 
    static logger_type g_logger;

    const std :: string g_format =
    [%TimeStamp%](%LineID%)[%Severity%] [%Tag%]:%Message%;

    现在,在这个例子中,实际的boost记录器是一个全局对象。您可能想限制其范围,并将其传递给您自己的 logger 对象。我也把这个格式作为一个全局常量。 YMMV。



    这是 logger 类:



    <$ pre> class logger
    {
    public:
    logger(std :: string file);
    :tag_(file)
    {
    使用backend_type = bl :: sinks :: text_file_backend;
    using sink_type = bl :: sinks :: synchronous_sink< backend_type> ;;

    auto backend = boost :: make_shared< backend_type>(
    bl :: keywords :: file_name = file +_%N.log,
    bl ::关键字: :rotate_size = 10 * 1024 * 1024,
    bl :: keywords :: time_based_rotation =
    bl :: sinks :: file :: rotation_at_time_point(0,0,0),
    bl :: keywords :: auto_flush = true);

    auto sink = boost :: make_shared< sink_type>(backend);
    sink-> set_formatter(bl :: parse_formatter(g_format));
    sink-> set_filter(tag_attr == tag_);

    bl :: core :: get() - > add_sink(sink);
    }

    void log(const std :: string& s)
    {
    BOOST_LOG_SCOPED_THREAD_TAG(Tag,tag_);
    BOOST_LOG_SEV(g_logger,bl :: trivial :: info)< s;
    }

    private:
    const std :: string tag_;
    };

    我使用文件名作为标签,但它可以是其他任何东西,只要它独特。



    首先, text_file_backend 被创建并且被给予新的宿,然后将其添加到核。这实际上是当你调用 add_file_log()时,会发生什么,它只是一个帮助函数。我已经重用了你在你的例子中的相同的参数(文件名模式,旋转等)



    有趣的是这一个:

      sink-> set_filter(tag_attr == tag_); 

    这里, tag_attr 属性关键字。关键字在Boost.Log中有点不寻常:它们可以用于创建将在运行时评估的表达式。在这种情况下,接收器将只接受 tag_attr == tag _ 的条目。因此,当一个日志记录器记录某事时,它会将自己的标签设置为一个属性,并且接收器将忽略没有此标签的任何东西。在 log()中,您可以看到设置了Tag属性。



    这是 main()

      int main()
    {
    bl :: register_simple_formatter_factory<
    bl :: trivial :: severity_level,char>(Severity);

    boost :: log :: add_common_attributes();

    bl :: add_console_log(
    std :: clog,bl :: keywords :: format = g_format);

    logger lg1(1);
    logger lg2(2);

    lg1.log(a);
    lg1.log(b);
    lg1.log(c);

    lg2.log(d);
    lg2.log(e);
    lg2.log(f);
    }

    你会发现我已经把 logger ,因为它不真正属于那里。条目abc将被写入1_0.txtde f2_0.txt。所有六个条目都将写入控制台。

      + -------------- + 
    | lg1.log(a)|
    + -------------- +
    |
    v
    + ------------------------- +
    |条目:|
    |时间戳:1472660811 |
    |消息:a|
    | LineID:1 |
    |严重性:info |
    |标签:1|
    + ------------------------- +
    |
    v
    + ------ + + --------------------- +
    |核心| ----- + ----> |控制台槽|
    + ------ + | | file:stdout | - >写为
    | | filter:none |
    | + --------------------- +
    |
    | + --------------------- +
    | |文件接收器|
    + ----> |文件:1_0.txt| - >写为
    | | filter:tag ==1|
    | + --------------------- +
    |
    | + --------------------- +
    | |文件接收器|
    + ----> |文件:2_0.txt| - >丢弃
    | filter:tag ==2|
    + --------------------- +


    I use the add_file_log() function to initialize a logging sink that stores log records into a text file. When I define several sinks, I have observed:

    • a file is created for each sink.
    • the output is copied to all files.

    This is my logger:

    class logger
    {
    public:
      logger(const logger&) =delete;
      logger(logger&&) =delete;
      logger& operator=(const logger&) =delete;
      logger& operator=(logger&&) =delete;
    
      static logger& get_instance(
        const std::string& file,
        bool console
      )
      {
          boost::log::register_simple_formatter_factory<
                                                        boost::log::trivial::severity_level,
                                                        char
                                                       >("Severity");
    
          std::string the_format = "[%TimeStamp%] (%LineID%) [%Severity%]: %Message%";
    
          if(!file.empty()) {
            boost::log::add_file_log(
              boost::log::keywords::file_name = file + "_%N.log",
              boost::log::keywords::rotation_size = 10 * 1024 * 1024,
              boost::log::keywords::time_based_rotation =
                boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
              boost::log::keywords::auto_flush = true,
              //boost::log::keywords::open_mode = (std::ios::out | std::ios::app),
              boost::log::keywords::format = the_format
            );
          }
    
          boost::log::add_common_attributes();
          static logger instance{ the_format, console };
          return instance;
      }
    
      void log(
        const std::string& msg
      )
      {
          BOOST_LOG_SEV ( m_log_, boost::log::trivial::info ) << msg;
      }
    
    private:
      boost::log::sources::severity_logger<
                                           boost::log::trivial::severity_level
                                          > m_log_;
    
      logger(
        const std::string& format,
        bool console
      )
      {
          if(console) {
            boost::log::add_console_log(
              std::clog,
              boost::log::keywords::format = format
            );
          }
      }
    }; // logger
    

    This is my main() function:

    void test(
      const std::string& file
    )
    {
      logger& lg1 = logger::get_instance( file, false );
      lg1.log( "Hello" );
      lg1.log( "World" );
      lg1.log( "Bye" );
    } // test
    
    int main()
    {
      unsigned char result = EXIT_SUCCESS;
    
      try
      {
        std::string file1 = "a.txt",
                    file2 = "b.txt";
        logger& lg = logger::get_instance( file1, false );
    
        for(int i = 1; i<=10; i++) {
           lg.log( std::to_string(i) );
           if(i == 5) {
             test( file2 );
           }
        }
      }
      catch ( std::exception& e )
      {
        std::cerr << "Error: " << e.what() << std::endl;
        result = EXIT_FAILURE;
      }
      return result;
    }
    

    After running the example, the files contain:

    a.txt_0.log

    [2016-Aug-31 11:49:48.584353] (1) [info]: 1
    [2016-Aug-31 11:49:48.585376] (2) [info]: 2
    [2016-Aug-31 11:49:48.585418] (3) [info]: 3
    [2016-Aug-31 11:49:48.585442] (4) [info]: 4
    [2016-Aug-31 11:49:48.585462] (5) [info]: 5
    [2016-Aug-31 11:49:48.585505] (6) [info]: Hello  <-- 
    [2016-Aug-31 11:49:48.585610] (7) [info]: World  <-- Generated by second logger
    [2016-Aug-31 11:49:48.585672] (8) [info]: Bye    <--
    [2016-Aug-31 11:49:48.585709] (9) [info]: 6
    [2016-Aug-31 11:49:48.585744] (10) [info]: 7
    [2016-Aug-31 11:49:48.585777] (11) [info]: 8
    [2016-Aug-31 11:49:48.585813] (12) [info]: 9
    [2016-Aug-31 11:49:48.585842] (13) [info]: 10
    

    b.txt_0.log

    [2016-Aug-31 11:49:48.585505] (6) [info]: Hello
    [2016-Aug-31 11:49:48.585610] (7) [info]: World
    [2016-Aug-31 11:49:48.585672] (8) [info]: Bye
    [2016-Aug-31 11:49:48.585709] (9) [info]: 6    <--
    [2016-Aug-31 11:49:48.585744] (10) [info]: 7   <--
    [2016-Aug-31 11:49:48.585777] (11) [info]: 8   <-- Generated by the first logger
    [2016-Aug-31 11:49:48.585813] (12) [info]: 9   <--
    [2016-Aug-31 11:49:48.585842] (13) [info]: 10  <--
    

    How I can prevent this behavior? I want each file only stores the information generated by its associated logger.

    解决方案

    You seem to have a misunderstanding of how Boost.Log works.

    There are sources and sinks. A source takes data, such as a string, and creates an entry with it. The entry is then given to the core, which dispatches it to all the sinks. The sinks can then filter, format and output the entries to wherever they want, such as stdout or a file.

    An example of a source would be the severity_logger you are using. You might be used to the term "logger" instead of "source", but "logger" isn't very precise because logging is a multi-stage process.

    You don't usually have to create multiple sources ("loggers"). Instead, you can add multiple global sinks. In your case, you'll need a filtered sink per file.

                                      +--------------+
                                +---> | console sink | ----> stdout
                                |     +--------------+
                                |
    +--------+      +------+    |     +--------------+
    | source | ---> | core | ---+---> | file sink    | ----> log1.txt
    +--------+      +------+    |     +--------------+
                                |
                                |     +--------------+
                                +---> | file sink    | ----> log2.txt
                                      +--------------+
    

    Now, you could have multiple loggers, each with their own threading model, attributes, character type, etc., but they would still all generate entries and give them to the core. In your case, it wouldn't be very useful.

    Let's get the headers out of the way:

    #include <string>
    #include <fstream>
    #include <boost/log/sinks.hpp>
    #include <boost/log/utility/setup/formatter_parser.hpp>
    #include <boost/log/sources/severity_channel_logger.hpp>
    #include <boost/log/trivial.hpp>
    #include <boost/log/utility/setup/file.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/log/utility/setup/console.hpp>
    #include <boost/log/expressions.hpp>
    #include <boost/log/attributes/scoped_attribute.hpp>
    
    namespace bl = boost::log;
    

    Let's start:

    BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string);
    

    A log entry has attributes which can be set every time something is logged. These attributes are usually used in formatting (such as "[%TimeStamp%] [%Message%]"), but we'll add a new attribute to allow for differentiating between the different files. I called the attribute "Tag".

    using logger_type =
        bl::sources::severity_logger<bl::trivial::severity_level>;
    static logger_type g_logger;
    
    const std::string g_format =
        "[%TimeStamp%] (%LineID%) [%Severity%] [%Tag%]: %Message%";
    

    Now, in this example, the actual boost logger is a global object. You may want to restrict its scope and pass it around to your own logger objects. I've also made the the format a global constant. YMMV.

    This is the logger class:

    class logger
    {
    public:
        logger(std::string file);
            : tag_(file)
        {
            using backend_type = bl::sinks::text_file_backend;
            using sink_type = bl::sinks::synchronous_sink<backend_type>;
    
            auto backend = boost::make_shared<backend_type>(
                bl::keywords::file_name = file + "_%N.log",
                bl::keywords::rotation_size = 10 * 1024 * 1024,
                bl::keywords::time_based_rotation =
                    bl::sinks::file::rotation_at_time_point(0, 0, 0),
                bl::keywords::auto_flush = true);
    
            auto sink = boost::make_shared<sink_type>(backend);
            sink->set_formatter(bl::parse_formatter(g_format));
            sink->set_filter(tag_attr == tag_);
    
            bl::core::get()->add_sink(sink);
        }
    
        void log(const std::string& s)
        {
            BOOST_LOG_SCOPED_THREAD_TAG("Tag", tag_);
            BOOST_LOG_SEV(g_logger, bl::trivial::info) << s;
        }
    
    private:
        const std::string tag_;
    };
    

    I've used the file name as a tag, but it could be anything else as long as it's unique. Every log entry will have this tag as an attribute, which will be used in the sink filter.

    First, a text_file_backend is created and is given to a new sink, which is then added to the core. This is actually what happens when you call add_file_log(), it's just a helper function. I've reused the same parameters you had in your example (filename pattern, rotation, etc.)

    The interesting line is this one:

    sink->set_filter(tag_attr == tag_);
    

    Here, tag_attr was defined above as an attribute keyword. Keywords are a bit unusual in Boost.Log: they can be used to create expressions that will be evaluated at runtime. In this case, the sink will only accept entries where tag_attr == tag_. So when a logger logs something, it sets its own tag as a attribute, and the sink will ignore anything that doesn't have this tag. In log(), you can see the "Tag" attribute being set.

    Here's main():

    int main()
    {
        bl::register_simple_formatter_factory<
            bl::trivial::severity_level, char>("Severity");
    
        boost::log::add_common_attributes();
    
        bl::add_console_log(
            std::clog, bl::keywords::format=g_format);
    
        logger lg1("1");
        logger lg2("2");
    
        lg1.log("a");
        lg1.log("b");
        lg1.log("c");
    
        lg2.log("d");
        lg2.log("e");
        lg2.log("f");
    }
    

    You'll see that I've moved the common stuff outside of logger since it doesn't really belong there. Entries "a", "b" and "c" will be written to "1_0.txt" and "d", "e" and "f" to "2_0.txt". All six entries will be written to the console.

         +--------------+
         | lg1.log("a") |
         +--------------+
                |
                v
    +-------------------------+
    | Entry:                  |
    |  Timestamp: 1472660811  |
    |  Message:   "a"         |
    |  LineID:    1           |
    |  Severity:  info        |
    |  Tag:       "1"         |
    +-------------------------+
                |            
                v
             +------+             +---------------------+
             | core | -----+----> | console sink        |
             +------+      |      |  file: stdout       |  --> written
                           |      |  filter: none       |
                           |      +---------------------+
                           |
                           |      +---------------------+
                           |      | file sink           |
                           +----> |  file: "1_0.txt"    |  --> written
                           |      |  filter: tag == "1" |
                           |      +---------------------+
                           |
                           |      +---------------------+
                           |      | file sink           |
                           +----> |  file: "2_0.txt"    |  --> discarded
                                  |  filter: tag == "2" |
                                  +---------------------+
    

    这篇关于Boost.log:当使用add_file_log()函数时,如何防止输出将被复制到所有添加的流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆