TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

C++のログ出力ライブラリ

電王トーナメントのときに将棋所のログ出力が同期処理で非常に遅いこと知らずに時間切れ負けを起こしてしまった。
その後、速度優先でログ出力を行わないように修正したが、ログ出力がないと探索の状況がわからず不便である。

そこでログをファイルに出力するようにして、「tail -f」などで監視できるようにしたい。
マルチレッドで同期してログ出力すると速度低下していまうため、速度低下しないようにしたい。
自分で作るのも大変なのでオープンソースのライブラリがあれば、それを利用したい。
以前にGoogleのglogを試したことがあるがビルドが大変だったので、ヘッダーのみのライブラリがよい。
また、WindowsでもLinuxでも使用できるものがよい。

要件を満たすライブラリがないか調べたところ、spdlogというライブラリを見つけた。
spdlog/include/spdlog at master · gabime/spdlog · GitHub

spdlogは、欲しい要件をすべて満たしている。

  • ヘッダーのみで使用できる。
  • 非同期でログ出力が可能。CPUがIO待ちのときに出力するので速度低下を心配しないでログ出力できる。
  • スレッドセーフ
  • Windows(Visual Studio)でもLinux(gcc)でも使用できる。

以下に、spdlogを使って非同期のログ出力を試した内容を記述します。

インストール

GitHubReleasesから最新のソースをダウンロードする。
ソースのincludeディレクトリをプロジェクトのソースディレクトリにコピーする。

使い方

スレッドセーフで非同期出力を行うには以下のように記述する。

ロガー作成
#include "spdlog/spdlog.h"

auto loggersink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
auto logger = std::make_shared<spdlog::async_logger>("mylogger", loggersink, 8192);

spdlog::async_loggerの引数の"mylogger"はロガー名、8192はキューのサイズを指定している。

main関数などで作成した場合、他の関数からロガーを取得するには、

auto logger = spdlog::get("mylogger");

のようにロガー名を指定して取得する。

ロガーをグローバル変数として作成してしまってもよい。

ファイルに出力するには、stdout_sink_mtの代わりに、simple_file_sink_mtを使えばよい。

auto loggersink = std::make_shared<spdlog::sinks::simple_file_sink_mt>("fileName.txt");

標準出力にしておいて実行時にリダイレクトしてもよい。

ログ出力
logger->info("hello");

値を埋め込む場合は、

logger->info("va1={}, val2={}", val1, val2);

のように記述する。

非同期のロガーを使う場合、プログラムの終了時に

spdlog::drop_all();

を呼び出す。

ログレベル

ログレベルには、critical, error, warn, info, debug, traceがある。

出力するログレベルを変更するには、

logger->set_level(spdlog::level::trace);

のように指定する。

リリース版でデバッグログとトレースログをプリプロセッサで条件コンパイルするには、

logger->debug("debug val={}", val);

と書くところを、

SPDLOG_DEBUG(logger, "debug val={}", val);

と記述する。

デバッグ版でデバッグ用ログ出力の条件コンパイルを有効するには、SPDLOG_TRACE_ONとSPDLOG_DEBUG_ONマクロを定義する。

#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
ログ出力フォーマット

デフォルトでは、以下のようにログ出力される。

[2018-02-10 17:22:05.778] [mylogger] [info] hello

フォーマットを変更するには、

logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");

のようにする。

パターンの指定の仕方は、
3. Custom formatting · gabime/spdlog Wiki · GitHub
を参照。

改行コード

Windowsで標準出力にログ出力すると、改行コード"\r"が2個出力される問題があった。
改行コードを変更するには、

#define SPDLOG_EOL "\n"

のようにする。
Windowsでは、ファイル出力時に自動的に"\r"が付くので改行コードは"\n"を指定する。