BOOST_LOG_TRIVIAL vs logrotate(重新打开日志)

BOOST_LOG_TRIVIAL vs logrotate (reopen log)

我有一个程序 运行 作为一项服务长期存在。我使用 BOOST_LOG_TRIVIAL(info) << "Message" 来记录各种事件。我想与 logrotate 合作并在轮换后重新打开我的日志文件。理论上剧本的工作原理如下:

到目前为止我准备的例子(可能不是必需的):

#include <iostream>
#include <stdexcept>

#include <csignal>

#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/support/date_time.hpp>

inline
void setupLogging(std::string const &logFileName)
{
  namespace logging = boost::log;
  namespace src = boost::log::sources;
  namespace expr = boost::log::expressions;
  namespace keywords = boost::log::keywords;
  logging::add_common_attributes();
  auto format = expr::stream
    << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
    << " progname " << logging::trivial::severity
    << ": " << expr::smessage;
  auto fileOutput = logging::add_file_log(
      keywords::file_name = logFileName, keywords::format = format
      , keywords::auto_flush = true, keywords::open_mode = std::ios::app
    );
  auto consoleOutput = logging::add_console_log(
    std::cerr, keywords::format = format, keywords::auto_flush = true
  );
  BOOST_LOG_TRIVIAL(debug) << "CHECKPOINT @ setupLogging() after log initialization.";
}

void my_signal_handler(int signal)
{
  BOOST_LOG_TRIVIAL(info) << "my_signal_handler BEGIN";
  /* REOPEN LOG HERE */
  BOOST_LOG_TRIVIAL(info) << "my_signal_handler END";
}

int main()
{
  setupLogging("logrotate.test.log");
  if(signal(SIGHUP, my_signal_handler) == SIG_ERR)
  {
    BOOST_LOG_TRIVIAL(error) << "Failed to register signal handler";
    return 1;
  } else
    BOOST_LOG_TRIVIAL(info) << "Signal handler registered.";
  BOOST_LOG_TRIVIAL(info) << "CHECKPOINT 0";
  for(size_t i=1; i<100; ++i)
  {
    boost::this_thread::sleep_for( boost::chrono::seconds(1) );
    BOOST_LOG_TRIVIAL(info) << "CHECKPOINT " << i;
  }
  return 0;
}

正在编译:

LINK="-lboost_system -lboost_date_time -lboost_log -lboost_log_setup -lboost_thread -lboost_chrono -lpthread"
g++ -std=c++11 -Wextra -DBOOST_LOG_DYN_LINK -pedantic -O3 logrotate_test.cpp -o logrotate_test $LINK

Adam 答案的源代码表示

namespace detail666777888
{
       using namespace boost;
       using namespace boost::log;
       typedef shared_ptr< sinks::synchronous_sink< sinks::text_file_backend > > T;
}
typedef detail666777888::T SPFileSink;
SPFileSink logFileSink;

void setupLogging(...){
... logFileSink = logging::add_file_log ...
}

void my_sighup_handler(int /*signal*/)
{
  BOOST_LOG_TRIVIAL(info) << "my_sighup_handler START";
  auto oldLFS = logFileSink;
  setupLogging("logrotate.test.log");
  boost::log::core::get()->remove_sink(oldLFS);
  BOOST_LOG_TRIVIAL(info) << "my_sighup_handler FINISH";
}

只需使用 copytruncate logrotate 选项。

来自手册:

Truncate the original log file in place after creating a copy, instead of moving the old log file and optionally creating a new one. It can be used when some program cannot be told to close its logfile and thus might continue writing (appending) to the previous log file forever. Note that there is a very small time slice between copying the file and truncating it, so some logging data might be lost. When this option is used, the create option will have no effect, as the old log file stays in place.

http://linux.die.net/man/8/logrotate

编辑:

试试这个。看看 add_file_log[1] 函数源代码。那么:

  1. 记住你用 add_file_log 添加的对象(它 returns 下沉)
  2. 当您收到信号 remove_sink[2] 并使用 add_file_log[ 添加新的接收器时1] -- (日志条目在其他线程生成时可能会泄漏)

[1] http://www.boost.org/doc/libs/1_55_0/boost/log/utility/setup/file.hpp

[2]http://www.boost.org/doc/libs/1_55_0/boost/log/core/core.hpp

我遇到了同样的问题并找到了一个非常简单的解决方案:只需用一个文件名(没有任何模式)初始化 text_file_backend 并调用 text_file_backend::rotate_file()。这将关闭当前文件并打开一个同名的新文件。

// create sink
auto backend = boost::make_shared< boost::log::sinks::text_file_backend >(
        boost::log::keywords::file_name = "my.log",
        boost::log::keywords::open_mode = std::ios_base::out | std::ios_base::app
);
auto sink = boost::make_shared<sinks::synchronous_sink<boost::log::sinks::text_file_backend>>(backend);
boost::log::core::get()->add_sink(sink);

// ... do some logging

// move log file and...
// reopen log file
sink->locked_backend()->rotate_file();

// ... do more logging

我用 std::ios_base::app 打开日志文件,以防止文件存在时日志文件被截断。

这种方案的优点是不需要复制文件,日志记录不会丢失也不会重复。