Search code examples
c++boostboost-log

How to correctly exit from a program, which uses Boost Log?


The sample program below works fine, but the valgrind shows, that 520 bytes in 6 blocks are still reachable after exit.

#include <iostream>
#include <string>

#include <boost/log/common.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/utility/setup/file.hpp>

#include <boost/phoenix/bind/bind_member_function.hpp>
#include <boost/shared_ptr.hpp>

namespace bl = boost::log;
namespace bp = boost::phoenix;

enum class MySeverityLevel
{
  panic,
  alert,
  critical,
  error,
  warning,
  notice,
  info,
  debug
};

BOOST_LOG_ATTRIBUTE_KEYWORD(Severity, "Severity", MySeverityLevel)
BOOST_LOG_ATTRIBUTE_KEYWORD(Channel, "Channel", std::string)

class Logger
{

public:

  Logger(const std::string& CH, const MySeverityLevel SEV):
  m_log(bl::keywords::channel = CH), m_channel(CH), m_severityMax(SEV)
  {
    m_sink = bl::add_file_log
    (
      bl::keywords::file_name = CH + ".log",
      bl::keywords::filter = bp::bind(&Logger::filter, this, Channel.or_none(), Severity.or_none())
    );
  }

  ~Logger()
  {
    this->terminate();
  }

  void terminate()
  {
    if (m_sink)
    {
      bl::core::get()->remove_sink(m_sink);
      m_sink->flush();
      m_sink.reset();
    }
  }

  void panic(const std::string& S)
  {
    BOOST_LOG_SEV(m_log, MySeverityLevel::panic) << S;
  }

  void debug(const std::string& S)
  {
    BOOST_LOG_SEV(m_log, MySeverityLevel::debug) << S;
  }

private:

  using ChannelValue = bl::value_ref<std::string, tag::Channel>;
  using MySeverityLevelValue = bl::value_ref<MySeverityLevel, tag::Severity>;

  bool filter(const ChannelValue& CH, const MySeverityLevelValue& SEV) const
  {
    if (CH && SEV)
    {
      return (CH.get() == m_channel) && (SEV.get() <= m_severityMax);
    }
    else
    {
      return false;
    }
  }

  bl::sources::severity_channel_logger<MySeverityLevel, std::string> m_log;
  boost::shared_ptr<bl::sinks::sink> m_sink;
  const std::string m_channel;
  const MySeverityLevel m_severityMax;

};

int main()
{
  Logger lgA("A", MySeverityLevel::error);
  lgA.panic("A: Panic");
  lgA.debug("A: Debug");
  lgA.terminate();
}

That's the valgrind output:

hekto@ubuntu:~$ valgrind --leak-check=full --show-leak-kinds=all ./tc0013
==48934== Memcheck, a memory error detector
==48934== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==48934== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==48934== Command: ./tc0013
==48934== 
==48934== 
==48934== HEAP SUMMARY:
==48934==     in use at exit: 520 bytes in 6 blocks
==48934==   total heap usage: 83 allocs, 77 frees, 97,276 bytes allocated
==48934== 
==48934== 8 bytes in 1 blocks are still reachable in loss record 1 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x4E98C61: boost::log::v2_mt_posix::sources::aux::get_severity_level() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x11723B: boost::log::v2_mt_posix::sources::aux::severity_level<MySeverityLevel>::set_value(MySeverityLevel) (severity_feature.hpp:135)
==48934==    by 0x115E74: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:252)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== 8 bytes in 1 blocks are still reachable in loss record 2 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x4E9EF69: boost::log::v2_mt_posix::aux::this_thread::get_id() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x4E8BB96: boost::log::v2_mt_posix::core::open_record(boost::log::v2_mt_posix::attribute_set const&) (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x118DEA: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:259)
==48934==    by 0x11825B: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::open_record_with_channel_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&, boost::parameter::void_) (channel_feature.hpp:195)
==48934==    by 0x1172B2: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (channel_feature.hpp:171)
==48934==    by 0x115E8B: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:253)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== 16 bytes in 1 blocks are still reachable in loss record 3 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x512A5BA: boost::detail::add_thread_exit_function(boost::detail::thread_exit_function_base*) (in /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.65.1)
==48934==    by 0x4E98C9A: boost::log::v2_mt_posix::sources::aux::get_severity_level() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x11723B: boost::log::v2_mt_posix::sources::aux::severity_level<MySeverityLevel>::set_value(MySeverityLevel) (severity_feature.hpp:135)
==48934==    by 0x115E74: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:252)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== 24 bytes in 1 blocks are still reachable in loss record 4 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x4E98C84: boost::log::v2_mt_posix::sources::aux::get_severity_level() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x11723B: boost::log::v2_mt_posix::sources::aux::severity_level<MySeverityLevel>::set_value(MySeverityLevel) (severity_feature.hpp:135)
==48934==    by 0x115E74: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:252)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== 24 bytes in 1 blocks are still reachable in loss record 5 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x512A1BC: boost::detail::make_external_thread_data() (in /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.65.1)
==48934==    by 0x512A5AD: boost::detail::add_thread_exit_function(boost::detail::thread_exit_function_base*) (in /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.65.1)
==48934==    by 0x4E98C9A: boost::log::v2_mt_posix::sources::aux::get_severity_level() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x11723B: boost::log::v2_mt_posix::sources::aux::severity_level<MySeverityLevel>::set_value(MySeverityLevel) (severity_feature.hpp:135)
==48934==    by 0x115E74: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:252)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== 440 bytes in 1 blocks are still reachable in loss record 6 of 6
==48934==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==48934==    by 0x512A03D: boost::detail::make_external_thread_data() (in /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.65.1)
==48934==    by 0x512A5AD: boost::detail::add_thread_exit_function(boost::detail::thread_exit_function_base*) (in /usr/lib/x86_64-linux-gnu/libboost_thread.so.1.65.1)
==48934==    by 0x4E98C9A: boost::log::v2_mt_posix::sources::aux::get_severity_level() (in /usr/lib/x86_64-linux-gnu/libboost_log.so.1.65.1)
==48934==    by 0x11723B: boost::log::v2_mt_posix::sources::aux::severity_level<MySeverityLevel>::set_value(MySeverityLevel) (severity_feature.hpp:135)
==48934==    by 0x115E74: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_severity_logger<boost::log::v2_mt_posix::sources::basic_channel_logger<boost::log::v2_mt_posix::sources::basic_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, MySeverityLevel>::open_record_unlocked<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (severity_feature.hpp:252)
==48934==    by 0x114961: boost::log::v2_mt_posix::record boost::log::v2_mt_posix::sources::basic_composite_logger<char, boost::log::v2_mt_posix::sources::severity_channel_logger<MySeverityLevel, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, boost::log::v2_mt_posix::sources::single_thread_model, boost::log::v2_mt_posix::sources::features<boost::log::v2_mt_posix::sources::severity<MySeverityLevel>, boost::log::v2_mt_posix::sources::channel<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::open_record<boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> >(boost::parameter::aux::tagged_argument<boost::log::v2_mt_posix::keywords::tag::severity, MySeverityLevel const> const&) (basic_logger.hpp:575)
==48934==    by 0x1135A5: Logger::panic(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (tc0013.cpp:81)
==48934==    by 0x11097B: main (tc0013.cpp:120)
==48934== 
==48934== LEAK SUMMARY:
==48934==    definitely lost: 0 bytes in 0 blocks
==48934==    indirectly lost: 0 bytes in 0 blocks
==48934==      possibly lost: 0 bytes in 0 blocks
==48934==    still reachable: 520 bytes in 6 blocks
==48934==         suppressed: 0 bytes in 0 blocks
==48934== 
==48934== For counts of detected and suppressed errors, rerun with: -v 
==48934== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

In my real application the Logger class lives in a library, that's why I need to properly terminate it (the termination is performed by the systemctl). However, I still see exceptions in syslog during the exit from my application:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::log::v2_mt_posix::system_error> >'

I think that finding a way to get rid of these "520 bytes in 6 blocks" after exit will help me to terminate my application clearly.

How to write the terminate function (see above) correctly - in order not to leave anything in Boost Log internals reachable?

  • OS: Ubuntu 18.04.1 LTS
  • Compiler: g++ 7.3.0
  • Boost: 1.65.1
  • Valgrind: 3.13.0

Solution

  • Inspired by the @user7860670 answer, I'm (after many years!) answering my own question. The test below doesn't leak memory after the exit from it - the idea is to do all the logging in a separate consumer thread, and to send logging messages to this thread via a blocking queue.

    #include <iostream>
    #include <string>
    
    #include <condition_variable>
    #include <mutex>
    #include <queue>
    #include <thread>
    
    #include <boost/log/common.hpp>
    #include <boost/log/sources/logger.hpp>
    #include <boost/log/utility/setup/file.hpp>
    
    #include <boost/phoenix/bind/bind_member_function.hpp>
    #include <boost/shared_ptr.hpp>
    
    namespace bl = boost::log;
    namespace bp = boost::phoenix;
    
    enum class MySeverityLevel
    {
      panic,
      alert,
      critical,
      error,
      warning,
      notice,
      info,
      debug
    };
    
    BOOST_LOG_ATTRIBUTE_KEYWORD(Severity, "Severity", MySeverityLevel)
    BOOST_LOG_ATTRIBUTE_KEYWORD(Channel, "Channel", std::string)
    
    class Logger
    {
    
    public:
    
      Logger(std::string const& CH, MySeverityLevel const SEV):
      m_channel(CH), m_severityMax(SEV), m_thr(&Logger::consumer, this)
      {
      }
    
      ~Logger()
      {
        this->terminate();
      }
    
      void terminate()
      {
        m_thr.request_stop();
      }
    
      void panic(std::string const& S)
      {
        this->push({S, MySeverityLevel::panic});
      }
    
      void debug(std::string const& S)
      {
        this->push({S, MySeverityLevel::debug});
      }
    
    private:
    
      using LogMessage = struct {std::string txt; MySeverityLevel level;};
    
      void push(LogMessage const& MSG)
      {
        std::lock_guard lock(m_mtx);
        m_que.push(MSG);
        m_cnd.notify_one();
      }
    
      bool pop(LogMessage& MSG)
      {
        std::unique_lock lock(m_mtx);
        if (m_cnd.wait(lock, m_thr.get_stop_token(), [this](){return not m_que.empty();}))
        {
          MSG = m_que.front();
          m_que.pop();
          return true;
        }
        else
        {
          return false;
        }
      }
    
      using ChannelValue = bl::value_ref<std::string, tag::Channel>;
      using MySeverityLevelValue = bl::value_ref<MySeverityLevel, tag::Severity>;
    
      bool filter(ChannelValue const& CH, MySeverityLevelValue const& SEV) const
      {
        if (CH && SEV)
        {
          return (CH.get() == m_channel) && (SEV.get() <= m_severityMax);
        }
        else
        {
          return false;
        }
      }
    
      void consumer()
      {
        // ------ initialization
        bl::sources::severity_channel_logger<MySeverityLevel, std::string> log(bl::keywords::channel = m_channel);
        auto const snk = bl::add_file_log
        (
          bl::keywords::file_name = m_channel + ".log",
          bl::keywords::filter = bp::bind(&Logger::filter, this, Channel.or_none(), Severity.or_none())
        );
        // ------ main loop
        while (true)
        {
          LogMessage msg;
          if (this->pop(msg))
          {
            BOOST_LOG_SEV(log, msg.level) << msg.txt;
          }
          else
          {
            break;
          }
        }
        // ------ termination
        bl::core::get()->remove_sink(snk);
        snk->flush();
      }
    
      std::string const m_channel;
      MySeverityLevel const m_severityMax;
    
      std::queue<LogMessage> m_que;
      std::condition_variable_any m_cnd;
      std::mutex m_mtx;
      std::jthread m_thr;
    
    };
    
    int main()
    {
      Logger lgA("A", MySeverityLevel::error);
      lgA.panic("A: Panic");
      lgA.debug("A: Debug");
      lgA.terminate();
    }
    

    It looks like there is a perfect coexistence between Boost threads (from the boost::log) and standard C++ jthread, which I've chosen because of its ability to be smoothly stopped.