# How to Create Rotating File Sinks in spdlog: A Complete Guide

> Learn how to create rotating file sinks in spdlog with our complete guide. Effectively manage log file sizes automatically for better organization and performance.

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

---

**To create rotating file sinks in spdlog, use the `rotating_file_sink_mt` or `rotating_file_sink_st` templates, or the convenience factory functions `spdlog::rotating_logger_mt()` and `spdlog::rotating_logger_st()`, which automatically roll over log files when they reach a specified size limit.**

The **gabime/spdlog** library provides a high-performance, header-only C++ logging framework with built-in support for log rotation. When you create rotating file sinks in spdlog, the library handles automatic file rollover, ensuring that log archives remain manageable while preserving historical data according to configurable retention policies.

## How Rotating File Sinks Work in spdlog

The rotating file sink implementation resides in [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h) with inline definitions in [`include/spdlog/sinks/rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink-inl.h). The class template `spdlog::sinks::rotating_file_sink<Mutex>` inherits from `base_sink<Mutex>` and encapsulates a `details::file_helper` for low-level file operations.

When a log message arrives, the `sink_it_` method formats the message and estimates the resulting file size. If the size would exceed the configured `max_size_`, the sink flushes the current file and invokes `rotate_()` before writing the new entry. The rotation algorithm iterates backwards from `max_files_` down to 1, renaming each existing file to the next index (e.g., [`log.1.txt`](https://github.com/gabime/spdlog/blob/main/log.1.txt) becomes [`log.2.txt`](https://github.com/gabime/spdlog/blob/main/log.2.txt)), then opens a fresh file for continued logging.

## Creating a Rotating File Logger

### Using the Factory Helper (Multi-Threaded)

The simplest way to create a rotating logger is through the `spdlog::rotating_logger_mt()` factory function defined in [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h).

```cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>

int main() {
    // Create a logger that rotates after 5 MiB, keeping up to 3 files
    auto logger = spdlog::rotating_logger_mt(
        "my_logger",               // logger name
        "logs/app.log",            // base filename
        5 * 1024 * 1024,           // 5 MiB max size per file
        3);                        // keep 3 rotated files

    logger->info("Application started");
}

```

This function internally instantiates `rotating_file_sink_mt` (the mutex-synchronized variant) through `synchronous_factory::create`.

### Explicit Sink Construction (Single-Threaded)

For single-threaded applications where synchronization overhead is unnecessary, use `rotating_file_sink_st` with a `null_mutex`:

```cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>

int main() {
    using sink_t = spdlog::sinks::rotating_file_sink_st;
    auto sink = std::make_shared<sink_t>(
        "logs/server.log",          // base filename
        10 * 1024 * 1024,           // 10 MiB max size
        5,                          // keep 5 rotated files
        false);                     // do not rotate on open

    auto logger = std::make_shared<spdlog::logger>("server", sink);
    spdlog::register_logger(logger);

    logger->warn("Server listening on port {}", 8080);
}

```

## Understanding the Rotation Algorithm

The rotation logic in [`include/spdlog/sinks/rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink-inl.h) follows a specific naming convention and renaming strategy:

- **Filename calculation**: The `calc_filename()` method generates names like [`log.txt`](https://github.com/gabime/spdlog/blob/main/log.txt), [`log.1.txt`](https://github.com/gabime/spdlog/blob/main/log.1.txt), through [`log.N.txt`](https://github.com/gabime/spdlog/blob/main/log.N.txt) by splitting the base filename and inserting rotation indices.
- **Validation**: The constructor enforces that `max_size` must be greater than 0 and `max_files` cannot exceed 200,000 (`MaxFiles` constant).
- **Atomic renaming**: The `rotate_()` method closes the current file, then iterates from `max_files_` down to 1, renaming each existing file to the next higher index using `rename_file_()`, which removes existing targets before invoking the OS rename primitive.

## Thread Safety Considerations

spdlog provides two type aliases for different concurrency requirements:

- **`rotating_file_sink_mt`**: Uses `std::mutex` for thread-safe access from multiple threads.
- **`rotating_file_sink_st`**: Uses `spdlog::details::null_mutex` (no-op) for single-threaded scenarios, eliminating synchronization overhead.

Choose the appropriate variant based on your application's threading model to optimize performance.

## Manual Rotation and Advanced Usage

You can force immediate rotation programmatically using the `rotate_now()` method:

```cpp
logger->flush();          // ensure all data is written
auto sink = std::static_pointer_cast<spdlog::sinks::rotating_file_sink_mt>(logger->sinks()[0]);
sink->rotate_now();       // immediate rotation

```

This acquires the sink's mutex and invokes the internal `rotate_()` logic directly, useful for log rotation triggered by external signals or time-based policies.

## Summary

- **Create rotating file sinks in spdlog** using `rotating_logger_mt()`/`rotating_logger_st()` factory functions or direct template instantiation of `rotating_file_sink_mt`/`st`.
- The implementation resides in [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h) with rotation logic in the [`-inl.h`](https://github.com/gabime/spdlog/blob/main/-inl.h) header.
- Configure maximum file size and retention count (up to 200,000 files) via constructor parameters.
- Select `rotating_file_sink_mt` for multi-threaded applications or `rotating_file_sink_st` for single-threaded performance.
- Use `rotate_now()` to trigger manual rotation when needed.

## Frequently Asked Questions

### What is the maximum number of rotated files supported?

According to the source code in [`include/spdlog/sinks/rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink-inl.h), spdlog enforces a hard limit of 200,000 rotated files (`MaxFiles` constant). The constructor validates that the `max_files` parameter is greater than 0 and does not exceed this upper bound.

### How does spdlog handle file naming during rotation?

The `calc_filename()` function in [`include/spdlog/sinks/rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink-inl.h) splits the base filename into stem and extension, then inserts the rotation index between them. This produces sequences like `app.log`, `app.1.log`, `app.2.log`, etc., ensuring chronological ordering by index.

### Can I force a log rotation manually?

Yes. Obtain a pointer to the sink (casting from `logger->sinks()`), then call `rotate_now()`. This method locks the mutex and executes the same `rotate_()` algorithm used during automatic size-based rotation, closing the current file and cycling archived files immediately.

### What is the difference between rotating_file_sink_mt and rotating_file_sink_st?

`rotating_file_sink_mt` instantiates the template with `std::mutex`, providing thread-safe logging for concurrent access. `rotating_file_sink_st` uses `null_mutex`, which performs no locking and offers better performance for single-threaded contexts where synchronization is unnecessary.