2016-12-22 30 views
0

我正在使用Boost.Log将各种数据集记录到不同的文件。我想为某些文件启用auto_flush功能,但对其他文件禁用(用于记录原始十六进制数据)。我无法得到这个工作。所以我简化了问题到只有一个文件,但它仍然看起来好像auto_flush仍然没有被禁用。这里是我的代码:如何禁用自动刷新升压日志

test.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

#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

#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; 
} 

写入category1.log输出显示了一个换行符被应用之间两个电话BOOST_LOG_TRIVIAL,即使我明确地设置auto_flush fal SE:

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

我也用BOOST_LOG_CHANNEL_SEV记录到多个文件,但设置auto_flush为false似乎仍然没有任何效果...

  1. 你如何禁用auto_flush,并有连续的日志报表打印到日志文件中的同一行?
  2. 解决方案是否可以缩放到多个日志文件,以便您可以为某些文件启用auto_flush,但不能为其他文件启用auto_flush

UPDATE:

随着安德烈的回答,我能够创建一个自定义水槽后端,允许用户通过以去除换行符可选的“NoNewline”值某些日志( \ n)的。这是通过add_value Boost.Log关键字实现的。以下是使用severity_channel_logger通过其严重性和通道过滤日志的一个解决方案示例,其中特定通道的日志将发送到特定文件。我从其他例子中拉出来形成这个解决方案,这些在代码中被引用。

的main.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; 
} 

回答

1

你怎么禁用auto_flush,并有连续的日志报表打印到日志文件中的同一行?

首先,auto_flush与每个日志记录后的换行符没有关系。在写入每条日志记录后,它会使接收器清除其缓冲区,而不管它是否包含换行符。其次,auto_flush只能在每个接收器的基础上启用或禁用。在text_ostream_backend的特殊情况下,它意味着连接到接收器的所有数据流都将被刷新,或者没有数据。第三,后面的换行符由内部后端输出,并且当前不能禁用此行为。

如果您只想在选择日志记录后刷新日志文件,则最佳方法是使用属性指示何时需要刷新。然后,您将不得不创建您自己的接收器后端,该接收器将在日志记录中检查该属性,并记录其处理并采取适当的行动。创建接收器描述为here

解决方案是否可以缩放到多个日志文件,以便可以为某些文件启用auto_flush,而不是其他文件?

是的,当然。您必须为每个文件创建一个接收器,并在这些接收器中相应地设置auto_flush

+0

谢谢你清理我的困惑,安德烈。这非常有帮助! – squareskittles