読者です 読者をやめる 読者になる 読者になる

kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

Boost.Logの出力をVisual Studioの出力に表示する

Win APIのOutputDebugStringを使用することで、Visual Studioの出力ウィンドウにログを表示することができます。Boost.Logでは同様の機能を持った、専用のbackendが標準で用意されているので、それを利用することで、Boost.Logの出力をVS上に表示することができます。

Boost.Logは基本として、

  1. backendを準備
  2. backendからsinkを準備
  3. sinkをloggerに設定

という流れをとりますが、今回は、出力用のbackendを用意することがポイントです。

debug_output_backend

boost::log::sinks::debug_output_backendがお目当てのbackendとなるので、これのインスタンスを生成して設定していきます。BOOST_LOG_TRIVIALをいきなり使った際はある程度フォーマットされた出力となりますが、一からbackendを作った場合は一切フォーマットされないので、その設定も行います。

shared_ptr<sinks::debug_output_backend> backend =
  make_shared<sinks::debug_output_backend>();
    
typedef sinks::synchronous_sink<sinks::debug_output_backend> sink_t;
shared_ptr<sink_t> sink(new sink_t(backend));

sink->set_formatter(
  expr::format("%1%\t%2%\t%3%\n")
  % expr::format_date_time<posix_time::ptime>(
    "TimeStamp", "%H:%M:%S")
  % logging::trivial::severity
  % expr::message
);

logging::core::get()->add_sink(sink);

logging::add_common_attributes();

loggerは複数用意できますが、今のとこグローバルに1つあればよいと思っているので、BOOST_LOG_TRIVIALで使用される、loggin::core::get()を用います。

TimeStampやseverityの出力は標準で用意されているattributeで、簡単に使用できますが、logging::add_common_attributesを明示的に呼び出す必要があります。初めは見落としていて、必要な出力を得られれず...といったことが何度かありました。

ソース

前回の分と組み合わせると、次のようになります。この状態ではVS上には表示されますが、コンソール上には表示されなくなります。これは機能的に不可能というわけではなく、デフォルトで用意されているsinkと自前でadd_sinkしたものは取り扱いが別ということみたいです。自前でbackend(とsink)を用意して追加することで、コンソール上にも表示されるようになります。

attributeの件といい、BOOST_LOG_TRIVIALはすごく手軽に開始できるのですが、まともに使い出すときは一から全部設定するといったくらいの気持ちでちょうどよさげですね。

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include <boost/log/expressions.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/sinks/debug_output_backend.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/trivial.hpp>

int main() {
  using namespace boost;
  namespace logging = boost::log;
  namespace expr = boost::log::expressions;
  namespace sinks = boost::log::sinks;

  shared_ptr<sinks::debug_output_backend> backend =
    make_shared<sinks::debug_output_backend>();
    
  typedef sinks::synchronous_sink<sinks::debug_output_backend> sink_t;
  shared_ptr<sink_t> sink(new sink_t(backend));

  sink->set_formatter(
    expr::format("%1%\t%2%\t%3%\n")
    % expr::format_date_time<posix_time::ptime>(
      "TimeStamp", "%H:%M:%S")
    % logging::trivial::severity
    % expr::message
  );

  logging::core::get()->add_sink(sink);

  logging::add_common_attributes();

#ifdef _DEBUG
  // do nothing
#else
  logging::core::get()->set_filter(
    logging::trivial::severity >= logging::trivial::info
    );
#endif

  BOOST_LOG_TRIVIAL(trace) << "trace message";
  BOOST_LOG_TRIVIAL(debug) << "debug";
  BOOST_LOG_TRIVIAL(info) << "info message";
  BOOST_LOG_TRIVIAL(warning) << "warning message";
  BOOST_LOG_TRIVIAL(error) << "error message";
  BOOST_LOG_TRIVIAL(fatal) << "fatal message";
}