# How to Use spdlog File Event Handlers for Open/Close Callbacks

> Leverage spdlog file event handlers to execute custom code before or after log file open/close events. Gain control over your file lifecycle with easy-to-implement callbacks.

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

---

**spdlog file event handlers allow you to register `std::function` callbacks that execute custom code immediately before or after a log file opens or closes, providing hooks into the file lifecycle via the `file_event_handlers` structure.**

The gabime/spdlog library includes a lightweight mechanism for executing custom logic during log file lifecycle events. By implementing **spdlog file event handlers**, applications can inject monitoring, auditing, or resource management code that runs synchronously with file operations. This feature centers on a simple structure defined in [`include/spdlog/common.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/common.h) that all file-based sinks support natively.

## The file_event_handlers Structure

The core of this functionality is the `file_event_handlers` struct defined in **[`include/spdlog/common.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/common.h)** (lines 330-341). It contains four optional `std::function` objects that represent distinct lifecycle hooks:

- **`before_open`** – `void(const filename_t &filename)`  
  Invoked immediately before the file descriptor is opened.

- **`after_open`** – `void(const filename_t &filename, std::FILE *file_stream)`  
  Invoked after the file has been successfully opened, passing the active file stream pointer.

- **`before_close`** – `void(const filename_t &filename, std::FILE *file_stream)`  
  Invoked right before the file stream is closed.

- **`after_close`** – `void(const filename_t &filename)`  
  Invoked after the file descriptor has been closed and invalidated.

```cpp
// include/spdlog/common.h
struct file_event_handlers {
    file_event_handlers()
        : before_open(nullptr), after_open(nullptr),
          before_close(nullptr), after_close(nullptr) {}

    std::function<void(const filename_t &filename)>               before_open;
    std::function<void(const filename_t &filename,
                       std::FILE *file_stream)>                after_open;
    std::function<void(const filename_t &filename,
                       std::FILE *file_stream)>                before_close;
    std::function<void(const filename_t &filename)>               after_close;
};

```

All members initialize to `nullptr`, making them truly optional. When a function is assigned, the internal file management code invokes it at the appropriate moment.

## How File Event Handlers Work Internally

All file-based sinks—including `basic_file_sink`, `rotating_file_sink`, and `daily_file_sink`—delegate low-level I/O operations to `spdlog::details::file_helper`. According to the spdlog source code, when a sink constructs its `file_helper`, it forwards the user-supplied `file_event_handlers` to the helper's constructor:

```cpp
// From basic_file_sink-inl.h (constructor implementation)
file_helper_{event_handlers}

```

The `file_helper` class, declared in [`include/spdlog/details/file_helper.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/file_helper.h) and implemented in **[`include/spdlog/details/file_helper-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/file_helper-inl.h)**, stores these handlers and invokes them within its `open()` and `close()` methods.

In `file_helper::open()` (lines 21-41), the implementation checks and calls the handlers around the actual file opening logic:

```cpp
// include/spdlog/details/file_helper-inl.h
void file_helper::open(const filename_t &fname, bool truncate) {
    // ... validation logic ...
    
    if (event_handlers_.before_open) {
        event_handlers_.before_open(filename_);
    }
    
    // ... file opening via os::fopen_s ...
    
    if (!os::fopen_s(&fd_, fname, mode)) {
        if (event_handlers_.after_open) {
            event_handlers_.after_open(filename_, fd_);
        }
        return;
    }
    // ... error handling ...
}

```

Similarly, the `close()` method (lines 84-95) wraps the `std::fclose` call with the before and after hooks:

```cpp
// include/spdlog/details/file_helper-inl.h
void file_helper::close() {
    if (fd_ != nullptr) {
        if (event_handlers_.before_close) {
            event_handlers_.before_close(filename_, fd_);
        }
        std::fclose(fd_);
        fd_ = nullptr;
        if (event_handlers_.after_close) {
            event_handlers_.after_close(filename_);
        }
    }
}

```

Because every file sink uses `file_helper` for I/O, they automatically inherit support for these callbacks without requiring additional implementation code.

## Implementing spdlog File Event Handlers in Practice

To use file event handlers, instantiate a `file_event_handlers` object, assign lambda functions or callable objects to the relevant members, and pass the structure to your chosen sink's constructor. The test suite in **[`tests/test_custom_callbacks.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_custom_callbacks.cpp)** demonstrates this pattern:

```cpp
// tests/test_custom_callbacks.cpp
spdlog::file_event_handlers handlers;
handlers.before_open = [](const spdlog::filename_t &f) {
    std::cout << "About to open: " << f << '\n';
};
handlers.after_open = [](const spdlog::filename_t &f, std::FILE *) {
    std::cout << "Opened successfully: " << f << '\n';
};
handlers.before_close = [](const spdlog::filename_t &f, std::FILE *) {
    std::cout << "Closing: " << f << '\n';
};
handlers.after_close = [](const spdlog::filename_t &f) {
    std::cout << "Closed: " << f << '\n';
};

auto sink = std::make_shared<spdlog::sinks::basic_file_sink_st>(
    "mylog.txt", false, handlers);
spdlog::logger logger("file_logger", {sink});
logger.info("Hello world");
spdlog::drop_all();   // triggers the close callbacks

```

When executed, this code prints diagnostic messages bracketing each file operation. The handlers can capture external variables, enabling complex interactions with application state, metrics systems, or security audit trails.

## Common Use Cases for File Event Callbacks

**spdlog file event handlers** are particularly valuable in production environments requiring visibility into logging infrastructure:

- **Rotation Monitoring** – Track exactly when `rotating_file_sink` or `daily_file_sink` closes one file and opens another, enabling you to trigger log shipping or archival processes immediately upon file close.

- **External System Notifications** – Notify monitoring services or watchdog processes when log files are created, ensuring downstream consumers can begin tailing new files without polling.

- **Security Auditing** – Record precise timestamps and process IDs in a separate audit log whenever sensitive log files are opened, satisfying compliance requirements for access tracking.

- **Resource Management** – Flush external buffers, sync filesystem caches, or update database records immediately before a file closes, ensuring data consistency across related subsystems.

## Summary

- The `file_event_handlers` structure in [`include/spdlog/common.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/common.h) defines four optional callbacks: `before_open`, `after_open`, `before_close`, and `after_close`.
- All file-based sinks automatically support these handlers through the `file_helper` class implemented in [`include/spdlog/details/file_helper-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/file_helper-inl.h).
- Callbacks receive the filename and (for open/close operations) the active `std::FILE*` stream pointer.
- You register handlers by passing a configured `file_event_handlers` object to sink constructors such as `basic_file_sink` or `rotating_file_sink`.
- This mechanism enables audit trails, external monitoring, and resource synchronization without modifying spdlog's internals.

## Frequently Asked Questions

### How do I register a callback for when a log file opens in spdlog?

Create a `spdlog::file_event_handlers` instance and assign a callable to either `before_open` or `after_open`, then pass the handlers object as the final argument to your file sink constructor. For example, when constructing a `basic_file_sink_st`, include the handlers as the third parameter: `std::make_shared<spdlog::sinks::basic_file_sink_st>("log.txt", false, handlers)`.

### Can I use file event handlers with rotating_file_sink?

Yes. The `rotating_file_sink` constructor accepts a `file_event_handlers` parameter and forwards it to the internal `file_helper`. This allows your callbacks to fire during rotation events—`before_close` and `after_close` trigger when the old file closes, while `before_open` and `after_open` fire when the new file is created.

### What parameters do spdlog file event handlers receive?

The `before_open` and `after_close` handlers receive a single `const filename_t &filename` parameter. The `after_open` and `before_close` handlers receive both the filename and a `std::FILE *file_stream` pointer, allowing you to inspect or manipulate the open file descriptor (such as checking file size with `ftell` or manually flushing buffers).

### Are file event handlers thread-safe in spdlog?

The handlers themselves are `std::function` objects stored within the `file_helper`, and spdlog invokes them synchronously during `open()` and `close()` operations. While spdlog's sink implementations protect these calls with mutexes where necessary, the thread safety of your callback code depends entirely on your implementation. Ensure any shared state accessed within your lambdas is properly synchronized, as callbacks may execute on the thread that triggers file rotation or logger destruction.