spdlogは、高速で使いやすいC++用のログライブラリです。多機能かつ柔軟性があり、さまざまなアプリケーションで利用されています。この記事ではspdlogの概要と使い方について紹介します。
本記事で使用している環境は以下です。
・Ubuntu 24.04 LTS
・C++17
spdlogとは
spdlogは、2014年にGitHub上で公開され、シンプルで使いやすく、かつ非常に高速なロギングライブラリとして設計されています。現在に至るまでに非同期ロギングの実装、カスタムフォーマッタのサポートや複数のシンク(出力先)を組み合わせる機能の実装、動的設定変更など様々なアップデートがなされています。
spdlogは、シンプルでありながら高機能なロギングライブラリとして、C++開発者の間で広く支持されており、知っておいて損はない注目のロギングライブラリです。
Github:gabime/spdlog
spdlogの特徴
- 高速性能: 内部でfmtライブラリを使用しており、高速なログフォーマットと出力が可能。
- シンプルなAPI: 簡単なAPIで、少ないコード量で多機能なロギング。
- マルチシンク: 複数のログシンク(コンソール、ファイルなど)を同時に使用可能。
- フォーマットのカスタマイズ: ログメッセージのフォーマットを柔軟にカスタマイズ可能。
- スレッドセーフ: スレッドセーフな設計であり、マルチスレッド環境でも安全に使用可能。
- 非同期ロギング: 非同期ロギングをサポートし、パフォーマンスの向上が可能。
spdlogの使い方
さっそく、コードを書きながらspdlogを使ってみましょう。
インストール
spdlogのGitHubページに従ってインストールします。
git clone https://github.com/gabime/spdlog.git
cd spdlog && mkdir build && cd build
cmake .. && make -j
sudo make install
適当なプロジェクトを作成し、CMakeLists.txtに以下を記述します。
cmake_minimum_required(VERSION 3.11)
# プロジェクト名とバージョン
project(example_spdlog VERSION 1.0)
# 実行ファイルを生成する
add_executable(example_spdlog example_spdlog.cpp)
# spdlogライブラリをリンク
if(NOT TARGET spdlog)
find_package(spdlog REQUIRED)
endif()
target_link_libraries(example_spdlog PRIVATE spdlog::spdlog_header_only)
CMakeの導入や使い方については以下の記事で紹介しているので参考にしてみてください。
今回は以下のようなプロジェクトを作成しました。
example_spdlog/
├── build/
├── CMakeLists.txt
└── example_spdlog.cpp
サンプルプログラムを動かしてみる
とりあえず、spdlogのサンプルログ出力を行ってみます。example_spdlog.cppを作成し、以下のコードを書いてください。各ログレベルでのログ出力をした後にログレベルを変更したり、ログフォーマットを変更したりしていますね。
#include "spdlog/spdlog.h"
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// Note that this does not change the current log level, it will only
// remove (depending on SPDLOG_ACTIVE_LEVEL) the call on the release code.
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
ビルドして実行してみましょう。
cd build
cmake ..
make
./example_spdlog
以下のようにログが出力されました。
[2024-05-23 21:37:05.950] [info] Welcome to spdlog!
[2024-05-23 21:37:05.950] [error] Some error message with arg: 1
[2024-05-23 21:37:05.950] [warning] Easy padding in numbers like 00000012
[2024-05-23 21:37:05.950] [critical] Support for int: 42; hex: 2a; oct: 52; bin: 101010
[2024-05-23 21:37:05.950] [info] Support for floats 1.23
[2024-05-23 21:37:05.950] [info] Positional args are supported too..
[2024-05-23 21:37:05.950] [info] left aligned
[2024-05-23 21:37:05.950] [debug] This message should be displayed..
コードの中のspdlog::set_level(spdlog::level::debug);
でログレベルをdebug
にしているので、次の行のdebugログが出力されていますね。ログレベルをinfo
以上にするとdebug
以下は出力されなくなります。
また、最後に書いてある以下の2行についてですが、これはマクロとして提供されているログ出力APIです。マクロのため、コンパイル時にログレベルをチェックされます。
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
使いどころとしては、不要なログをコンパイル時に無効化し、パフォーマンスを最適化できるため、Releaseモードでは使用することをお勧めします。
stdout/stderr ロガーの作成
次は標準出力(stdout)と標準エラー出力(stderr)出力に対するロガーを作成し、これらのロガーを使用してログ出力をする例です。以下のコードを書いて実行してみます。このコードはカラー表示が可能なマルチスレッドロガーを作成し、標準出力と標準エラー出力にログメッセージを出力します。
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
// create a color multi-threaded logger
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
spdlog::get("console")->info("stdout");
spdlog::get("stderr")->error("stderr");
}
int main()
{
stdout_example();
}
以下のようにログが出力されました。
[2024-05-23 23:08:10.973] [console] [info] stdout
[2024-05-23 23:08:10.975] [stderr] [error] stderr
シンプルにログ出力をするだけであればロガーを使わなくても良いですが、複雑なログ処理や特定の設定が必要な場合はロガーを作成すると良いと思います。
ファイル出力
次はログのファイル出力をしてみましょう。以下のコードを書いて実行してみます。このコードでは、basic_logfile_example()
関数でbasic_logger
という名前のロガーを作成します。このロガーはlogs/basic-log.txt
にログ出力を行います。このロガーを使ってhello
という文字列を出力してみます。
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include <iostream>
void basic_logfile_example()
{
try
{
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
}
spdlog::get("basic_logger")->info("hello");
}
int main()
{
basic_logfile_example();
}
以下のようにlogs/basic-log.txt
にログが出力されました。
[2024-05-23 23:19:27.835] [basic_logger] [info] hello
ファイルローテート
ファイル出力の応用です。ファイルサイズと最大ファイル数を設定し、一つのファイルに記録しきれないログは次のファイルに出力されます。最後のファイルもいっぱいになれば古いログから捨てられていきます。
以下のコードを書いて実行してみましょう。1024byteのログファイルを最大4つ作成する例です。
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include <iostream>
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5 MB size max and 3 rotated files
auto max_size = 1024 * 5;
auto max_files = 3;
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
for (size_t i = 0; i < 1000; i++)
{
spdlog::get("some_logger_name")->info("hello {}", i);
}
}
int main()
{
rotating_example();
}
ファイルローテートしていることが確認できます。
非同期ロギング
この例では非同期ロガーを作成します。非同期ロガーは、ログメッセージの処理をバックグラウンドスレッドで行うため、アプリケーションのパフォーマンスを向上させることができます。
以下のコードを作成して実行してみましょう。
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
void async_example()
{
spdlog::init_thread_pool(8192, 1); // キューのサイズを8192、バックグラウンドスレッドを1つに設定
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
async_file->info("This is an async log message.");
async_file->error("This is an async error message.");
}
int main()
{
async_example();
return 0;
}
spdlog::init_thread_pool(8192, 1);
この部分でログ出力用のスレッドプールの設定を変更しています。今回はキューサイズ8192byte、スレッド数1に設定しました。
最後に
今回はC++で高速かつシンプルなロギングライブラリのspdlogを紹介しました。今回は基本的な使い方のみを紹介しましたが、別の記事ではより応用的な使い方を紹介したいと思います。