Integrating spdlog with Qt using qt_sinks: Complete Implementation Guide
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 anyQObjectimplementing a compatible Qt slot (e.g.,QPlainTextEdit::append).qt_color_sink– Writes color-coded text toQTextEdit, supporting per-level color customization viaQTextCharFormat.
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:
#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:
// 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():
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:
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:
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, the constructor guards against null objects:
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_ptror parent-child relationships - Explicitly call
logger->flush()and reset the logger (logger.reset()) before destroying the Qt widget - Use
QPointerto monitor widget validity if implementing custom sink wrappers
Summary
qt_sinkandqt_color_sinkininclude/spdlog/sinks/qt_sinks.hprovide native Qt integration for spdlog- Factory functions
qt_logger_mt()andqt_logger_st()simplify instantiation with proper thread-safety guarantees - Internal implementation uses
QMetaObject::invokeMethodwithQt::AutoConnectionfor automatic thread marshaling - The
meta_methodparameter requires exact Qt slot signatures like"append(const QString&)" - Widget lifetime must exceed logger lifetime to avoid
spdlog_exexceptions or dangling pointers - Color customization via
set_color()acceptsQTextCharFormatobjects 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →