# How to Use spdlog Qt Sinks for GUI Logging: A Complete Guide

> Integrate spdlog Qt sinks for thread-safe GUI logging. Redirect logs to QTextEdit, QPlainTextEdit, or custom QObject slots with this comprehensive guide.

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

---

**spdlog Qt sinks provide thread-safe integration with Qt widgets, allowing you to redirect log messages directly to `QTextEdit`, `QPlainTextEdit`, or custom `QObject` slots via `QMetaObject::invokeMethod`.**

The `gabime/spdlog` library offers specialized **spdlog Qt sinks** that bridge high-performance C++ logging with Qt's GUI framework. These sinks, defined in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h), enable real-time log display in Qt applications by dispatching formatted messages to the GUI thread through Qt's meta-object system.

## What Are spdlog Qt Sinks?

spdlog provides two Qt-specific sink implementations that inherit from `spdlog::sinks::base_sink<Mutex>`:

- **`qt_sink`**: Sends formatted text to any `QObject` implementing a slot (such as `append` or `appendPlainText`). It forwards strings via `QMetaObject::invokeMethod` defined in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h).

- **`qt_color_sink`**: Extends `qt_sink` functionality by applying color formatting based on log levels. It maintains a fixed-size buffer (`max_lines`) and supports UTF-8 strings.

Both come in thread-safety variants: `*_mt` (mutex-protected) for multi-threaded environments and `*_st` (single-threaded) for better performance when thread safety is not required.

## Setting Up Basic Logging to QTextEdit

To create a logger that writes to a `QTextEdit`, use the `qt_logger_mt` factory function. This creates a multithreaded logger that invokes the specified slot on the target widget:

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextEdit edit;
    edit.show();

    // Create a multithreaded logger that appends to QTextEdit::append()
    auto logger = spdlog::qt_logger_mt("qt", &edit, "append");
    logger->set_pattern("%^[%L] %v%$");   // colour tags will be ignored by this sink
    logger->info("Application started");
    logger->warn("This is a warning");
    logger->error("Something went wrong!");

    return a.exec();
}

```

The `qt_logger_mt` function is defined at lines 45-52 of [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h). It instantiates a `qt_sink_mt` internally and wraps it in a `spdlog::logger` using the default `synchronous_factory`.

## Using QPlainTextEdit for Plain Text Output

For plain-text logging without HTML overhead, target `QPlainTextEdit` using the `appendPlainText` slot:

```cpp
QPlainTextEdit *plain = new QPlainTextEdit;
auto logger = spdlog::qt_logger_st("plain", plain, "appendPlainText");
logger->debug("Debug line");

```

This approach avoids the rich-text processing overhead of `QTextEdit::append()`, making it suitable for high-volume logging scenarios.

## Implementing Color-Aware Logging

The `qt_color_sink` applies ANSI color codes from your pattern to Qt's text color API. Use `qt_color_logger_mt` (lines 89-96 in [`qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/qt_sinks.h)) to create a color-aware logger:

```cpp
QTextEdit *edit = new QTextEdit;
int maxLines = 500;               // keep at most 500 lines
auto logger = spdlog::qt_color_logger_mt("colored", edit, maxLines, /*is_utf8=*/true);

// Example pattern – the part inside {} determines colour range
logger->set_pattern("[%L] %v");

// Emit coloured messages
logger->info("Info message");
logger->warn("Warning with {} placeholder");   // {} will be coloured as WARN
logger->error("Error {}!", "failure");        // colored part is "failure!"

```

The color mapping (trace → gray, debug → cyan, info → green, warn → yellow, error → red, critical → white on red) is established in the `qt_color_sink` constructor (lines 80-100 of the header).

## Choosing Thread Safety Variants

spdlog Qt sinks provide explicit thread-safety controls through template specializations:

**`qt_logger_mt` / `qt_color_logger_mt`**: Use `base_sink<std::mutex>` to protect the `sink_it_` method. Essential when worker threads generate log messages.

**`qt_logger_st` / `qt_color_logger_st`**: Use `base_sink<spdlog::details::null_mutex>` for single-threaded scenarios. These variants eliminate mutex overhead but require you to guarantee that only one thread accesses the logger.

```cpp
// Multithreaded (mutex protected) – safe if logger is used from several threads
auto mt_logger = spdlog::qt_logger_mt("mt", &edit);

// Single-threaded – slightly faster, but you must guarantee only one thread uses it
auto st_logger = spdlog::qt_logger_st("st", &edit);

```

The threading model is implemented in [`base_sink.h`](https://github.com/gabime/spdlog/blob/main/base_sink.h) through the `Mutex` template parameter, while the actual GUI dispatch always occurs on the main thread via `QMetaObject::invokeMethod` with `Qt::AutoConnection`.

## Managing Widget Lifetime

The Qt sink does **not** track the lifetime of the target widget. If the widget is destroyed before the logger, undefined behavior occurs. The header issues a warning about this on lines 10-12.

To safely manage lifetimes, ensure the logger is destroyed before the widget:

```cpp
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow() {
        logView = new QTextEdit(this);
        setCentralWidget(logView);
        logger = spdlog::qt_logger_mt("gui", logView);
    }
    ~MainWindow() { spdlog::drop("gui"); }   // destroy logger before the widget
private:
    QTextEdit *logView;
    std::shared_ptr<spdlog::logger> logger;
};

```

Alternatively, pass a permanent `QObject` (such as the main window) and connect a custom slot that forwards to the actual widget, ensuring the receiver object remains valid throughout the logger's lifetime.

## Summary

- **spdlog Qt sinks** live in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h) and provide `qt_sink` and `qt_color_sink` implementations.
- Use factory functions `qt_logger_mt`, `qt_logger_st`, `qt_color_logger_mt`, or `qt_color_logger_st` to create loggers bound to Qt widgets.
- Messages are dispatched to the GUI thread via `QMetaObject::invokeMethod`, ensuring thread-safe UI updates from any thread.
- Choose `*_mt` variants for multi-threaded applications and `*_st` for single-threaded optimization.
- Always ensure the target widget outlives the logger, or explicitly call `spdlog::drop(logger_name)` before widget destruction.

## Frequently Asked Questions

### What is the difference between qt_sink and qt_color_sink?

`qt_sink` forwards raw formatted text to Qt widgets using `QMetaObject::invokeMethod`, while `qt_color_sink` additionally parses `msg.color_range_start` and `msg.color_range_end` from the `spdlog::details::log_msg` structure to apply level-specific colors to the text. The color sink maintains an internal buffer limit (`max_lines`) to prevent unbounded memory growth in long-running GUI applications.

### How do I prevent crashes when my Qt widget is destroyed?

You must ensure the logger is destroyed before its target widget. As noted in lines 10-12 of [`qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/qt_sinks.h), the sink holds a raw pointer to the `QObject` and does not track its lifetime. Call `spdlog::drop(logger_name)` in your widget's destructor or encapsulate the logger in a class that outlives the widget to prevent dangling pointer dereferences.

### Can I use spdlog Qt sinks with custom QObject slots?

Yes. The factory functions accept any slot name as the third parameter. For example, `spdlog::qt_logger_mt("custom", myObject, "customLogSlot")` will invoke `QMetaObject::invokeMethod` on `myObject` with the slot name `"customLogSlot"`, passing the formatted log message as a `QString` parameter. This allows you to implement custom filtering, formatting, or redirection logic in your own `QObject` subclasses.

### Which thread safety variant should I choose for my application?

Use `qt_logger_mt` or `qt_color_logger_mt` when your application logs from multiple threads (such as worker threads or background tasks). These use `std::mutex` to protect the internal `sink_it_` method. Use `qt_logger_st` or `qt_color_logger_st` only when you can guarantee all logging occurs from the main GUI thread, as these variants avoid mutex overhead but are not thread-safe.