MENU

【spdlog】高速で簡単なログ出力!C++の最新ログライブラリを紹介

spdlogは、高速で使いやすいC++用のログライブラリです。多機能かつ柔軟性があり、さまざまなアプリケーションで利用されています。この記事ではspdlogの概要と使い方について紹介します。

本記事で使用している環境は以下です。
Ubuntu 24.04 LTS
C++17

目次

spdlogとは

spdlogは、2014年にGitHub上で公開され、シンプルで使いやすく、かつ非常に高速なロギングライブラリとして設計されています。現在に至るまでに非同期ロギングの実装、カスタムフォーマッタのサポートや複数のシンク(出力先)を組み合わせる機能の実装、動的設定変更など様々なアップデートがなされています。

spdlogは、シンプルでありながら高機能なロギングライブラリとして、C++開発者の間で広く支持されており、知っておいて損はない注目のロギングライブラリです。

Github:gabime/spdlog

spdlogの特徴

  1. 高速性能: 内部でfmtライブラリを使用しており、高速なログフォーマットと出力が可能。
  2. シンプルなAPI: 簡単なAPIで、少ないコード量で多機能なロギング。
  3. マルチシンク: 複数のログシンク(コンソール、ファイルなど)を同時に使用可能。
  4. フォーマットのカスタマイズ: ログメッセージのフォーマットを柔軟にカスタマイズ可能。
  5. スレッドセーフ: スレッドセーフな設計であり、マルチスレッド環境でも安全に使用可能。
  6. 非同期ロギング: 非同期ロギングをサポートし、パフォーマンスの向上が可能。

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を紹介しました。今回は基本的な使い方のみを紹介しましたが、別の記事ではより応用的な使い方を紹介したいと思います。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

20代の組み込みソフトウェアエンジニア
主な使用言語はC++

---------------------資格---------------------
応用情報技術者
ネットワークスペシャリスト

目次