How to Use Compile-Time Log Level Filtering with SPDLOG_ACTIVE_LEVEL in spdlog

Define SPDLOG_ACTIVE_LEVEL before including spdlog.h to eliminate logging calls below a specified severity at compile time, resulting in zero runtime overhead for disabled levels.

The gabime/spdlog library provides a compile-time log level filtering mechanism that strips unwanted logging code from your binary entirely. By setting the SPDLOG_ACTIVE_LEVEL macro, you instruct the preprocessor to replace disabled logging macros with no-operations, ensuring that debug or trace statements consume no CPU cycles in production builds.

Understanding SPDLOG_ACTIVE_LEVEL

SPDLOG_ACTIVE_LEVEL acts as a compile-time gatekeeper that determines which logging macros generate actual code. According to the source code in include/spdlog/common.h (lines 41-44), if you do not explicitly define this macro, spdlog defaults to SPDLOG_LEVEL_INFO, meaning only informational messages and above are compiled.

The filtering mechanism relies on numerical constants defined in include/spdlog/common.h:

#define SPDLOG_LEVEL_TRACE     0
#define SPDLOG_LEVEL_DEBUG     1
#define SPDLOG_LEVEL_INFO      2
#define SPDLOG_LEVEL_WARN      3
#define SPDLOG_LEVEL_ERROR     4
#define SPDLOG_LEVEL_CRITICAL  5
#define SPDLOG_LEVEL_OFF       6

When SPDLOG_ACTIVE_LEVEL is set to a specific value, any logging macro with a higher numerical value (lower severity) expands to (void)0, while macros with equal or lower values (higher severity) expand to real function calls.

How Compile-Time Filtering Works

The implementation spans two critical header files. In include/spdlog/spdlog.h (lines 99-152), the library compares SPDLOG_ACTIVE_LEVEL against each severity constant using preprocessor conditionals. For levels exceeding the active threshold, the macro resolves to a no-op. For levels within the threshold, the macro expands to a logging call as shown in lines 298-347.

This design ensures that the compiler never sees the disabled logging code. Because the preprocessor replaces unwanted macros with (void)0, the resulting binary contains no instructions for those log statements, eliminating both runtime overhead and string literals that would otherwise increase binary size.

Configuring Compile-Time Log Levels

You can define SPDLOG_ACTIVE_LEVEL through multiple methods depending on your build requirements.

Method 1: Source File Definition

Define the macro immediately before including the spdlog header to enable trace-level debugging for a specific file:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#include <spdlog/spdlog.h>

int main() {
    SPDLOG_TRACE("This trace message gets compiled");
    SPDLOG_DEBUG("This debug message gets compiled");
    SPDLOG_INFO("This info message gets compiled");
}

For large projects, pass the definition via compiler flags to maintain consistent settings across all translation units:

g++ -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_WARN -I./include main.cpp -o main
#include <spdlog/spdlog.h>

int main() {
    SPDLOG_TRACE("Removed at compile time");   // Expands to (void)0
    SPDLOG_DEBUG("Removed at compile time");   // Expands to (void)0
    SPDLOG_INFO("Removed at compile time");    // Expands to (void)0
    SPDLOG_WARN("This warning appears");       // Real logging call
}

Method 3: Per-File Granularity

You can override the global setting for specific debugging modules while maintaining stricter levels elsewhere:

// debug_module.cpp
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include <spdlog/spdlog.h>

void detailed_diagnostic() {
    SPDLOG_DEBUG("Verbose debugging enabled only in this file");
}

Method 4: Disabling All Logging

Set the level to SPDLOG_LEVEL_OFF to eliminate all logging overhead in release builds:

g++ -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF -I./include release_build.cpp

All macros—including SPDLOG_CRITICAL—become no-ops, completely removing spdlog code from the compiled binary.

Implementation Details in Source Code

Three header files constitute the core of this mechanism:

  • include/spdlog/common.h: Defines the numerical constants (SPDLOG_LEVEL_TRACE through SPDLOG_LEVEL_OFF) and establishes the default SPDLOG_ACTIVE_LEVEL value when undefined by the user.
  • include/spdlog/spdlog.h: Contains the conditional macro logic (lines 78-89 document the intended usage, while lines 99-152 implement the filtering). This file determines whether each SPDLOG_* macro expands to a logging call or (void)0.
  • include/spdlog/tweakme.h: Provides a commented example showing how users might override default levels globally.

The header documentation in spdlog.h (lines 78-89) explicitly states that you must define SPDLOG_ACTIVE_LEVEL to one of the provided constants before including the header to enable or disable log calls at compile time according to the global level.

Summary

  • Define SPDLOG_ACTIVE_LEVEL before any #include <spdlog/spdlog.h> statement to control which logging macros compile.
  • Numerical values range from 0 (TRACE) to 6 (OFF), where higher values disable more logging levels.
  • Zero runtime cost occurs for disabled levels because the preprocessor replaces them with (void)0, allowing the compiler to optimize away all associated code.
  • Compiler flags provide the most maintainable approach for large codebases, while per-file definitions enable granular debugging control.
  • Binary size reduction occurs when disabling levels because string literals and formatting code for filtered logs are excluded from the final executable.

Frequently Asked Questions

What is the default SPDLOG_ACTIVE_LEVEL if I don't define it?

According to include/spdlog/common.h lines 41-44, spdlog defaults to SPDLOG_LEVEL_INFO when SPDLOG_ACTIVE_LEVEL is undefined. This means SPDLOG_TRACE and SPDLOG_DEBUG calls become no-ops automatically, while info, warning, error, and critical messages compile normally.

Does compile-time filtering affect runtime log level settings?

No. SPDLOG_ACTIVE_LEVEL operates exclusively at compile time through preprocessor macros, while runtime filtering uses the set_level() method on logger instances. Compile-time filtering happens before compilation, removing code entirely, whereas runtime filtering evaluates log levels during execution. You can use both mechanisms simultaneously, but compile-time filtering takes precedence—if SPDLOG_DEBUG is disabled at compile time, no runtime setting can re-enable it.

Can I use different compile-time levels for different loggers?

No. SPDLOG_ACTIVE_LEVEL is a global compile-time constant that applies to all logging macros universally. However, you can achieve similar granularity by using different translation units with different macro definitions, or by using runtime log levels for per-logger control while maintaining a permissive compile-time level (such as SPDLOG_LEVEL_TRACE).

What happens to binary size when using SPDLOG_LEVEL_OFF?

When you define SPDLOG_ACTIVE_LEVEL as SPDLOG_LEVEL_OFF, all logging macros—including SPDLOG_CRITICAL—expand to (void)0. This removes not only the function call overhead but also the string literals and formatting templates associated with log messages, significantly reducing binary size compared to runtime-filtered builds where the strings remain embedded in the executable.

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 →