How to Integrate spdlog Using Header-Only Mode

To integrate spdlog using header-only mode, define the SPDLOG_HEADER_ONLY macro before including any spdlog headers, which causes the implementation files (the *-inl.h files) to be compiled directly into your translation units.

The gabime/spdlog repository supports a header-only configuration that eliminates the need to link against a precompiled library. This approach embeds all logging implementation code into your project's object files, simplifying build configuration and dependency management.

How Header-Only Mode Works

spdlog's architecture separates interface declarations from implementations using a dual-header pattern. Public headers such as spdlog.h and logger.h contain only declarations and type definitions, while companion files ending in -inl.h (e.g., spdlog-inl.h, logger-inl.h) hold the actual implementations.

In include/spdlog/spdlog.h at approximately line 350, a conditional preprocessor block controls the inclusion of the implementation:

#ifdef SPDLOG_HEADER_ONLY
#include "spdlog/spdlog-inl.h"
#endif

When you define SPDLOG_HEADER_ONLY, the public headers automatically include their matching -inl.h counterparts. This makes the library header-only by compiling all necessary code into each translation unit that includes the headers. Without this macro, the implementations reside in the compiled library (src/*.cpp), requiring you to link against libspdlog.a or libspdlog.so.

Integration Steps

Step 1: Copy the Header Files

Copy the include/spdlog directory from the repository into your project's source tree or add it to your compiler's include path. This folder contains the public API headers and the bundled copy of the fmt formatting library.

Step 2: Define the SPDLOG_HEADER_ONLY Macro

You must define SPDLOG_HEADER_ONLY before any spdlog header is included. You can do this either via a compiler flag:

g++ -std=c++17 -DSPDLOG_HEADER_ONLY -I/path/to/spdlog/include main.cpp -o my_app

Or directly in your source code:

#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"

Step 3: Manage the fmt Library Dependency

By default, spdlog uses a bundled version of the fmt library located in include/spdlog/fmt/. If your project already links against fmt externally, define SPDLOG_FMT_EXTERNAL to prevent symbol duplication:

#define SPDLOG_HEADER_ONLY
#define SPDLOG_FMT_EXTERNAL
#include "spdlog/spdlog.h"

When using this option, ensure the external fmt headers are available in your include path and link against the fmt library.

Step 4: Compile with C++11 or Later

spdlog requires a C++11-compliant compiler or newer due to its use of variadic templates, thread-local storage, and other modern language features. No additional libraries beyond the C++ standard library are required for linking.

Code Examples

Basic Console Logging

The following example demonstrates minimal setup for header-only console logging:

#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"

int main()
{
    spdlog::info("Header-only spdlog works!");
    spdlog::warn("Warning with value: {}", 42);
    return 0;
}

Color Console Output

Color console sinks are also available in header-only mode through the stdout_color_sinks.h header and its implementation in stdout_color_sinks-inl.h:

#define SPDLOG_HEADER_ONLY
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"

int main()
{
    auto console = spdlog::stdout_color_mt("console");
    console->info("Colored output in header-only mode");
    return 0;
}

Asynchronous Logging with Thread Pool

Async loggers require the thread pool implementation from include/spdlog/details/thread_pool-inl.h, which is automatically included when SPDLOG_HEADER_ONLY is defined:

#define SPDLOG_HEADER_ONLY
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"

int main()
{
    spdlog::init_thread_pool(8192, 1);
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(
        "async_file", "logs/async.txt");
    async_file->info("Async logger using header-only mode");
    return 0;
}

Using External fmt

When linking against a system-installed fmt library:

#define SPDLOG_HEADER_ONLY
#define SPDLOG_FMT_EXTERNAL
#include "spdlog/spdlog.h"

// Ensure -lfmt is passed to the linker
int main()
{
    spdlog::info("Using external fmt library");
    return 0;
}

Key Implementation Files

Understanding these source files helps when debugging or customizing header-only integration:

Summary

  • Define SPDLOG_HEADER_ONLY before including spdlog headers to enable header-only compilation.
  • The implementation logic resides in *-inl.h files (such as spdlog-inl.h and logger-inl.h) that are automatically included when the macro is defined.
  • Use SPDLOG_FMT_EXTERNAL when linking against an external fmt library to avoid duplicate symbols.
  • Header-only mode requires only the include/spdlog directory and a C++11-compliant compiler; no separate library linking is necessary.
  • All features—including async logging, color consoles, and file sinks—work identically in header-only mode.

Frequently Asked Questions

How do I switch from compiled library mode to header-only mode?

Remove the spdlog library from your linker flags (e.g., remove -lspdlog from your build command) and add #define SPDLOG_HEADER_ONLY before your first spdlog include. Ensure you are not linking against a previously compiled libspdlog.a to avoid duplicate symbol errors, as the implementation will now be compiled directly into your translation units.

Does header-only mode affect compilation times?

Yes, compilation times typically increase because the compiler must parse and compile the spdlog implementation code in every translation unit that includes the headers. For small projects, this is negligible; for large codebases, consider using precompiled headers (PCH) or the compiled library mode to reduce build times.

Can I use async loggers in header-only mode?

Absolutely. Include spdlog/async.h and initialize the thread pool with spdlog::init_thread_pool(). The header-only mode automatically pulls in include/spdlog/details/thread_pool-inl.h, which contains the full implementation of the message queue and worker thread logic required for asynchronous logging operations.

What is the difference between SPDLOG_HEADER_ONLY and SPDLOG_FMT_EXTERNAL?

SPDLOG_HEADER_ONLY controls how spdlog's own code is compiled—either as headers or requiring a compiled library. SPDLOG_FMT_EXTERNAL controls the formatting backend: when defined, spdlog uses your system's fmt library instead of its bundled copy. These macros are independent; you can use header-only mode with either the bundled fmt or an external one.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →