SPDLog Macros Explained: Compile-Time Logging Control in C++

SPDLog macros provide zero-cost compile-time filtering by expanding to logging calls when enabled or to no-ops when disabled, automatically capturing source location and supporting both default and custom logger instances.

All logging macros in the gabime/spdlog repository are defined in include/spdlog/spdlog.h, wrapping the core logger API to eliminate boilerplate while maintaining high performance. Understanding these SPDLog macros is essential for configuring compile-time verbosity levels and optimizing release builds.

How SPDLog Macros Work at Compile Time

The macro system relies on conditional compilation to remove disabled logging statements entirely from the binary. This happens before the C++ compiler generates machine code, ensuring that suppressed log levels incur absolutely no runtime overhead.

The Role of SPDLOG_ACTIVE_LEVEL

The SPDLOG_ACTIVE_LEVEL macro acts as a global verbosity gate. Defined before including spdlog.h, it determines which severity levels remain in the compiled binary.

// Enable debug-level logging at compile time
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include "spdlog/spdlog.h"

Each logging macro checks against this value using preprocessor conditionals. For example, in include/spdlog/spdlog.h around lines 292–311, the debug macros are guarded by:

#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
// Macro definitions active
#else
#define SPDLOG_DEBUG(...) (void)0
#endif

When the condition fails, the macro expands to (void)0, which the compiler optimizes away completely.

Source Location Capture with SPDLOG_FUNCTION

By default, SPDLog macros automatically capture file, line, and function information. The internal SPDLOG_LOGGER_CALL macro constructs a spdlog::source_loc object:

#define SPDLOG_LOGGER_CALL(logger, level, ...) \
    (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)

This captures __FILE__ and __LINE__ from the preprocessor, along with SPDLOG_FUNCTION (which resolves to __func__ or compiler-specific equivalents). To disable this overhead, define SPDLOG_NO_SOURCE_LOC before including the header, causing the macro to omit the source location payload.

The Two Macro Families

SPDLog provides parallel macro sets for every severity level: trace, debug, info, warn, error, critical, and off. These divide into two categories based on the target logger.

SPDLOG_LOGGER_ Macros

These macros target a specific logger instance passed as the first argument. They expand to the log() method on that instance:

auto file_logger = spdlog::basic_logger_mt("filelog", "app.log");
SPDLOG_LOGGER_WARN(file_logger, "Low disk space warning: {}% remaining", 5);

In include/spdlog/spdlog.h, SPDLOG_LOGGER_DEBUG expands to:

#define SPDLOG_LOGGER_DEBUG(logger, ...) \
    SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)

This pattern applies to all severity levels, delegating to SPDLOG_LOGGER_CALL with the appropriate spdlog::level enum value.

SPDLOG_ Macros

These convenience macros route to the default logger (spdlog::default_logger_raw()), eliminating the need to pass a logger pointer explicitly:

SPDLOG_INFO("Application started successfully");
// Expands to:
// SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), "Application started successfully")

The default logger typically writes to stdout. If no default logger exists, these macros safely handle the null case.

Compile-Time Optimization and Zero-Cost Abstractions

The macro system achieves zero-cost abstraction through several mechanisms:

  1. Conditional compilation — Disabled levels compile to no-ops
  2. Inline expansion — Macros inline directly to logger->log() calls without function call overhead
  3. Compile-time formatting checks — When using fmt integration, format strings are validated at compile time

Define SPDLOG_NO_SOURCE_LOC in include/spdlog/tweakme.h or as a compiler flag to remove source location storage, reducing binary size and per-log overhead in performance-critical paths.

Practical Configuration Examples

Enable debug logging in development:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include "spdlog/spdlog.h"

int main() {
    spdlog::set_level(spdlog::level::debug); // Runtime level
    
    SPDLOG_DEBUG("Variable x = {}", 42);     // Compiled and executed
    SPDLOG_TRACE("Deep trace");             // Compiled but filtered at runtime
}

Use custom loggers for specific modules:

auto network_logger = spdlog::basic_logger_mt("network", "network.log");
SPDLOG_LOGGER_INFO(network_logger, "Connection established: {}", ip_address);

Strip all logging for release builds:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_OFF
#include "spdlog/spdlog.h"
// All SPDLOG_* macros now expand to (void)0

Summary

  • SPDLog macros are defined in include/spdlog/spdlog.h and wrap the core logger::log() API
  • SPDLOG_ACTIVE_LEVEL controls compile-time filtering, eliminating disabled statements from the binary
  • Two macro families exist: SPDLOG_<LEVEL>() for the default logger, and SPDLOG_LOGGER_<LEVEL>() for custom instances
  • Source location is captured automatically via __FILE__, __LINE__, and SPDLOG_FUNCTION, unless disabled with SPDLOG_NO_SOURCE_LOC
  • Zero runtime overhead occurs when logging levels are disabled at compile time

Frequently Asked Questions

What is SPDLOG_ACTIVE_LEVEL?

SPDLOG_ACTIVE_LEVEL is a preprocessor macro that sets the minimum logging severity included in the compilation. Valid values range from SPDLOG_LEVEL_TRACE (0) to SPDLOG_LEVEL_OFF (6). Any logging macro with a severity level lower than the active level expands to (void)0 and is removed by the compiler. Define this macro before including spdlog.h.

How do I disable source location tracking?

Define SPDLOG_NO_SOURCE_LOC before including spdlog.h. This causes all logging macros to omit the spdlog::source_loc parameter, reducing per-log overhead and binary size. This is useful in release builds where file and line information is not required. The definition can be placed in include/spdlog/tweakme.h or passed as a compiler flag (-DSPDLOG_NO_SOURCE_LOC).

What is the difference between SPDLOG_INFO and SPDLOG_LOGGER_INFO?

SPDLOG_INFO(...) logs to the default logger obtained via spdlog::default_logger_raw(), requiring no logger argument. SPDLOG_LOGGER_INFO(logger, ...) accepts a specific logger instance as its first argument, routing the message to that sink. The latter is essential when using multiple loggers for different subsystems or output destinations.

Do SPDLog macros have runtime overhead when disabled?

No. When SPDLOG_ACTIVE_LEVEL is set higher than a specific macro's level (e.g., set to WARN while using SPDLOG_DEBUG), the preprocessor replaces the macro with (void)0. The compiler optimizes this away completely, resulting in zero instructions and no runtime cost. This is why SPDLog macros are preferred over runtime-checked logging in performance-critical C++ applications.

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 →