How to Flush Log Messages Immediately in spdlog: 4 Methods Explained
Call logger->flush() for manual flushing, use spdlog::flush_on(level) to auto-flush by severity, or construct sinks with force_flush=true to flush after every write.
When building high-reliability applications with gabime/spdlog, you often need to ensure critical log entries persist to disk or console without delay. While the library buffers output for performance, you can override this behavior to flush log messages immediately in spdlog using several mechanisms built into the logger and sink architecture.
Method 1: Explicit Manual Flushing with flush()
The most direct way to guarantee persistence is calling the flush() method on your logger instance. This invokes the internal flush_() routine, which iterates through all attached sinks and calls their respective flush() implementations.
auto logger = spdlog::basic_logger_mt("file_logger", "logs.txt");
logger->info("Starting critical operation");
logger->error("Fatal condition detected");
// Force immediate write to underlying device
logger->flush();
This mechanism is declared in include/spdlog/logger.h and implemented in include/spdlog/logger-inl.h, where logger::flush() delegates to the protected flush_() method that synchronizes each sink’s buffer.
Method 2: Automatic Flushing by Severity Level
For hands-free operation, configure a flush level using spdlog::flush_on(). When a log message meets or exceeds this severity, the library automatically triggers a flush after writing the record.
// Flush automatically for every message (trace is the lowest level)
spdlog::flush_on(spdlog::level::trace);
auto console = spdlog::stdout_color_mt("console");
console->info("This writes and flushes immediately");
console->debug("So does this");
The global registry stores this setting in include/spdlog/spdlog.h, while each logger checks should_flush_() inside include/spdlog/logger-inl.h to determine whether to invoke flush_() after processing the log entry.
Method 3: Construct Sinks with Force-Flush Enabled
Many sink constructors accept a force_flush boolean parameter. When set to true, the sink calls flush() after every log write, ensuring no buffering occurs at the sink level.
// true enables force_flush
auto sink = std::make_shared<spdlog::sinks::stdout_sink_mt>(true);
auto logger = std::make_shared<spdlog::logger>("forced", sink);
spdlog::register_logger(logger);
logger->info("Instant persistence");
The stdout_sink_base class honors the force_flush_ flag in include/spdlog/sinks/stdout_sinks-inl.h, calling fflush(file_) immediately after each write. File sinks implement similar logic in include/spdlog/sinks/basic_file_sink-inl.h, where they invoke file_helper_.flush() to synchronize with the filesystem.
Method 4: Periodic Background Flushing (Alternative)
If strictly immediate flushing is not required but you want regular persistence without manual calls, use the background flusher. This spawns a thread that flushes all loggers at a set interval.
// Flush all loggers every 500 milliseconds
spdlog::flush_every(std::chrono::milliseconds{500});
While this does not flush instantaneously with each log entry, it provides a middle ground between performance and durability. The periodic flusher is managed by the registry implementation in include/spdlog/spdlog.h.
Choosing the Right Immediate Flush Strategy
Select the appropriate mechanism based on your durability requirements:
- Explicit
flush(): Use when you need point-in-time guarantees, such as before application exit or after critical error handling. flush_on(level): Use when specific severity levels (e.g.,errororcritical) must always persist immediately, but lower levels can buffer.- Force-flush sinks: Use when you control sink instantiation and require every single write to persist, common for console output or audit trails.
flush_every(): Use when you can tolerate slight delay (milliseconds) and want to amortize flush costs across many loggers.
Summary
- Manual control: Call
logger->flush()to force all attached sinks to write their buffers immediately. - Severity-based: Configure
spdlog::flush_on(level)to auto-flush when messages meet a threshold, implemented viashould_flush_()in the logger. - Sink-level: Pass
trueto sink constructors (e.g.,stdout_sink_mt(true)) to enableforce_flush_, ensuringfflush()or equivalent after every write. - Architecture: All flushing ultimately delegates to sink-specific implementations declared in
include/spdlog/sinks/base_sink.hand specialized in files likeinclude/spdlog/sinks/basic_file_sink-inl.hwhich callsfile_helper_.flush().
Frequently Asked Questions
Does calling flush() block the calling thread?
Yes. When you invoke logger->flush(), the calling thread executes the flush sequence, which iterates through all sinks and invokes their blocking flush() operations. For file sinks, this typically triggers a system call like fflush() or fsync(), which waits until the OS confirms the write.
What is the performance impact of flushing after every log message?
Significant. Each flush operation performs a system call and potentially disk I/O, which can reduce throughput by orders of magnitude compared to buffered logging. Use force-flush sinks or flush_on(level::trace) only when durability is more critical than performance, or during debugging sessions.
Can I flush only specific sinks while keeping others buffered?
Yes. While logger->flush() flushes all attached sinks, you can access individual sinks via logger->sinks() and call flush() on specific ones. For example: logger->sinks()[0]->flush(); This allows fine-grained control over which outputs require immediate persistence without affecting high-throughput file loggers.
How do I ensure logs are flushed before my application exits?
Register an atexit handler or use a scope guard that calls spdlog::shutdown() or manually invokes flush() on your loggers. The global registry destructor attempts to flush all loggers, but explicit flushing ensures no messages are lost during abnormal termination.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →