# spdlog rotating_file_sink vs daily_file_sink: Key Implementation Differences

> Explore spdlog rotating_file_sink vs daily_file_sink implementation differences. Understand distinct file naming and cleanup for size-based vs. time-based log rotation.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: deep-dive
- Published: 2026-08-06

---

**The `rotating_file_sink` rotates log files when they exceed a maximum size, while `daily_file_sink` rotates them at a scheduled time each day, with each sink using distinct file naming schemes and cleanup mechanisms.**

Both sinks inherit from `spdlog::sinks::base_sink<Mutex>` in the gabime/spdlog repository and provide automatic file rotation, yet they differ fundamentally in trigger logic, naming conventions, and configuration options. Understanding these distinctions helps you select the right sink for high-volume logging versus time-based archival requirements.

## Rotation Trigger Mechanisms

The primary distinction between these sinks lies in **what triggers a rotation event**.

### Size-Based Rotation in rotating_file_sink

In [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h), the `rotating_file_sink` monitors the current file size through the `current_size_` member. When a log message would exceed `max_size`, the sink invokes the `rotate_()` method (lines 41-48) to perform the rotation immediately. This approach ensures that no single log file grows beyond a manageable byte threshold, which is critical for storage-constrained environments.

### Time-Based Rotation in daily_file_sink

Conversely, `daily_file_sink` implemented in [`include/spdlog/sinks/daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/daily_file_sink.h) uses `std::chrono` and `log_clock` to calculate the next rotation point via `next_rotation_tp_()`. The rotation check occurs in `sink_it_()` (lines 106-114), which compares the incoming message's timestamp against `rotation_tp_`. When the current time passes the scheduled rotation hour and minute, the sink closes the current file and creates a new one for the next day.

## File Naming and Organization

Each sink generates filenames using fundamentally different strategies.

### Numeric Suffixes vs Date Stamps

The `rotating_file_sink` uses `calc_filename()` (lines 29-30) to generate a chain of files with numeric suffixes: [`log.txt`](https://github.com/gabime/spdlog/blob/main/log.txt) becomes [`log.1.txt`](https://github.com/gabime/spdlog/blob/main/log.1.txt), then [`log.2.txt`](https://github.com/gabime/spdlog/blob/main/log.2.txt), up to `max_files`. This creates a chronological sequence where higher numbers indicate older files.

The `daily_file_sink` employs `daily_filename_calculator` (lines 30-37) to embed the date directly into the filename using the pattern `basename_YYYY-MM-DD.ext`. An alternative `daily_filename_format_calculator` variant allows custom date formatting strings, making it easier to identify logs by calendar date without checking file metadata.

## Configuration and Constructor Options

The constructor signatures reflect their distinct purposes:

**rotating_file_sink** (from [`rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/rotating_file_sink.h) lines 24-28):

```cpp
rotating_file_sink(filename_t base_filename, 
                   size_t max_size, 
                   size_t max_files, 
                   bool rotate_on_open = false, ...)

```

**daily_file_sink** (from [`daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/daily_file_sink.h) lines 71-78):

```cpp
daily_file_sink(filename_t base_filename, 
                int rotation_hour, 
                int rotation_minute, 
                bool truncate = false, 
                uint16_t max_files = 0, ...)

```

Key differences include:

- **Size-centric vs time-centric**: The rotating variant takes `max_size` in bytes, while the daily variant takes `rotation_hour` and `rotation_minute`.
- **Rotate on open**: Only `rotating_file_sink` supports the `rotate_on_open` flag, which forces an immediate rotation when the sink is created—useful for "log rollover on application start" scenarios.
- **Truncation**: Only `daily_file_sink` offers a `truncate` parameter that, when true, truncates the daily file upon creation via `file_helper_.open(new_filename, truncate_)`.

## File Management and Cleanup Strategies

Both sinks limit the total number of archived files, but implement cleanup differently.

### Rename Chain Deletion

In [`rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/rotating_file_sink-inl.h), the `rotate_()` method builds a rename chain: [`log.2.txt`](https://github.com/gabime/spdlog/blob/main/log.2.txt) moves to [`log.3.txt`](https://github.com/gabime/spdlog/blob/main/log.3.txt), [`log.1.txt`](https://github.com/gabime/spdlog/blob/main/log.1.txt) to [`log.2.txt`](https://github.com/gabime/spdlog/blob/main/log.2.txt), and finally the active [`log.txt`](https://github.com/gabime/spdlog/blob/main/log.txt) becomes [`log.1.txt`](https://github.com/gabime/spdlog/blob/main/log.1.txt). After this cascade, if the file count exceeds `max_files`, the sink simply deletes the highest-numbered file (the oldest).

### Circular Queue Tracking

In [`daily_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/daily_file_sink-inl.h), the `delete_old_()` method (lines 64-82) maintains a `filenames_q_`—a `details::circular_q` structure that tracks the most recently created filenames. When rotation occurs and `max_files` is set, the sink calculates which file is now too old (N days prior) and removes it from disk. This circular queue approach handles date-based cleanup without needing to scan the filesystem or parse filenames.

## Practical Usage Examples

**Size-based rotation** for application logs with high volume:

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

// Rotate when file exceeds 10MB, keep 5 archived files
auto rot_logger = spdlog::rotating_logger_mt(
    "rot_logger", "logs/app.log", 10 * 1024 * 1024, 5);
    
// Optionally force rotation on startup
rot_logger->info("Application started");

```

**Daily rotation** for audit trails and daily archives:

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

// Rotate every day at 02:00, keep last 7 days, truncate on open
auto daily_logger = spdlog::daily_logger_mt(
    "daily_logger", "logs/audit.log", 2, 0, false, 7);
    
daily_logger->info("Daily audit log entry");

```

## Summary

- **Trigger**: `rotating_file_sink` uses file size (`max_size`) via `rotate_()`, while `daily_file_sink` uses scheduled time via `sink_it_()` and `rotation_tp_` comparisons.
- **Naming**: Rotating sink uses numeric suffixes (`calc_filename()`), daily sink uses date stamps (`daily_filename_calculator`).
- **Cleanup**: Rotating sink renames files in a chain and deletes the oldest; daily sink uses a `filenames_q_` circular queue to track and `delete_old_()` expired files.
- **Options**: Rotating sink supports `rotate_on_open`; daily sink supports `truncate` and custom rotation times.
- **Source files**: Implementation details reside in [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h) (and [`-inl.h`](https://github.com/gabime/spdlog/blob/main/-inl.h)) versus [`include/spdlog/sinks/daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/daily_file_sink.h) (and [`-inl.h`](https://github.com/gabime/spdlog/blob/main/-inl.h)).

## Frequently Asked Questions

### Can I use both rotating and daily file sinks simultaneously in spdlog?

Yes. You can attach multiple sinks to a single logger using `spdlog::logger` with `add_sink()`, or create separate loggers for different retention policies. This allows you to maintain size-limited debug logs alongside permanent daily audit trails within the same application.

### How does daily_file_sink handle timezone changes or daylight saving time?

The `daily_file_sink` uses `std::chrono` and the system's `log_clock`, which typically represents UTC or system local time depending on your platform. As implemented in `next_rotation_tp_()`, the calculation uses `std::tm` structures. If the system clock changes (including DST transitions), the next rotation calculation adjusts accordingly, though you may experience a shorter or longer rotation period during the transition hour.

### What happens if the log directory runs out of disk space with rotating_file_sink?

The `rotating_file_sink` does not explicitly check disk space before writing. When `current_size_` approaches `max_size`, it attempts the `rotate_()` operation. If the filesystem is full, the rename operations in the rotation chain may fail, and depending on your error handler configuration, the sink may either throw an exception or drop log messages. You should monitor disk space externally or implement a custom `rotating_file_sink` subclass with pre-write checks.

### Is there a performance difference between the two sinks?

Both sinks have minimal overhead, but `rotating_file_sink` performs a simple size check (`current_size_ += msg_size`) in `sink_it_()`, while `daily_file_sink` must convert timestamps and compare against a `rotation_tp_` time point. For extremely high-throughput logging (millions of messages per second), the size check is marginally faster, though the difference is negligible for most applications. The daily sink's `filenames_q_` circular queue adds minimal memory overhead regardless of file count.