# Flush on vs Flush Every in spdlog: When to Flush Logs for Maximum Reliability

> Understand spdlog flush_on vs flush_every. Persist critical logs instantly with flush_on or schedule periodic flushes with flush_every for optimal reliability and performance.

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

---

**Use `flush_on(level)` to immediately persist critical messages when they reach a specific severity threshold, and `flush_every(duration)` to periodically flush all buffers on a fixed schedule without per-message overhead.**

Choosing the right flushing strategy in **gabime/spdlog** is essential for balancing I/O performance with data durability. The library provides two distinct mechanisms for controlling when buffered log data is written to sinks. Understanding when to flush logs in spdlog ensures that critical errors are never lost while maintaining high throughput for routine logging operations.

## Understanding `flush_on` vs `flush_every`

### `flush_on`: Per-Message Flushing

The `flush_on(level)` function sets a **global flush level** that applies to every logger in the registry. When a log record's severity is greater than or equal to this threshold, the logger immediately calls `flush()` on all its sinks before returning. This guarantees that critical messages are persisted to disk instantly, while lower-level traffic remains buffered for performance.

According to the source code in [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) (lines 300–303) and [`include/spdlog/logger-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger-inl.h) (lines 100–103), `flush_on` performs a simple atomic store:

```cpp
// spdlog/logger.h
void flush_on(level::level_enum log_level);

// spdlog/logger-inl.h
SPDLOG_INLINE void logger::flush_on(level::level_enum log_level) { 
    flush_level_.store(log_level); 
}

```

During each log call, the method `should_flush_()` (defined in [`logger-inl.h`](https://github.com/gabime/spdlog/blob/main/logger-inl.h) lines 166–167) checks if `msg.level >= flush_level()`. If true, `logger::flush_()` iterates over the logger’s sinks and invokes `sink->flush()` ([`logger-inl.h`](https://github.com/gabime/spdlog/blob/main/logger-inl.h) lines 148–150).

### `flush_every`: Periodic Flushing

The `flush_every(duration)` function starts a **background thread** that calls `flush()` on all registered loggers at fixed intervals, regardless of message level. This is implemented as a thin wrapper around the registry’s periodic flusher (see [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) lines 83–88 and [`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h) line 68):

```cpp
// spdlog/spdlog.h
template <typename Rep, typename Period>
inline void flush_every(std::chrono::duration<Rep, Period> interval) {
    details::registry::instance().flush_every(interval);
}

```

The registry spawns a thread that sleeps for the requested interval and then executes `apply_all([](auto l){ l->flush(); })`, ensuring that all buffered logs are persisted on a predictable schedule.

## Performance and Thread Safety Considerations

**Performance Impact:**
- **`flush_on`** adds only a cheap atomic compare per log entry; the expensive I/O operation happens only when high-severity messages arrive.
- **`flush_every`** incurs the cost of a background thread and triggers a global flush at each tick, which can become expensive if sinks are numerous or slow (e.g., remote network sinks).

**Thread Safety:**
`flush_every` requires that **all** loggers be thread-safe. As noted in [`spdlog.h`](https://github.com/gabime/spdlog/blob/main/spdlog.h) line 84, if any logger uses a non-thread-safe sink (such as a non-locking stdout sink), the periodic flusher may race with user threads. In contrast, `flush_on` respects each sink’s existing thread-safety guarantees because flushes are triggered synchronously by the calling thread.

## When to Use Which Strategy

1. **Critical Reliability:** If losing the latest error messages is unacceptable, configure `spdlog::flush_on(spdlog::level::err)` or `level::critical`. This ensures that any error is immediately flushed, while ordinary `info` and `debug` traffic stays buffered.

2. **Regular Durability Without Overhead:** For long-lived services that can tolerate a few seconds of delay, use `spdlog::flush_every(std::chrono::seconds(5))`. This provides predictable persistence without the per-message cost of `flush_on`.

3. **Combined Strategy:** It is common to use both mechanisms simultaneously. Set `flush_on(level::critical)` for urgent failures, and `flush_every(std::chrono::seconds(10))` to guarantee that lower-level logs are persisted periodically.

## Code Examples

### Example 1: Flush Immediately on Error or Higher

```cpp
#include "spdlog/spdlog.h"

int main() {
    spdlog::set_level(spdlog::level::info);          // global log level
    spdlog::flush_on(spdlog::level::err);           // immediate flush for errors

    auto logger = spdlog::stdout_color_mt("mylog");
    logger->info("normal message");                 // buffered
    logger->error("something went wrong!");         // forces flush
}

```

### Example 2: Periodic Flushing Every 5 Seconds

```cpp
#include "spdlog/spdlog.h"

int main() {
    spdlog::flush_every(std::chrono::seconds(5));   // starts background flusher

    auto file_logger = spdlog::basic_logger_mt("filelog", "app.log");
    file_logger->debug("debug line");               // will be flushed within 5s
    
    // Keep main alive to see periodic flushing
    std::this_thread::sleep_for(std::chrono::seconds(6));
}

```

### Example 3: Combine Both Strategies

```cpp
#include "spdlog/spdlog.h"

int main() {
    spdlog::flush_on(spdlog::level::critical);      // only critical messages force immediate flush
    spdlog::flush_every(std::chrono::seconds(10));  // otherwise flush every 10s

    auto logger = spdlog::basic_logger_mt("combined", "app.log");
    logger->info("routine operation");              // flushed within 10s
    logger->critical("system failure!");            // flushed immediately
}

```

## Summary

- **`flush_on`** triggers immediate I/O when messages meet a severity threshold, ensuring critical logs are never lost.
- **`flush_every`** uses a background thread to flush all loggers on a fixed schedule, reducing per-message overhead.
- Combining both strategies provides immediate persistence for errors alongside periodic durability for routine logs.
- Always verify thread-safety when using `flush_every`, as it iterates over all loggers from a separate thread.

## Frequently Asked Questions

### What is the difference between `flush_on` and `flush_every` in spdlog?

`flush_on` checks every log message's severity and flushes immediately if it meets the threshold, while `flush_every` spawns a background thread that flushes all loggers at fixed time intervals regardless of message content. The former is event-driven; the latter is time-driven.

### Does `flush_on` affect all loggers in spdlog?

Yes, `flush_on` sets a global flush level in the registry that applies to all registered loggers. Each logger checks this global level via an atomic load before deciding whether to flush its sinks.

### Is `flush_every` thread-safe in spdlog?

`flush_every` is safe only if **all** registered loggers use thread-safe sinks. The periodic flusher calls `flush()` on every logger from a background thread, which will race with user threads if any sink lacks locking (e.g., `stdout_color_sink` without `mt`).

### Can I use `flush_on` and `flush_every` together in the same application?

Yes, combining both is a recommended pattern. Use `flush_on(spdlog::level::err)` to guarantee immediate persistence of errors, and `flush_every(std::chrono::seconds(10))` to ensure that lower-severity messages are eventually written without paying the I/O cost on every log call.