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

> Streamline your C++ logging with spdlog! Learn to use compile-time log level filtering via SPDLOG_ACTIVE_LEVEL. Eliminate low-level logs for zero runtime overhead and faster applications.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: how-to-guide
- Published: 2026-07-25

---

**Define `SPDLOG_ACTIVE_LEVEL` before including [`spdlog.h`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/common.h):

```cpp
#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`](https://github.com/gabime/spdlog/blob/main/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:

```cpp
#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");
}

```

### Method 2: Compiler Flags (Recommended)

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

```bash
g++ -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_WARN -I./include main.cpp -o main

```

```cpp
#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:

```cpp
// 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:

```bash
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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/tweakme.h)**: Provides a commented example showing how users might override default levels globally.

The header documentation in [`spdlog.h`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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.