【问题标题】:BOOST_LOG_TRIVIAL vs logrotate (reopen log)BOOST_LOG_TRIVIAL vs logrotate(重新打开日志)
【发布时间】:2016-06-21 12:26:16
【问题描述】:

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

  • 我的程序启动,发生了一些日志记录
  • 日志由logrotate 等其他程序或由我手动从something.log 重命名为something.log.1
  • 我的程序继续登录到something.log.1 文件。
  • 我将 SIGHUP(或其他内容)发送到我的程序,以便我可以重新打开日志文件。但我不知道该怎么做。

到目前为止我准备的示例(可能没有必要):

#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

亚当答案的源代码表示

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

【问题讨论】:

    标签: c++ linux boost logrotate


    【解决方案1】:

    我遇到了同样的问题并找到了一个非常简单的解决方案:只需使用单个文件名(没有任何模式)初始化 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 打开日志文件以防止日志文件在文件存在时被截断。

    此方案的优点是无需复制文件,日志记录既不会丢失也不会重复。

    【讨论】:

      【解决方案2】:

      只需使用 copytruncate logrotate 选项。

      来自手册:

      创建副本后在原地截断原始日志文件,而不是 移动旧的日志文件并选择性地创建一个新的。它可以 当某些程序无法被告知关闭其日志文件时使用 可能会永远继续写入(附加)到前一个日志文件。 请注意,复制文件之间的时间片非常小 并截断它,因此可能会丢失一些日志记录数据。当这 使用选项,创建选项将不起作用,因为旧日志 文件保留在原地。

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

      编辑:

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

      1. 记住您使用 add_file_log 添加的对象(它返回接收器)
      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

      【讨论】:

      • 感谢您提供此解决方法。但我希望有一个解决方案。如果其他一切都失败了,这将是赢家。 :-)
      • 先添加新接收器,然后删除旧接收器,确保数据不会丢失。但是这两个文件中都有可能出现数据。这对我来说是可以接受的。 :-)
      猜你喜欢
      • 2012-05-29
      • 2021-02-28
      • 2017-11-02
      • 2017-04-30
      • 1970-01-01
      • 2011-09-06
      • 2019-12-14
      • 2016-09-08
      • 2017-03-29
      相关资源
      最近更新 更多