如何禁用Boost Log

时间:2016-12-22 15:56:44

标签: c++ boost flush boost-log

我正在使用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设置为false:

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,而不启用其他文件?
  3. 更新

    根据Andrey的回答,我能够创建一个自定义接收器后端,允许用户将可选的“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;
    }
    

2 个答案:

答案 0 :(得分:1)

  

如何禁用auto_flush,并将连续的日志语句打印到日志文件中的同一行?

首先,auto_flush与每个日志记录后的尾随换行无关。无论是否包含换行符,它都会在写入每个日志记录后使接收器刷新其缓冲区。其次,auto_flush只能在每个接收器的基础上启用或禁用。在text_ostream_backend的特定情况下,它表示连接到接收器的所有流都将被刷新,或者不会被刷新。第三,后端换行在内部由sink后端输出,目前无法禁用此行为。

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

  

是否可以将解决方案扩展为多个日志文件,以便您可以为某些文件启用auto_flush,而不启用其他文件?

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

答案 1 :(得分:0)

2019年10月更新:

Boost 1.71的新答案

Boost.Log现在提供了set_auto_newline_mode功能,可以控制每个接收器后端如何处理换行符。这消除了对 custom 后端的需求(在我的旧答案中已实现)。现在,我们可以轻松地创建带有 disabled 的自动换行的日志记录后端。 auto_newline_mode选项是:

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

这里是我的multi-file Boost.Log example的一个简单得多的版本,它对此进行了测试。 Category1日志具有正常的自动换行符。 Category2日志的严重性级别不足,因此无法打印。最后,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;
}

Boost版本<1.71 的旧答案

有了@Andrey的答案,我能够创建一个自定义接收器后端,该后端允许用户将可选的“ 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;
}