# Integrating spdlog with Qt using qt_sinks: Complete Implementation Guide

> Implement spdlog with Qt using qt_sinks for thread-safe UI updates. Learn how to forward logs to Qt widgets from background threads with this complete guide.

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

---

**Use `spdlog::qt_logger_mt()` or `spdlog::qt_color_logger_mt()` from `<spdlog/sinks/qt_sinks.h>` to forward log messages directly to Qt widgets via `QMetaObject::invokeMethod`, ensuring thread-safe UI updates even when logging from background threads.**

Integrating spdlog with Qt using qt_sinks bridges high-performance C++ logging with native Qt GUI applications. The **gabime/spdlog** repository provides specialized sinks that leverage Qt's meta-object system to marshal log calls across thread boundaries, eliminating the need for custom queue management when updating `QTextEdit` or `QPlainTextEdit` widgets from worker threads.

## How qt_sinks Work Internally

The Qt sinks derive from `spdlog::sinks::base_sink<Mutex>`, inheriting the same thread-safety policies as standard spdlog sinks. Two primary implementations exist:

- **`qt_sink`** – Forwards plain text to any `QObject` implementing a compatible Qt slot (e.g., `QPlainTextEdit::append`).
- **`qt_color_sink`** – Writes color-coded text to `QTextEdit`, supporting per-level color customization via `QTextCharFormat`.

Both sinks use `QMetaObject::invokeMethod` with **Qt::AutoConnection** to ensure log messages execute on the thread owning the target widget. This automatic marshaling prevents race conditions when background threads generate log events that must update the main GUI thread.

## Step-by-Step Implementation

### 1. Include the Qt Sinks Header

Add the Qt-specific sink definitions to your compilation unit:

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

```

### 2. Create a Logger Using Factory Functions

Spdlog provides convenience factory functions that instantiate loggers pre-configured with Qt sinks. Choose between multi-threaded (`*_mt`) or single-threaded (`*_st`) variants based on your thread safety requirements:

```cpp
// Multi-threaded logger feeding a QPlainTextEdit
auto logger = spdlog::qt_logger_mt(
    "gui_logger", 
    plainTextEdit, 
    "append(const QString&)"
);

// Single-threaded variant for single-threaded contexts
auto logger_st = spdlog::qt_logger_st(
    "gui_logger_st", 
    textEdit, 
    "append(const QString&)"
);

```

The `meta_method` parameter must specify the fully-qualified Qt slot signature, such as `"append(const QString&)"` for standard text editing widgets.

### 3. Configure Color Output (qt_color_sink only)

For colored output, use `qt_color_logger_mt` or instantiate `qt_color_sink` directly. Customize appearance per log level using `set_color()`:

```cpp
auto color_logger = spdlog::qt_color_logger_mt(
    "color_logger", 
    textEdit, 
    1000  // max_buffer_size
);

// Customize warning level color
QTextCharFormat warn_format;
warn_format.setForeground(Qt::darkYellow);
color_logger->set_color(spdlog::level::warn, warn_format);

```

## Complete Code Examples

### Plain Text Logging to QPlainTextEdit

This example creates a multi-threaded logger that appends plain text to a `QPlainTextEdit` widget:

```cpp
QPlainTextEdit *log_view = new QPlainTextEdit(parent);
auto logger = spdlog::qt_logger_mt(
    "qt_logger", 
    log_view, 
    "append(const QString&)"
);

logger->set_level(spdlog::level::debug);
logger->info("Application started successfully");

```

### Color-Coded Logging to QTextEdit

This implementation uses `qt_color_sink` to display warnings in yellow and errors in red:

```cpp
QTextEdit *color_log_view = new QTextEdit(parent);
auto color_logger = spdlog::qt_color_logger_mt(
    "qt_color_logger", 
    color_log_view, 
    1000
);

// Configure warning appearance
QTextCharFormat warn_fmt;
warn_fmt.setForeground(Qt::darkYellow);
color_logger->set_color(spdlog::level::warn, warn_fmt);

color_logger->warn("Disk space running low");

```

## Lifetime Management and Thread Safety

Because `qt_sink` stores a raw `QObject*` pointer without maintaining a hard reference, you must ensure the target widget outlives the logger. According to the implementation in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h), the constructor guards against null objects:

```cpp
if (!qt_object_) {
    throw_spdlog_ex("qt_sink: qt_object is null");
}

```

If the widget is destroyed while the logger remains active, subsequent log calls may crash or throw `spdlog_ex` exceptions. Best practices include:

- Tie the logger's lifetime to the widget's lifetime using `std::shared_ptr` or parent-child relationships
- Explicitly call `logger->flush()` and reset the logger (`logger.reset()`) before destroying the Qt widget
- Use `QPointer` to monitor widget validity if implementing custom sink wrappers

## Summary

- **`qt_sink`** and **`qt_color_sink`** in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h) provide native Qt integration for spdlog
- Factory functions `qt_logger_mt()` and `qt_logger_st()` simplify instantiation with proper thread-safety guarantees
- Internal implementation uses `QMetaObject::invokeMethod` with `Qt::AutoConnection` for automatic thread marshaling
- The `meta_method` parameter requires exact Qt slot signatures like `"append(const QString&)"`
- Widget lifetime must exceed logger lifetime to avoid `spdlog_ex` exceptions or dangling pointers
- Color customization via `set_color()` accepts `QTextCharFormat` objects for rich text styling

## Frequently Asked Questions

### How do I safely log to the Qt GUI from a background thread?

The qt_sinks automatically handle cross-thread communication using `QMetaObject::invokeMethod` with `Qt::AutoConnection`. When you log from a background thread, the sink marshals the message to the main GUI thread where the widget update executes safely. Use the `*_mt` factory functions if multiple threads will call the logger simultaneously.

### Which Qt widgets work with qt_sinks?

Any `QObject` exposing a slot that accepts `const QString&` (or compatible parameter) works with `qt_sink`. Common choices include `QPlainTextEdit` (using `"append(const QString&)"`), `QTextEdit`, or custom `QObject` subclasses with user-defined slots. The `qt_color_sink` specifically requires `QTextEdit` to support color formatting via `QTextCharFormat`.

### How do I change text colors for different log levels?

Call `set_color(spdlog::level::level_enum, QTextCharFormat)` on a `qt_color_sink` instance. Create a `QTextCharFormat` object, set its foreground color using `setForeground()`, and pass it to the sink. This affects only future log messages; previously displayed text retains its original formatting.

### What happens if my Qt widget is destroyed before the logger?

The sink throws a `spdlog_ex` exception during construction if the provided `QObject*` is null. If the widget is destroyed after logger creation, subsequent log calls may dereference invalid memory. Always ensure the logger is destroyed or reset before the target widget, or implement a wrapper that checks `QPointer` validity before invoking the sink.