简体   繁体   English

如何禁用 Boost Log 的自动刷新

[英]How do you disable auto-flush for Boost Log

I am using Boost.Log to log various sets of data to different files.我正在使用Boost.Log将各种数据集记录到不同的文件中。 I would like to have the auto_flush feature enabled for some files, but disabled for others (so that a newline is not inserted on each consecutive log statement).我想为某些文件启用auto_flush功能,但为其他文件禁用(这样不会在每个连续的日志语句中插入换行符)。 I couldn't get this to work in my larger project.我无法在更大的项目中使用它。 So I simplified the problem down to just one file, but it appears that auto_flush is still cannot be disabled.所以我将问题简化为一个文件,但似乎仍然无法禁用auto_flush Here is code for a minimal example:这是一个最小示例的代码:

test.hpp :测试.hpp

#include <fstream>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/trivial.hpp>

void init();

test.cpp :测试.cpp

#include "test.hpp"

void init() {
    // Initialize sink.
    typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend> text_sink;
    // Grab the Boost Log core.
    auto coreHandle = boost::log::core::get();
    // Add stream 1.
    boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>();
    sink->locked_backend()->add_stream(
            boost::make_shared<std::ofstream>("category1.log"));
    sink->locked_backend()->auto_flush(false);
    coreHandle->add_sink(sink);
}

main.cpp :主.cpp :

#include <iostream>
#include <string>
#include "test.hpp"

// Main entry point to program.
int main(int numArg, char const * const arguments[]) {

    double number1 = 42;

    init();

    BOOST_LOG_TRIVIAL(info) << "This message should go to category 1 log..." << number1;
    BOOST_LOG_TRIVIAL(info) << "This message should also go to category 1 log on the same line..." << number1;

    return EXIT_SUCCESS;
}

The output written to category1.log shows that a newline was applied between the two calls to BOOST_LOG_TRIVIAL , even though I explicitly set auto_flush to false in the init() function:写入category1.log的输出显示在对BOOST_LOG_TRIVIAL的两次调用之间应用了换行符,即使我在init()函数中将auto_flush显式设置为 false:

This message should go to category 1 log...42
This message should also go to category 1 log on the same line...42

I also use BOOST_LOG_CHANNEL_SEV to log to multiple files, but setting auto_flush to false still appears to have no effect...我也使用BOOST_LOG_CHANNEL_SEV登录到多个文件,但将auto_flush设置为 false 似乎仍然没有效果......

  1. How do you disable auto_flush , and have consecutive log statements print to the same line in the log file?如何禁用auto_flush并将连续的日志语句打印到日志文件中的同一行?
  2. Can a solution be scaled to multiple log files, such that you can enable auto_flush for some files, but not others?一个解决方案是否可以扩展到多个日志文件,以便您可以为某些文件启用auto_flush ,而不能为其他文件启用auto_flush

How do you disable auto_flush , and have consecutive log statements print to the same line in the log file?如何禁用auto_flush并将连续的日志语句打印到日志文件中的同一行?

First, auto_flush has no relation to the trailing newline after each log record.首先, auto_flush与每个日志记录后的尾随换行符auto_flush It makes the sink flush its buffers after each log record is written, whether or not it contains a newline.它使接收器在写入每个日志记录后刷新其缓冲区,无论它是否包含换行符。 Second, auto_flush can only be enabled or disabled on per-sink basis.其次, auto_flush只能在每个接收器的基础上启用或禁用。 In the particular case of text_ostream_backend it means either all streams attached to the sink will be flushed, or none of them.text_ostream_backend的特殊情况下,这意味着连接到接收器的所有流都将被刷新,或者都不刷新。 Third, the trailing newline is output by the sink backend internally, and this behavior cannot be disabled currently.第三,尾随换行符是由接收器后端内部输出的,目前无法禁用此行为。

If you want to flush the log file only after select log records, your best approach would be to use an attribute to indicate when flushing is needed.如果您只想在选择日志记录后刷新日志文件,最好的方法是使用属性来指示何时需要刷新。 Then you would have to create your own sink backend that would examine that attribute in the log records it processes and act appropriately.然后,您必须创建自己的接收器后端,该后端将检查它处理的日志记录中的该属性并采取适当的行动。 Creating sinks is described here . 此处描述创建接收器。

Can a solution be scaled to multiple log files, such that you can enable auto_flush for some files, but not others?一个解决方案是否可以扩展到多个日志文件,以便您可以为某些文件启用auto_flush ,而不能为其他文件启用auto_flush

Yes, of course.是的当然。 You have to create a sink per each file and set up auto_flush accordingly in those sinks.您必须为每个文件创建一个接收器,并在这些接收auto_flush相应地设置auto_flush

Update 2019-10-17: 2019-10-17 更新:

There has been development regarding the trailing newline insertion.关于尾随换行插入已经有了发展。 In Boost 1.71 , in text-based sinks there appeared an option to disable automatic trailing newline insertion.Boost 1.71 中,在基于文本的接收器中出现了禁用自动尾随换行插入的选项。 See auto_newline_mode enum and set_auto_newline_mode methods in the sink backends.请参阅auto_newline_mode器后端中的auto_newline_mode枚举和set_auto_newline_mode方法。

UPDATE October 2019: 2019 年 10 月更新:

New Answer for Boost 1.71 Boost 1.71 的新答案

Boost.Log now provides a set_auto_newline_mode capability to control how new-lines are handled for each sink backend. Boost.Log 现在提供了set_auto_newline_mode功能来控制如何为每个接收器后端处理换行符。 This removes the need for a custom backend (as implemented in my old answer).这消除了对自定义后端的需要(如我的旧答案中所实施)。 Now, we can easily create a logging backend with automatic new lines disabled .现在,我们可以轻松创建禁用自动换行的日志记录后端。 The auto_newline_mode options are: auto_newline_mode选项是:

  • auto_newline_mode::disabled_auto_newline
  • auto_newline_mode::always_insert
  • auto_newline_mode::insert_if_missing

Here is a much simpler version of my multi-file Boost.Log example which tests this.这是我的 多文件 Boost.Log 示例的一个更简单的版本,它对此进行了测试。 The Category1 log features the normal auto newline. Category1日志具有正常的自动换行符。 The Category2 log doesn't print because its severity level is insufficient. Category2日志不打印,因为其严重性级别不足。 Finally, the Category3 log has auto-newline disabled, so consecutive logs are placed on the same line.最后, Category3日志禁用了自动换行,因此连续的日志放在同一行上。

#include <iostream>
#include <string>
#include <fstream>

#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions/keyword.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/keywords/severity.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

#include <boost/filesystem.hpp>

// Includes from the example: http://www.boost.org/doc/libs/1_62_0/libs/log/example/doc/extension_stat_collector.cpp
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/phoenix.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/attributes/value_visitation.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>

// Includes from example: https://www.ociweb.com/resources/publications/sett/may-2016-boostlog-library/
#include <boost/log/sources/global_logger_storage.hpp>

namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;

BOOST_LOG_GLOBAL_LOGGER(Channel1Logger, src::severity_channel_logger<logging::trivial::severity_level>);
BOOST_LOG_GLOBAL_LOGGER(Channel2Logger, src::severity_channel_logger<logging::trivial::severity_level>);
BOOST_LOG_GLOBAL_LOGGER(Channel3Logger, src::severity_channel_logger<logging::trivial::severity_level>);

BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel1Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category1"));
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel2Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category2"));
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel3Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category3"));

#define LOG_CATEGORY1(LEVEL) BOOST_LOG_SEV(Channel1Logger::get(), logging::trivial::LEVEL)
#define LOG_CATEGORY2(LEVEL) BOOST_LOG_SEV(Channel2Logger::get(), logging::trivial::LEVEL)
#define LOG_CATEGORY3(LEVEL) BOOST_LOG_SEV(Channel3Logger::get(), logging::trivial::LEVEL)

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)


/** Initialize the Boost.Log logging. */
void init() {
    // Alias the backend and sink types.
    typedef boost::log::sinks::text_ostream_backend MyBackendType;
    typedef boost::log::sinks::synchronous_sink< MyBackendType > MyBackendSinkType;
    // Grab the Boost Log core.
    auto coreHandle = boost::log::core::get();
    // Define the path where the log files should reside.
    boost::filesystem::path destinationDir("D:\\test");

    // Create a minimal severity table filter
    typedef expr::channel_severity_filter_actor< std::string, logging::trivial::severity_level > min_severity_filter;
    min_severity_filter minSeverity = expr::channel_severity_filter(channel, severity);

    // Set up the minimum severity levels for different channels.
    minSeverity["Category1"] = logging::trivial::info;
    minSeverity["Category2"] = logging::trivial::info;
    minSeverity["Category3"] = logging::trivial::info;

    // Define log file 1.
    boost::filesystem::path logFile1(destinationDir / "category1.log");
    // Create a log backend, and add a stream to it.
    boost::shared_ptr< MyBackendType > customBackend(new MyBackendType());
    customBackend->add_stream(boost::shared_ptr< std::ostream >(
            new std::ofstream(logFile1.string().c_str())));
    // Enable auto flush for this backend.
    customBackend->auto_flush(true);
    // Create a sink with the custom backend.
    boost::shared_ptr< MyBackendSinkType > customSink(new MyBackendSinkType(customBackend));
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category1") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);

    // Define log file 2.
    boost::filesystem::path logFile2(destinationDir / "category2.log");
    // Create a log backend, and add a stream to it.
    customBackend = boost::make_shared< MyBackendType >();
    customBackend->add_stream(boost::shared_ptr< std::ostream >(
            new std::ofstream(logFile2.string().c_str())));
    // Enable auto flush for this backend.
    customBackend->auto_flush(true);
    // Create a sink with the custom backend.
    customSink = boost::make_shared< MyBackendSinkType >(customBackend);
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category2") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);

    // Define log file 3.
    boost::filesystem::path logFile3(destinationDir / "category3.log");
    // Create a log backend, and add a stream to it.
    customBackend = boost::make_shared< MyBackendType >();
    customBackend->add_stream(boost::shared_ptr< std::ostream >(
            new std::ofstream(logFile3.string().c_str())));
    // Enable auto flush for this backend.
    customBackend->auto_flush(true);
    // Disable the auto newline for this backend.
    customBackend->set_auto_newline_mode(boost::log::sinks::auto_newline_mode::disabled_auto_newline);
    // Create a sink with the custom backend.
    customSink = boost::make_shared< MyBackendSinkType >(customBackend);
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category3") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);
}

// This is the main entry point to the program.
int main (int numArgs, char const * const argList) {
    double number1 = 42;

    // Initialize the Boost.Log logging.
    init();

    // The Category1 logger has normal auto-newline. 
    LOG_CATEGORY1(info) << "New Category1 log1.";
    LOG_CATEGORY1(info) << "New Category1 log2.";
    // Category2 logger won't print to file b/c doesn't meet severity requirements.
    LOG_CATEGORY2(trace) << "New Category2 log.";

    // Category3 logger has auto-newline disabled. 
    LOG_CATEGORY3(info) << "[Put this on line 1]";
    LOG_CATEGORY3(info) << "[Put this on line 1 also]" << std::endl;
    LOG_CATEGORY3(info) << "[Put this on line 2]";

    std::cout << "Successful Completion!" << std::endl;
    return EXIT_SUCCESS;
}

Old Answer for Boost versions <1.71 Boost 版本 <1.71 的旧答案

With @Andrey's answer, I was able to create a custom sink backend that allows the user to pass an optional "NoNewline" value to certain logs in order to remove the newline character ( \\n ).通过@Andrey 的回答,我能够创建一个自定义接收器后端,允许用户将可选的“NoNewline”值传递给某些日志,以删除换行符 ( \\n )。 This is achieved via the add_value Boost.Log keyword.这是通过add_value Boost.Log 关键字实现的。 Here's an example of one solution using severity_channel_logger to filter logs via their severity and channel, where logs of a specific channel will be sent to a specific file.下面是使用severity_channel_logger通过其严重性和通道过滤日志的一种解决方案示例,其中特定通道的日志将发送到特定文件。 I pulled from other examples to form this solution, and these are cited in the code.我从其他示例中提取以形成此解决方案,并在代码中引用了这些。

main.cpp :主.cpp :

#include <iostream>
#include <string>
#include <fstream>

#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions/keyword.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/keywords/severity.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>

#include <boost/filesystem.hpp>

// Includes from the example: http://www.boost.org/doc/libs/1_62_0/libs/log/example/doc/extension_stat_collector.cpp
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/phoenix.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/attributes/value_visitation.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>

// Includes from example: https://www.ociweb.com/resources/publications/sett/may-2016-boostlog-library/
#include <boost/log/sources/global_logger_storage.hpp>

namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;

BOOST_LOG_GLOBAL_LOGGER(Channel1Logger, src::severity_channel_logger<logging::trivial::severity_level>);
BOOST_LOG_GLOBAL_LOGGER(Channel2Logger, src::severity_channel_logger<logging::trivial::severity_level>);
BOOST_LOG_GLOBAL_LOGGER(Channel3Logger, src::severity_channel_logger<logging::trivial::severity_level>);

BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel1Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category1"));
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel2Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category2"));
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS(Channel3Logger, src::severity_channel_logger<logging::trivial::severity_level>, (keywords::channel = "Category3"));

#define LOG_CATEGORY1(LEVEL) BOOST_LOG_SEV(Channel1Logger::get(), logging::trivial::LEVEL)
#define LOG_CATEGORY2(LEVEL) BOOST_LOG_SEV(Channel2Logger::get(), logging::trivial::LEVEL)
#define LOG_CATEGORY3(LEVEL) BOOST_LOG_SEV(Channel3Logger::get(), logging::trivial::LEVEL)

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", logging::trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)


// The backend collects records being forwarded from the front-end loggers.
class MyCustomBackend : public sinks::basic_sink_backend< 
        sinks::combine_requirements<
            sinks::synchronized_feeding,            
            sinks::flushing                         
        >::type>
{
private:
    // The file to write the collected information to.
    std::ofstream logFile;

public:
    // The constructor initializes the internal data
    explicit MyCustomBackend(const char * file_name);

    // The function consumes the log records that come from the frontend
    void consume(logging::record_view const& rec);
    // The function flushes the file
    void flush();

};

// The constructor initializes the internal data
MyCustomBackend::MyCustomBackend(const char * file_name) :
        logFile(file_name) {
    if (!logFile.is_open()) {
        throw std::runtime_error("Could not open the specified file!");
    }
}

// This function consumes the log records that come from the frontend.
void MyCustomBackend::consume(logging::record_view const & rec) {
    // If the NoNewline attribute is present, skip the newline.
    if (rec.attribute_values().count("NoNewline")) {
        logFile << *rec[boost::log::expressions::smessage];
    } else {
        logFile << *rec[boost::log::expressions::smessage] << std::endl;
    }
    // This is the equivalent of setting auto_flush.
    this->flush();
}

/** The function flushes the file. */
void MyCustomBackend::flush() {
    logFile.flush();
}

/** Initialize the Boost.Log logging. */
void init() {
    // Alias the custom sink types.
    typedef boost::log::sinks::synchronous_sink<MyCustomBackend> CustomBackendType;
    // Grab the Boost Log core.
    auto coreHandle = boost::log::core::get();
    // Define the path where the log files should reside.
    boost::filesystem::path destinationDir("C:\\test");

    // Create a minimal severity table filter
    typedef expr::channel_severity_filter_actor< std::string, logging::trivial::severity_level > min_severity_filter;
    min_severity_filter minSeverity = expr::channel_severity_filter(channel, severity);

    // Set up the minimum severity levels for different channels.
    minSeverity["Category1"] = logging::trivial::info;
    minSeverity["Category2"] = logging::trivial::info;
    minSeverity["Category3"] = logging::trivial::info;

    // Define log file 1.
    boost::filesystem::path logFile1(destinationDir / "category1.log");
    // Create a custom backend.
    boost::shared_ptr<MyCustomBackend> customBackend(new MyCustomBackend(logFile1.string().c_str()));
    // Create a sink with the custom backend.
    boost::shared_ptr<CustomBackendType> customSink(new CustomBackendType(customBackend));
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category1") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);
    
    // Define log file 2.
    boost::filesystem::path logFile2(destinationDir / "category2.log");
    // Create a custom backend.
    customBackend = boost::make_shared<MyCustomBackend>(logFile2.string().c_str());
    // Create a sink with the custom backend.
    customSink = boost::make_shared<CustomBackendType>(customBackend);
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category2") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);

    // Define log file 3.
    boost::filesystem::path logFile3(destinationDir / "category3.log");
    // Create a custom backend.
    customBackend = boost::make_shared<MyCustomBackend>(logFile3.string().c_str());
    // Create a sink with the custom backend.
    customSink = boost::make_shared<CustomBackendType>(customBackend);
    // Add a filter to the sink.
    customSink->set_filter((channel == "Category3") && minSeverity && (severity >= logging::trivial::info));
    // Add the sink to the Boost.Log core.
    coreHandle->add_sink(customSink);
}

int main (int numArgs, char const * const argList) {
    double number1 = 42;

    // Initialize the Boost.Log logging.
    init();

    LOG_CATEGORY1(info) << "New Category1 log.";
    // Won't print to file b/c doesn't meet severity requirements.
    LOG_CATEGORY2(trace) << "New Category2 log.";

    // Remove newline character ('\n') from specific logs. 
    LOG_CATEGORY3(info) << logging::add_value("NoNewline", true) << "[Put this on line 1]";
    LOG_CATEGORY3(info) << "[Put this on line 1 also]";
    LOG_CATEGORY3(info) << "[Put this on line 2]";

    return EXIT_SUCCESS;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM