# How to Use spdlog with Qt Widgets for GUI Logging

> Learn to use spdlog with Qt widgets for GUI logging. Integrate spdlog's qt_sink or qt_color_sink with QTextEdit for thread-safe log updates on the main thread.

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

---

**Use spdlog's `qt_sink` or `qt_color_sink` classes from [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h) to forward log messages directly to `QTextEdit` or `QPlainTextEdit` widgets via Qt's meta-object system, ensuring thread-safe GUI updates always occur on the main thread.**

The **gabime/spdlog** library provides dedicated Qt integration sinks that bridge high-performance C++ logging with graphical user interfaces. When you use spdlog with Qt widgets, log messages are formatted and dispatched through `QMetaObject::invokeMethod`, guaranteeing that GUI updates happen safely within Qt's event loop regardless of which thread generated the log entry.

## Qt Sink Types and Thread Safety

spdlog implements two specialized sinks for Qt applications in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h):

- **`qt_sink`** – Targets `QTextEdit`, `QPlainTextEdit`, or any `QObject` with a compatible slot. It forwards formatted text via the default `append` or `appendPlainText` slots.
- **`qt_color_sink`** – Extends `qt_sink` with colorized output support for `QTextEdit` only, applying different `QTextCharFormat` styles based on log levels and optionally truncating old lines to maintain performance.

Both sinks inherit from `spdlog::sinks::base_sink<Mutex>`, making them compatible with spdlog's entire formatter ecosystem. The template parameter `Mutex` determines thread safety: use `std::mutex` for multi-threaded applications or `spdlog::details::null_mutex` for single-threaded contexts where you want to avoid locking overhead.

## Implementation Details

### Thread Safety and Mutex Selection

The sinks use the `Mutex` template parameter to protect internal state. When you create a logger via `qt_logger_mt`, the sink instantiates with `std::mutex`, allowing concurrent logging from multiple threads. For GUI applications where all logging occurs on the main thread, `qt_logger_st` uses `null_mutex` for zero-overhead synchronization.

### Message Delivery via QMetaObject

In `qt_sink::sink_it_`, the sink first applies the formatter to convert the log message into a `QString`. It then uses `QMetaObject::invokeMethod` with `Qt::AutoConnection` to emit the string to the widget's slot specified by `meta_method_`. This mechanism ensures that even if a background thread calls `spdlog::info()`, the actual GUI update executes on the main thread, preventing race conditions and UI corruption.

### Color Handling and Line Limits

The `qt_color_sink` class extracts color ranges from `spdlog::details::log_msg` when color formatting is enabled. It constructs payloads in either UTF-8 or Latin-1 encoding and invokes a static helper method that inserts text with the appropriate `QTextCharFormat`. Additionally, the sink enforces a configurable maximum line count, automatically removing the oldest `QTextBlock` entries when the limit is exceeded to prevent unbounded memory growth in long-running applications.

## Code Examples

### Basic QTextEdit Logging

The simplest integration uses `qt_logger_mt` to create a multi-threaded logger that appends to a `QTextEdit`:

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

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

    // Create multi-threaded logger targeting QTextEdit::append
    auto logger = spdlog::qt_logger_mt("qt", &editor);
    spdlog::set_default_logger(logger);
    spdlog::set_level(spdlog::level::debug);

    spdlog::info("Application started");
    spdlog::debug("Debug value: {}", 42);
    spdlog::error("Critical failure!");

    return app.exec();
}

```

### QPlainTextEdit Integration

For plain text widgets, specify the `appendPlainText` slot explicitly:

```cpp
QPlainTextEdit *plain = new QPlainTextEdit;
auto logger = spdlog::qt_logger_mt("plain", plain, "appendPlainText");

```

### Colorized Logging with Line Limits

Use `qt_color_logger_mt` to enable syntax highlighting and prevent memory leaks in high-volume logging:

```cpp
QTextEdit *colored = new QTextEdit;
int maxLines = 500;  // Retain only the most recent 500 lines

auto logger = spdlog::qt_color_logger_mt("colored", colored, maxLines, true);
// Third parameter enables UTF-8 support

logger->info("System initialized");
logger->warn("Low memory: {}%", 15);
logger->error("Connection refused");

```

### Custom Slot Receivers

You can route logs to custom `QObject` slots for filtering, timestamping, or network forwarding:

```cpp
class LogInterceptor : public QObject {
    Q_OBJECT
public slots:
    void handleLog(const QString &msg) {
        // Prepend timestamp or filter sensitive data
        QString enriched = QDateTime::currentDateTime().toString() + " " + msg;
        textEdit->append(enriched);
    }
};

LogInterceptor interceptor;
auto logger = spdlog::qt_logger_mt("custom", &interceptor, "handleLog");

```

## Summary

- **Include [`qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/qt_sinks.h)** from the spdlog repository to access Qt-specific sinks.
- **Choose the appropriate mutex type**: `qt_logger_mt` for thread-safe logging or `qt_logger_st` for single-threaded performance.
- **Use `qt_sink`** for `QTextEdit` and `QPlainTextEdit`, or **`qt_color_sink`** for colorized output with automatic line truncation.
- **Leverage `QMetaObject::invokeMethod`** implicitly through these sinks to guarantee main-thread GUI updates from any thread context.
- **Specify custom slots** when you need to preprocess messages before displaying them in the widget.

## Frequently Asked Questions

### Does spdlog block the GUI thread when logging?

No, spdlog's Qt sinks use `QMetaObject::invokeMethod` with automatic connection types, which queues the update to the main thread's event loop rather than blocking the caller. Even when logging from worker threads, the actual widget update happens asynchronously on the GUI thread.

### Can I use spdlog with Qt widgets in a multi-threaded application?

Yes, use the `*_mt` factory functions (such as `qt_logger_mt` or `qt_color_logger_mt`) which create sinks protected by `std::mutex`. According to the source code in [`include/spdlog/sinks/qt_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/qt_sinks.h), these variants inherit from `base_sink<std::mutex>` and are safe for concurrent access from multiple threads.

### How do I limit the number of lines in a Qt log widget?

Use `qt_color_sink` or `qt_color_logger_mt` and specify the `max_lines` parameter. The sink automatically removes the oldest `QTextBlock` entries when the count exceeds your threshold, preventing the `QTextEdit` from consuming unbounded memory during extended operation.

### Is it possible to log to multiple targets including a Qt widget?

Yes, create a logger with multiple sinks. You can combine `qt_sink` with file sinks, console sinks, or network sinks using `spdlog::sinks::dist_sink` or by passing a vector of sinks to the logger constructor. Each sink receives the formatted message independently, allowing simultaneous file and GUI logging.