# spdlog File Sinks: Differences Between Rotating, Daily, and Hourly

> Understand spdlog file sinks rotating, daily, and hourly. Learn how rotating_file_sink, daily_file_sink, and hourly_file_sink manage log file lifecycles to enforce disk limits or trigger rotation.

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

---

**spdlog provides three primary file sinks—`rotating_file_sink` (size-driven), `daily_file_sink` (calendar-driven), and `hourly_file_sink` (time-slot-driven)—that manage log file lifecycle by either enforcing disk space limits or triggering rotation at specific chronological boundaries.**

The `gabime/spdlog` repository offers these distinct sink implementations to address different operational requirements for log retention and file organization. While `rotating_file_sink` caps individual file size to prevent unbounded growth, `daily_file_sink` and `hourly_file_sink` use absolute clock time to partition logs into predictable temporal segments.

## Time-Based Rotation: Daily and Hourly Sinks

Time-driven sinks create new log files at fixed intervals, embedding timestamps directly into filenames for easy chronological identification.

### Daily File Sink (`daily_file_sink`)

The daily sink creates a new log file at a configurable hour and minute each day (for example, 02:30 AM). In [`include/spdlog/sinks/daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/daily_file_sink.h), the implementation uses `daily_filename_calculator` (lines 30-38) to generate filenames that incorporate the current date, producing outputs like `app_2024-09-15.log`.

Rotation timing is computed by `next_rotation_tp_()` (lines 51-62), which calculates the absolute time point for the next file creation. The constructor signature is:

```cpp
daily_file_sink(base_filename, hour, minute, truncate = false, max_files = 0, ...)

```

Retention is handled by the optional `max_files` parameter. When set, the `delete_old_()` method (lines 64-82) automatically removes daily files older than the specified count, ensuring only the most recent *N* days remain on disk.

### Hourly File Sink (`hourly_file_sink`)

The hourly sink operates as a higher-frequency variant of the daily implementation, triggering rotation every 60 minutes at the top of the hour. Found in [`include/spdlog/sinks/hourly_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/hourly_file_sink.h), this sink uses `hourly_filename_calculator` to embed hour-level timestamps into filenames. It shares the same retention policy and thread-safety characteristics as the daily sink, making it suitable for high-volume logging scenarios where a single day’s logs would otherwise grow too large for practical analysis.

## Size-Based Rotation: Rotating File Sink (`rotating_file_sink`)

Unlike time-driven alternatives, `rotating_file_sink` monitors file size to determine when to create a new file. When the current log exceeds the configured `max_size` limit (for example, 10 MiB), the sink triggers a rotation.

The implementation in [`include/spdlog/sinks/rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink-inl.h) defines `calc_filename` (lines 54-64) to append numeric indices, generating sequences like `app.log`, `app.1.log`, `app.2.log`. The core rotation logic resides in `rotate_()` (lines 38-66), which renames existing files and removes the oldest when `max_files` is exceeded.

Size checking occurs on every write via `sink_it_()` (lines 13-22), which projects the new file size and calls `rotate_()` when the limit would be surpassed. The constructor is defined at lines 25-32:

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

```

## Technical Comparison of Sink Behaviors

| Aspect | Daily File Sink | Hourly File Sink | Rotating File Sink |
|--------|----------------|------------------|--------------------|
| **Rotation Trigger** | Specific time daily (HH:MM) | Every 60 minutes (top of hour) | File size exceeds `max_size` |
| **Filename Pattern** | Date-embedded (`app_YYYY-MM-DD.log`) | Hour-timestamp embedded | Indexed suffix (`app.N.log`) |
| **Retention Method** | `delete_old_()` removes oldest dated files | Same as daily | `rotate_()` shifts indices and deletes overflow |
| **Configuration** | `hour`, `minute`, `max_files` | `max_files` only | `max_size`, `max_files`, `rotate_on_open` |
| **Source Files** | [`daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/daily_file_sink.h) (lines 30-82) | [`hourly_file_sink.h`](https://github.com/gabime/spdlog/blob/main/hourly_file_sink.h) | [`rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/rotating_file_sink.h), [`rotating_file_sink-inl.h`](https://github.com/gabime/spdlog/blob/main/rotating_file_sink-inl.h) (lines 13-66) |

## Thread Safety and Sink Variants

All three sink types are available in multithreaded (`*_mt`) and single-threaded (`*_st`) variants, derived from `base_sink<Mutex>` as shown by the `using` declarations at the bottom of each header file. The multithreaded versions provide internal locking for concurrent access, while single-threaded variants offer lower overhead when logging from one thread.

## Practical Configuration Examples

Create a daily logger that rotates at 02:30 AM and keeps the last 7 days:

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

auto logger = spdlog::daily_logger_mt(
    "daily_logger",               // logger name
    "logs/myapp.log",             // base filename
    2, 30,                        // rotate at 02:30
    false,                        // do not truncate existing file
    7);                           // keep 7 most recent daily files
logger->info("Application started");

```

Create a size-based rotating logger that caps files at 5 MiB and retains 5 backups:

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

auto logger = spdlog::rotating_logger_mt(
    "rot_logger",                 // logger name
    "logs/myapp.log",             // base filename
    5 * 1024 * 1024,              // 5 MiB max size
    5);                           // keep 5 rotated files
logger->info("Application started");

```

Using the single-threaded daily variant for low-contention scenarios:

```cpp
auto logger = spdlog::daily_logger_st(
    "daily_st", "logs/st_app.log", 0, 0, false, 3);

```

## Summary

- **`rotating_file_sink`** manages disk space by enforcing a maximum file size and maintaining a fixed number of indexed backups, suitable for environments with strict storage quotas.
- **`daily_file_sink`** partitions logs by calendar date, creating new files at a specific time daily and optionally cleaning up old files via the `max_files` parameter.
- **`hourly_file_sink`** provides the same retention benefits as the daily sink but rotates every hour, ideal for applications generating high log volumes that require granular time-based separation.
- All sinks support optional file count limits and offer both multithreaded and single-threaded compilation modes through template specializations of `base_sink`.

## Frequently Asked Questions

### When should I use the hourly sink instead of the daily sink?

**Use `hourly_file_sink` when a single day’s log output exceeds manageable file sizes or when operational analysis requires isolating events to specific hours.** Because it generates a new file every 60 minutes using the same retention logic as the daily variant, it prevents individual log files from growing too large while maintaining chronological organization.

### Can I combine size-based and time-based rotation in one sink?

**spdlog does not provide a built-in sink that combines both triggers.** You must choose either `rotating_file_sink` for size-driven rotation or `daily_file_sink`/`hourly_file_sink` for time-driven rotation. If both constraints are required, you would need to implement a custom sink inheriting from `base_sink` or manage log rotation externally via system tools.

### What happens when the `max_files` limit is reached?

**The oldest file is automatically deleted to maintain the specified count.** In `rotating_file_sink`, the `rotate_()` method shifts indices and removes the file exceeding the limit. In `daily_file_sink`, the `delete_old_()` method scans for dated files older than the retention window and removes them from the filesystem.

### Are these file sinks thread-safe?

**Yes, the multithreaded variants (`*_mt`) are thread-safe.** They inherit from `base_sink<std::mutex>` and use internal locking to protect concurrent writes. For single-threaded applications, use the `*_st` variants (inheriting from `base_sink<spdlog::details::null_mutex>`) to eliminate locking overhead.