How to Integrate spdlog with Qt Using qt_sinks: A Complete Guide

Use spdlog's qt_sinks.h header to create thread-safe loggers that forward messages to Qt widgets via QMetaObject::invokeMethod, ensuring UI updates occur on the correct thread even when logging from background workers.

The gabime/spdlog library provides dedicated Qt integration through the qt_sinks module, allowing you to stream high-performance log output directly into QPlainTextEdit or QTextEdit widgets. This integration leverages Qt's meta-object system to marshal log calls across threads safely, making it straightforward to integrate spdlog with Qt using qt_sinks while maintaining responsive user interfaces.

Prerequisites and Header Inclusion

To begin, include the Qt-specific sink header in your source file. This header declares both sink types and the convenience factory functions for logger creation.

#include <spdlog/sinks/qt_sinks.h>

Ensure your build system links against Qt (typically Qt5 or Qt6) and that the spdlog include directory is in your compiler's search path.

Understanding qt_sinks Types

The include/spdlog/sinks/qt_sinks.h file defines two specialized sinks that derive from spdlog::sinks::base_sink<Mutex>, inheriting the same thread-safety policies as standard spdlog sinks.

qt_sink for Plain Text Widgets

The qt_sink writes plain text to any QObject that implements a compatible Qt slot. This sink works with QPlainTextEdit through its append(const QString&) slot, or any custom QObject exposing a similar signature.

qt_color_sink for Rich Text Output

The qt_color_sink targets QTextEdit widgets and supports per-log-level color customization. It inserts colored text using QTextCharFormat objects, allowing you to visually distinguish between debug, warning, and error messages in the same widget.

Creating a Qt Logger

Factory Function Signatures

Create loggers using the multi-threaded (*_mt) or single-threaded (*_st) convenience factory functions:

  • spdlog::qt_logger_mt(name, qt_object, meta_method) – Creates a thread-safe logger using qt_sink with mutex protection.
  • spdlog::qt_logger_st(name, qt_object, meta_method) – Creates a single-threaded logger without mutex overhead.
  • spdlog::qt_color_logger_mt(name, qt_object, max_logs) – Creates a colored logger for QTextEdit with a maximum log buffer size.

The qt_object parameter accepts a pointer to your target widget (e.g., QPlainTextEdit*), while meta_method must be the fully-qualified Qt slot signature as a string, such as "append(const QString&)".

Connecting to Widget Slots

The sink stores the QObject* pointer and method name internally. When a log entry arrives, it builds the formatted message using the configured spdlog formatter and invokes the slot via Qt::invokeMethod. This mechanism ensures that even if you log from a background thread, the UI update executes on the main thread that owns the widget.

Thread Safety and Cross-Thread Logging

Both qt_sink and qt_color_sink use Qt::AutoConnection when calling QMetaObject::invokeMethod. This automatically queues the call if the logging thread differs from the widget's thread, preventing race conditions during UI updates. Because the sinks inherit from base_sink<Mutex>, the *_mt variants provide thread-safe logging through standard mutex locking, while *_st variants offer higher performance when logging exclusively from the main thread.

Customizing Colors in qt_color_sink

Customize the appearance of log levels by creating QTextCharFormat objects and applying them to specific levels:

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

QTextCharFormat warnFmt;
warnFmt.setForeground(Qt::darkYellow);
color_logger->set_color(spdlog::level::warn, warnFmt);

The set_color(level, QTextCharFormat) method updates the internal format map used when inserting text into the QTextEdit.

Lifetime Management and Safety

The sink constructor includes a guard clause: if (!qt_object_) throw_spdlog_ex(...). If you pass a null pointer or if the widget is destroyed before the logger, the sink throws a spdlog_ex exception. Because the sink stores a raw pointer rather than a QPointer or hard reference, you must ensure the logger's lifetime is tied to the widget's lifetime, or explicitly reset the logger (e.g., call spdlog::drop("logger_name")) before destroying the target widget to avoid dangling pointers.

Complete Implementation Examples

Basic Plain-Text Integration

#include <spdlog/sinks/qt_sinks.h>
#include <QPlainTextEdit>

// Create widget and logger
QPlainTextEdit *logView = new QPlainTextEdit(parent);
auto logger = spdlog::qt_logger_mt("qt_logger", logView, "append(const QString&)");

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

Colored Logging with Custom Formatting

#include <spdlog/sinks/qt_sinks.h>
#include <QTextEdit>

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

// Configure custom warning color
QTextCharFormat warnFmt;
warnFmt.setForeground(Qt::darkYellow);
warnFmt.setFontWeight(QFont::Bold);
color_logger->set_color(spdlog::level::warn, warnFmt);

color_logger->warn("This warning appears in bold yellow text");

Summary

  • Include spdlog/sinks/qt_sinks.h to access Qt-specific logging functionality.
  • Choose between qt_sink for plain text and qt_color_sink for colored output in QTextEdit widgets.
  • Create loggers using qt_logger_mt or qt_color_logger_mt for thread-safe operation, passing the widget pointer and slot signature.
  • Leverage QMetaObject::invokeMethod with Qt::AutoConnection to automatically marshal log calls to the UI thread.
  • Manage object lifetimes carefully to prevent spdlog_ex exceptions when widgets are destroyed.
  • Customize log appearance using set_color with QTextCharFormat for visual log level distinction.

Frequently Asked Questions

What Qt widgets are compatible with qt_sinks?

The qt_sink works with any QObject exposing a slot that accepts a QString parameter, most commonly QPlainTextEdit and QTextEdit. The qt_color_sink specifically requires QTextEdit because it manipulates QTextCharFormat and rich text cursors.

How does spdlog ensure thread safety when updating Qt GUI elements?

The sinks use QMetaObject::invokeMethod with Qt::AutoConnection, which automatically detects if the logging thread differs from the widget's thread. If a different thread is detected, Qt queues the method call to execute on the widget's owning thread, preventing direct manipulation of GUI elements from worker threads.

Can I use custom slot signatures other than append(const QString&)?

Yes. The meta_method parameter in qt_logger_mt accepts any Qt slot signature that matches your widget's implementation. For example, if you have a custom QObject with a slot void addLogEntry(const QString& text), you would pass "addLogEntry(const QString&)" as the third parameter to the factory function.

Why does my application throw a spdlog_ex when creating the logger?

This exception occurs in the sink constructor if the qt_object pointer is null or if the specified meta_method does not exist on the object. Verify that your widget pointer is valid and that the slot signature exactly matches the Qt meta-object signature, including const qualifiers and reference symbols.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →