From d1eb68154f2a60c7d59e99d1504e6c6ac03fedb4 Mon Sep 17 00:00:00 2001 From: gabime Date: Sun, 28 May 2023 12:52:56 +0300 Subject: [PATCH 01/31] If exceptions are disabled, disable them in the bundled fmt as well --- CMakeLists.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3830dfe2..7ae5a4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,10 +255,19 @@ foreach( endif() endforeach() -if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC) - target_compile_options(spdlog PRIVATE -fno-exceptions) +# --------------------------------------------------------------------------------------- +# If exceptions are disabled, disable them in the bundled fmt as well +# --------------------------------------------------------------------------------------- +if(SPDLOG_NO_EXCEPTIONS) + if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + target_compile_definitions(spdlog PUBLIC FMT_EXCEPTIONS=0) + endif() + if(NOT MSVC) + target_compile_options(spdlog PRIVATE -fno-exceptions) + else() + target_compile_options(spdlog PRIVATE /EHsc) + endif() endif() - # --------------------------------------------------------------------------------------- # Build binaries # --------------------------------------------------------------------------------------- From 31cf79a70dfa61a4b2b7509eecca6a3b269bfc4a Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 30 May 2023 20:38:30 +0300 Subject: [PATCH 02/31] Remov foreward to standard vformat_to --- include/spdlog/logger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 825f5d06..abda5f4e 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -369,7 +369,7 @@ protected: { memory_buf_t buf; #ifdef SPDLOG_USE_STD_FORMAT - fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward(args)...)); + fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(args...)); #else fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(args...)); #endif @@ -395,7 +395,7 @@ protected: // format to wmemory_buffer and convert to utf8 wmemory_buf_t wbuf; fmt_lib::vformat_to( - std::back_inserter(wbuf), fmt, fmt_lib::make_format_args(std::forward(args)...)); + std::back_inserter(wbuf), fmt, fmt_lib::make_format_args(args...)); memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); From 32701af60bbf0cde6c9e69a5aff4517cf89e85f7 Mon Sep 17 00:00:00 2001 From: Ulmo-F Date: Tue, 6 Jun 2023 18:53:10 +0200 Subject: [PATCH 03/31] qt_sink: add some warning on its usage (#2753) * qt_sink: add some warning on its usage * qt_sink: add some warning on its usage - fix --------- Co-authored-by: Benoit FANCHON --- include/spdlog/sinks/qt_sinks.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 31b49c60..4f41728b 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -55,25 +55,35 @@ private: #include "spdlog/details/null_mutex.h" #include +// Warning: qt_sink won't be notified if the target QObject is destroyed. +// If the object's lifetime can be shorter than the logger's one, you should provide some permanent object and then use a standard signal/slot. using qt_sink_mt = qt_sink; +// Warning: qt_sink won't be notified if the target QObject is destroyed. +// If the object's lifetime can be shorter than the logger's one, you should provide some permanent object and then use a standard signal/slot. using qt_sink_st = qt_sink; } // namespace sinks // // Factory functions // +// Warning: the qt_sink won't be notified if the target widget is destroyed. +// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { return Factory::template create(logger_name, qt_object, meta_method); } +// Warning: the qt_sink won't be notified if the target widget is destroyed. +// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { return Factory::template create(logger_name, qt_object, meta_method); } +// Warning: the qt_sink won't be notified if the target QObject is destroyed. +// If the object's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_mt( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -81,6 +91,8 @@ inline std::shared_ptr qt_logger_mt( return Factory::template create(logger_name, qt_object, meta_method); } +// Warning: the qt_sink won't be notified if the target widget is destroyed. +// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_st( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -88,12 +100,16 @@ inline std::shared_ptr qt_logger_st( return Factory::template create(logger_name, qt_object, meta_method); } +// Warning: the qt_sink won't be notified if the target QObject is destroyed. +// If the objet's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { return Factory::template create(logger_name, qt_object, meta_method); } +// Warning: the qt_sink won't be notified if the target QObject is destroyed. +// If the objet's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { From 8fdcf0365be32b145a54d0a6efe22434ab9463af Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 6 Jun 2023 19:57:09 +0300 Subject: [PATCH 04/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 4f41728b..85f4f21a 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -7,6 +7,10 @@ // Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser... // etc) Building and using requires Qt library. // +// Warning: the qt_sink won't be notified if the target widget is destroyed. +// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, +// and then use a standard signal/slot. +// #include "spdlog/common.h" #include "spdlog/details/log_msg.h" @@ -66,24 +70,16 @@ using qt_sink_st = qt_sink; // // Factory functions // -// Warning: the qt_sink won't be notified if the target widget is destroyed. -// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { return Factory::template create(logger_name, qt_object, meta_method); } -// Warning: the qt_sink won't be notified if the target widget is destroyed. -// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_method); -} - -// Warning: the qt_sink won't be notified if the target QObject is destroyed. -// If the object's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. + return Factory::template create(logger_name, qt_object, meta_ template inline std::shared_ptr qt_logger_mt( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -91,8 +87,6 @@ inline std::shared_ptr qt_logger_mt( return Factory::template create(logger_name, qt_object, meta_method); } -// Warning: the qt_sink won't be notified if the target widget is destroyed. -// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_st( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -100,16 +94,10 @@ inline std::shared_ptr qt_logger_st( return Factory::template create(logger_name, qt_object, meta_method); } -// Warning: the qt_sink won't be notified if the target QObject is destroyed. -// If the objet's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { - return Factory::template create(logger_name, qt_object, meta_method); -} - -// Warning: the qt_sink won't be notified if the target QObject is destroyed. -// If the objet's lifetime can be shorter than the logger's one, you should provide some permanent QObject and then use a standard signal/slot. + return Factory::template create(logger_name, qt_object, meta_ template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { From 931cd2fb5454a47ba0c5815c54a962c2cba4c316 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 6 Jun 2023 19:58:26 +0300 Subject: [PATCH 05/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 85f4f21a..c31c7ced 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -59,11 +59,8 @@ private: #include "spdlog/details/null_mutex.h" #include -// Warning: qt_sink won't be notified if the target QObject is destroyed. -// If the object's lifetime can be shorter than the logger's one, you should provide some permanent object and then use a standard signal/slot. -using qt_sink_mt = qt_sink; -// Warning: qt_sink won't be notified if the target QObject is destroyed. -// If the object's lifetime can be shorter than the logger's one, you should provide some permanent object and then use a standard signal/slot. + +using qt_sink_mt = qt_sink using qt_sink_st = qt_sink; } // namespace sinks From 3a6ee663baefbfc1348a974cbf5562423292bb16 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 6 Jun 2023 20:06:45 +0300 Subject: [PATCH 06/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index c31c7ced..feef302e 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -60,7 +60,7 @@ private: #include "spdlog/details/null_mutex.h" #include -using qt_sink_mt = qt_sink +using qt_sink_mt = qt_sink; using qt_sink_st = qt_sink; } // namespace sinks From 230cad163da0b251e38134fcf94e5118e60b94b1 Mon Sep 17 00:00:00 2001 From: gabime Date: Tue, 6 Jun 2023 20:23:37 +0300 Subject: [PATCH 07/31] Fixed qt_sink --- include/spdlog/sinks/qt_sinks.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index feef302e..d61c6bec 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -59,7 +59,6 @@ private: #include "spdlog/details/null_mutex.h" #include - using qt_sink_mt = qt_sink; using qt_sink_st = qt_sink; } // namespace sinks @@ -76,7 +75,9 @@ inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTex template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_ + return Factory::template create(logger_name, qt_object, meta_method); +} + template inline std::shared_ptr qt_logger_mt( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -94,7 +95,9 @@ inline std::shared_ptr qt_logger_st( template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { - return Factory::template create(logger_name, qt_object, meta_ + return Factory::template create(logger_name, qt_object, meta_method); +} + template inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { From 5384512f25f571392821168c021541e672a9fea1 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 00:19:40 +0300 Subject: [PATCH 08/31] Store MetaMethod object in qt_sink for better performance --- include/spdlog/sinks/qt_sinks.h | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index d61c6bec..e4d03365 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -17,6 +17,7 @@ #include "spdlog/details/synchronous_factory.h" #include "spdlog/sinks/base_sink.h" +#include #include #include @@ -29,10 +30,17 @@ template class qt_sink : public base_sink { public: - qt_sink(QObject *qt_object, const std::string &meta_method) + qt_sink(QObject *qt_object, const std::string &meta_method_name) { + // store the meta method object for later usage qt_object_ = qt_object; - meta_method_ = meta_method; + const QMetaObject *metaobject = qt_object_->metaObject(); + qt_object_->dumpObjectInfo(); + int methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); + if (methodIndex == -1) { + throw_spdlog_ex("qt_sink: qt_object does not have meta_method " + meta_method_name); + } + meta_method_ = metaobject->method(methodIndex); } ~qt_sink() @@ -46,15 +54,15 @@ protected: memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); string_view_t str = string_view_t(formatted.data(), formatted.size()); - QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, - Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); + auto payload = QString::fromUtf8(str.data(), static_cast(str.size())).trimmed(); + meta_method_.invoke(qt_object_, Qt::AutoConnection, Q_ARG(QString, payload)); } void flush_() override {} private: QObject *qt_object_ = nullptr; - std::string meta_method_; + QMetaMethod meta_method_; }; #include "spdlog/details/null_mutex.h" @@ -67,27 +75,27 @@ using qt_sink_st = qt_sink; // Factory functions // template -inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") +inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } template -inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") +inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } template inline std::shared_ptr qt_logger_mt( - const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } template inline std::shared_ptr qt_logger_st( - const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } From 0f50ad92d6a526f4bbf51bb32443677bd63d01e6 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 00:21:44 +0300 Subject: [PATCH 09/31] Clean qt_sink code --- include/spdlog/sinks/qt_sinks.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index e4d03365..8e5ff20d 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -34,8 +34,7 @@ public: { // store the meta method object for later usage qt_object_ = qt_object; - const QMetaObject *metaobject = qt_object_->metaObject(); - qt_object_->dumpObjectInfo(); + auto *metaobject = qt_object_->metaObject(); int methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); if (methodIndex == -1) { throw_spdlog_ex("qt_sink: qt_object does not have meta_method " + meta_method_name); From 1f1897e3a46fde27358b4777ae29b2b4a300a971 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 00:21:58 +0300 Subject: [PATCH 10/31] Clean qt_sink code --- include/spdlog/sinks/qt_sinks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 8e5ff20d..c5cb8d64 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -35,7 +35,7 @@ public: // store the meta method object for later usage qt_object_ = qt_object; auto *metaobject = qt_object_->metaObject(); - int methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); + auto methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); if (methodIndex == -1) { throw_spdlog_ex("qt_sink: qt_object does not have meta_method " + meta_method_name); } From 6940f4fd46462bae41811d4e337e6f1b6b7ecf6c Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 00:29:23 +0300 Subject: [PATCH 11/31] Added some comments to qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index c5cb8d64..435e7d35 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -4,9 +4,10 @@ #pragma once // -// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser... -// etc) Building and using requires Qt library. +// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser...etc) +// Building and using requires Qt library. // + // Warning: the qt_sink won't be notified if the target widget is destroyed. // If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, // and then use a standard signal/slot. @@ -30,6 +31,8 @@ template class qt_sink : public base_sink { public: + // qt object is the object that receives the log messages (e.g QPlainTextEdit or QTextEdit) + // meta_method_name is the name of the slot to be called on the qt_object for every log message (e.g "append(QString)"). qt_sink(QObject *qt_object, const std::string &meta_method_name) { // store the meta method object for later usage From 6a96c7f902b7a12342e1104d4e73203dc74ea3e7 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 11:45:51 +0300 Subject: [PATCH 12/31] Added qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 112 +++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 435e7d35..6e6f156b 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -18,9 +18,12 @@ #include "spdlog/details/synchronous_factory.h" #include "spdlog/sinks/base_sink.h" +#include + #include #include #include +#include // // qt_sink class @@ -39,7 +42,8 @@ public: qt_object_ = qt_object; auto *metaobject = qt_object_->metaObject(); auto methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); - if (methodIndex == -1) { + if (methodIndex == -1) + { throw_spdlog_ex("qt_sink: qt_object does not have meta_method " + meta_method_name); } meta_method_ = metaobject->method(methodIndex); @@ -67,15 +71,106 @@ private: QMetaMethod meta_method_; }; + +// color sink to QTextEdit. Color location is determined by the sink log pattern +// Note: Only ascii (latin1) is supported by this sink. +template +class qt_color_sink : public base_sink +{ +public: + qt_color_sink(QTextEdit *qt_text_edit) : qt_text_edit_(qt_text_edit) + { + if(!qt_text_edit_) + { + throw_spdlog_ex("qt_color_text_sink: text_edit is null"); + } + // set default colors + QTextCharFormat format; + // trace + format.setForeground(Qt::gray); + colors_.at(level::trace) = format; + // debug + format.setForeground(Qt::cyan); + colors_.at(level::debug) = format; + // info + format.setForeground(Qt::green); + colors_.at(level::info) = format; + // warn + format.setForeground(Qt::yellow); + colors_.at(level::warn) = format; + // err + format.setForeground(Qt::red); + colors_.at(level::err) = format; + // critical + format.setForeground(Qt::white); + format.setBackground(Qt::red); + colors_.at(level::critical) = format; + } + + ~qt_color_sink() + { + flush_(); + } + + void set_level_format(level::level_enum color_level, QTextCharFormat format) + { + colors_.at(static_cast(color_level)) = format; + } + + QTextCharFormat& get_level_format(level::level_enum color_level) + { + return colors_.at(static_cast(color_level)); + } +protected: + void sink_it_(const details::log_msg &msg) override + { + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + string_view_t str = string_view_t(formatted.data(), formatted.size()); + + // apply the color to the color range in the formatted message. + auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); + if(msg.color_range_end > msg.color_range_start) + { + QTextCursor cursor(qt_text_edit_->document()); + cursor.movePosition(QTextCursor::End); + + // insert the text before the color range + cursor.insertText(payload.left(msg.color_range_start)); + + // insert the colorized text + auto format = get_level_format(msg.level); + cursor.setCharFormat(format); + cursor.insertText(payload.mid(msg.color_range_start, msg.color_range_end - msg.color_range_start)); + + // insert the text after the color range with default format + cursor.setCharFormat(QTextCharFormat()); + cursor.insertText(payload.mid(msg.color_range_end)); + } + else // no color range + { + qt_text_edit_->append(payload.trimmed()); + } + } + + void flush_() override {} + QTextEdit *qt_text_edit_; + std::array colors_; + }; + #include "spdlog/details/null_mutex.h" #include using qt_sink_mt = qt_sink; using qt_sink_st = qt_sink; +using qt_color_sink_mt = qt_color_sink; +using qt_color_sink_st = qt_color_sink; } // namespace sinks // // Factory functions // + +// create logger using QTextEdit object template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") { @@ -88,6 +183,7 @@ inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTex return Factory::template create(logger_name, qt_object, meta_method); } +// create logger using QPlainTextEdit object template inline std::shared_ptr qt_logger_mt( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText(QString)") @@ -102,6 +198,20 @@ inline std::shared_ptr qt_logger_st( return Factory::template create(logger_name, qt_object, meta_method); } +// create color qt logger using QTextEdit object +template +inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_object) +{ + return Factory::template create(logger_name, qt_object); +} + +template +inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_object) +{ + return Factory::template create(logger_name, qt_object); +} + +// create logger with other QObject object template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { From dfcb74b129a88de51bcc13656d366325ddc55506 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 12:51:07 +0300 Subject: [PATCH 13/31] Added default color handling to qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 6e6f156b..a7fbff05 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -84,7 +84,8 @@ public: { throw_spdlog_ex("qt_color_text_sink: text_edit is null"); } - // set default colors + default_color_ = qt_text_edit_->currentCharFormat(); + // set colors QTextCharFormat format; // trace format.setForeground(Qt::gray); @@ -112,15 +113,30 @@ public: flush_(); } - void set_level_format(level::level_enum color_level, QTextCharFormat format) + void set_default_color(QTextCharFormat format) { + //std::lock_guard lock(base_sink::mutex_); + default_color_ = format; + } + + void set_level_color(level::level_enum color_level, QTextCharFormat format) + { + //std::lock_guard lock(base_sink::mutex_); colors_.at(static_cast(color_level)) = format; } - QTextCharFormat& get_level_format(level::level_enum color_level) + QTextCharFormat& get_level_color(level::level_enum color_level) { + std::lock_guard lock(base_sink::mutex_); return colors_.at(static_cast(color_level)); } + + QTextCharFormat& get_default_color() + { + std::lock_guard lock(base_sink::mutex_); + return default_color_; + } + protected: void sink_it_(const details::log_msg &msg) override { @@ -136,15 +152,16 @@ protected: cursor.movePosition(QTextCursor::End); // insert the text before the color range + cursor.setCharFormat(default_color_); cursor.insertText(payload.left(msg.color_range_start)); // insert the colorized text - auto format = get_level_format(msg.level); - cursor.setCharFormat(format); + auto color = colors_.at(static_cast(msg.level)); + cursor.setCharFormat(color); cursor.insertText(payload.mid(msg.color_range_start, msg.color_range_end - msg.color_range_start)); // insert the text after the color range with default format - cursor.setCharFormat(QTextCharFormat()); + cursor.setCharFormat(default_color_); cursor.insertText(payload.mid(msg.color_range_end)); } else // no color range @@ -155,7 +172,9 @@ protected: void flush_() override {} QTextEdit *qt_text_edit_; + QTextCharFormat default_color_; std::array colors_; + }; #include "spdlog/details/null_mutex.h" From d7985e3965ba6f5df9675a1a7ce47eb355f56c43 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 13:07:21 +0300 Subject: [PATCH 14/31] Update comment about qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index a7fbff05..5b90b229 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -4,11 +4,15 @@ #pragma once // -// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser...etc) +// Custom sink for QPlainTextEdit or QTextEdit and its children(QTextBrowser...etc) // Building and using requires Qt library. -// +// Recommended: Use the `qt_color_sink` to get nice coloring per level +// For example +// auto edit = new QTextEdit(); +// setCentralWidget(edit); +// auto logger = spdlog::qt_color_logger_mt("my_logger", my_edit_widget); -// Warning: the qt_sink won't be notified if the target widget is destroyed. +// Warning: those sinks won't be notified if the target widget is destroyed. // If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, // and then use a standard signal/slot. // @@ -72,7 +76,9 @@ private: }; -// color sink to QTextEdit. Color location is determined by the sink log pattern +// QT color sink to QTextEdit. +// Color location is determined by the sink log pattern like in the rest of spdlog sinks. +// Colors can be modified if needed using sink->set_color(level, qtTextCharFormat). // Note: Only ascii (latin1) is supported by this sink. template class qt_color_sink : public base_sink From 95b8ee9b327740126b97f4f97be60bc8d07ef287 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 13:11:37 +0300 Subject: [PATCH 15/31] Remove comment in qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 5b90b229..2f72e6cb 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -29,9 +29,6 @@ #include #include -// -// qt_sink class -// namespace spdlog { namespace sinks { template From 31cefdce791f2c90c1bfff5ea7b7ec12fbfcab81 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 13:21:40 +0300 Subject: [PATCH 16/31] Use at() in ansicolor_sink --- include/spdlog/sinks/ansicolor_sink-inl.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index b5848f2d..26ccf8d3 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -21,20 +21,20 @@ SPDLOG_INLINE ansicolor_sink::ansicolor_sink(FILE *target_file, co { set_color_mode(mode); - colors_[level::trace] = to_string_(white); - colors_[level::debug] = to_string_(cyan); - colors_[level::info] = to_string_(green); - colors_[level::warn] = to_string_(yellow_bold); - colors_[level::err] = to_string_(red_bold); - colors_[level::critical] = to_string_(bold_on_red); - colors_[level::off] = to_string_(reset); + colors_.at(level::trace) = to_string_(white); + colors_.at(level::debug) = to_string_(cyan); + colors_.at(level::info) = to_string_(green); + colors_.at(level::warn) = to_string_(yellow_bold); + colors_.at(level::err) = to_string_(red_bold); + colors_.at(level::critical) = to_string_(bold_on_red); + colors_.at(level::off) = to_string_(reset); } template SPDLOG_INLINE void ansicolor_sink::set_color(level::level_enum color_level, string_view_t color) { std::lock_guard lock(mutex_); - colors_[static_cast(color_level)] = to_string_(color); + colors_.at(static_cast(color_level)) = to_string_(color); } template From 1f61f5e019f46f4d217c8d2e0633c89169604ad7 Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 13:23:44 +0300 Subject: [PATCH 17/31] clang format --- CMakeLists.txt | 22 ++- bench/CMakeLists.txt | 4 +- bench/latency.cpp | 3 - example/example.cpp | 2 +- include/spdlog/async.h | 6 +- include/spdlog/async_logger-inl.h | 42 ++--- include/spdlog/common.h | 13 +- include/spdlog/details/file_helper-inl.h | 2 +- include/spdlog/details/os-inl.h | 22 ++- include/spdlog/details/os.h | 2 +- include/spdlog/details/synchronous_factory.h | 2 +- include/spdlog/details/udp_client-windows.h | 6 +- include/spdlog/logger.h | 39 ++-- include/spdlog/pattern_formatter.h | 2 +- include/spdlog/sinks/ansicolor_sink-inl.h | 2 +- include/spdlog/sinks/daily_file_sink.h | 8 +- include/spdlog/sinks/kafka_sink.h | 24 +-- include/spdlog/sinks/msvc_sink.h | 13 +- include/spdlog/sinks/qt_sinks.h | 179 ++++++++++--------- include/spdlog/sinks/stdout_sinks-inl.h | 2 +- include/spdlog/sinks/win_eventlog_sink.h | 8 +- include/spdlog/spdlog.h | 34 ++-- tests/CMakeLists.txt | 8 +- tests/test_errors.cpp | 156 ++++++++-------- tests/test_pattern_formatter.cpp | 2 +- 25 files changed, 294 insertions(+), 309 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ae5a4e2..6556144b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,11 +181,12 @@ endif() target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB) target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$" - "$") + "$") target_link_libraries(spdlog PUBLIC Threads::Threads) spdlog_enable_warnings(spdlog) -set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) +set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION + ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d) if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH) @@ -199,8 +200,9 @@ endif() add_library(spdlog_header_only INTERFACE) add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only) -target_include_directories(spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$" - "$") +target_include_directories( + spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$" + "$") target_link_libraries(spdlog_header_only INTERFACE Threads::Threads) # --------------------------------------------------------------------------------------- @@ -323,12 +325,12 @@ if(SPDLOG_INSTALL) # --------------------------------------------------------------------------------------- # Install pkg-config file # --------------------------------------------------------------------------------------- - if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") set(PKG_CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") else() set(PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() - if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") set(PKG_CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") else() set(PKG_CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") @@ -342,15 +344,11 @@ if(SPDLOG_INSTALL) # --------------------------------------------------------------------------------------- # Install CMake config files # --------------------------------------------------------------------------------------- - export( - TARGETS spdlog - NAMESPACE spdlog:: - FILE "${CMAKE_CURRENT_BINARY_DIR}/${config_targets_file}") + export(TARGETS spdlog NAMESPACE spdlog:: FILE "${CMAKE_CURRENT_BINARY_DIR}/${config_targets_file}") install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file}) include(CMakePackageConfigHelpers) - configure_package_config_file("${project_config_in}" "${project_config_out}" - INSTALL_DESTINATION ${export_dest_dir}) + configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir}) write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion) install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}") diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 144f50b8..8003886a 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -17,8 +17,8 @@ if(NOT benchmark_FOUND) message(STATUS "Downloading GoogleBenchmark") include(FetchContent) - # disable tests - set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") + # disable tests + set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") # Do not build and run googlebenchmark tests FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) FetchContent_MakeAvailable(googlebenchmark) diff --git a/bench/latency.cpp b/bench/latency.cpp index f909cf5e..8f002ee1 100644 --- a/bench/latency.cpp +++ b/bench/latency.cpp @@ -116,9 +116,6 @@ int main(int argc, char *argv[]) tracing_null_logger_st->enable_backtrace(64); benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st); - - - #ifdef __linux bench_dev_null(); #endif // __linux__ diff --git a/example/example.cpp b/example/example.cpp index 636a1be3..ec31baaa 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -142,7 +142,7 @@ void daily_example() void callback_example() { // Create the logger - auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg &/*msg*/) { + auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) { // do what you need to do with msg }); } diff --git a/include/spdlog/async.h b/include/spdlog/async.h index d6e21349..94f9f6d9 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -35,7 +35,7 @@ template struct async_factory_impl { template - static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) + static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { auto ®istry_inst = details::registry::instance(); @@ -61,13 +61,13 @@ using async_factory = async_factory_impl; using async_factory_nonblock = async_factory_impl; template -inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&... sink_args) +inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&...sink_args) { return async_factory::create(std::move(logger_name), std::forward(sink_args)...); } template -inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&... sink_args) +inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&...sink_args) { return async_factory_nonblock::create(std::move(logger_name), std::forward(sink_args)...); } diff --git a/include/spdlog/async_logger-inl.h b/include/spdlog/async_logger-inl.h index 13da5940..4de8382a 100644 --- a/include/spdlog/async_logger-inl.h +++ b/include/spdlog/async_logger-inl.h @@ -24,37 +24,27 @@ SPDLOG_INLINE spdlog::async_logger::async_logger( {} // send the log message to the thread pool -SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg) +SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){ + SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); +} +else { - SPDLOG_TRY - { - if (auto pool_ptr = thread_pool_.lock()) - { - pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); - } - else - { - throw_spdlog_ex("async log: thread pool doesn't exist anymore"); - } - } - SPDLOG_LOGGER_CATCH(msg.source) + throw_spdlog_ex("async log: thread pool doesn't exist anymore"); +} +} +SPDLOG_LOGGER_CATCH(msg.source) } // send flush request to the thread pool -SPDLOG_INLINE void spdlog::async_logger::flush_() +SPDLOG_INLINE void spdlog::async_logger::flush_(){ + SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){pool_ptr->post_flush(shared_from_this(), overflow_policy_); +} +else { - SPDLOG_TRY - { - if (auto pool_ptr = thread_pool_.lock()) - { - pool_ptr->post_flush(shared_from_this(), overflow_policy_); - } - else - { - throw_spdlog_ex("async flush: thread pool doesn't exist anymore"); - } - } - SPDLOG_LOGGER_CATCH(source_loc()) + throw_spdlog_ex("async flush: thread pool doesn't exist anymore"); +} +} +SPDLOG_LOGGER_CATCH(source_loc()) } // diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 5f671c5c..0a262eb2 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -107,7 +107,8 @@ # define SPDLOG_TRY try # define SPDLOG_THROW(ex) throw(ex) # define SPDLOG_CATCH_STD \ - catch (const std::exception &) {} + catch (const std::exception &) \ + {} #endif namespace spdlog { @@ -173,12 +174,12 @@ using format_string_t = fmt::format_string; template using remove_cvref_t = typename std::remove_cv::type>::type; -template -#if FMT_VERSION >= 90101 +template +# if FMT_VERSION >= 90101 using fmt_runtime_string = fmt::runtime_format_string; -#else +# else using fmt_runtime_string = fmt::basic_runtime; -#endif +# endif // clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here, // in addition, fmt::basic_runtime is only convertible to basic_format_string but not basic_string_view @@ -391,7 +392,7 @@ template using enable_if_t = typename std::enable_if::type; template -std::unique_ptr make_unique(Args &&... args) +std::unique_ptr make_unique(Args &&...args) { static_assert(!std::is_array::value, "arrays not supported"); return std::unique_ptr(new T(std::forward(args)...)); diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index 3c45d8c0..74c89a87 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -92,7 +92,7 @@ SPDLOG_INLINE void file_helper::flush() SPDLOG_INLINE void file_helper::sync() { - if(!os::fsync(fd_)) + if (!os::fsync(fd_)) { throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno); } diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index e1080d05..ea8864ea 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -292,7 +292,8 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) return offset; #else -# if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) +# if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \ + (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris struct helper { @@ -362,15 +363,18 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT // There is no pthread_threadid_np prior to 10.6, and it is not supported on any PPC, // including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64. # if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__) - tid = pthread_mach_thread_np(pthread_self()); + tid = pthread_mach_thread_np(pthread_self()); # elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060 - if (&pthread_threadid_np) { - pthread_threadid_np(nullptr, &tid); - } else { - tid = pthread_mach_thread_np(pthread_self()); - } -# else + if (&pthread_threadid_np) + { pthread_threadid_np(nullptr, &tid); + } + else + { + tid = pthread_mach_thread_np(pthread_self()); + } +# else + pthread_threadid_np(nullptr, &tid); # endif return static_cast(tid); #else // Default to standard C++11 (other Unix) @@ -525,7 +529,7 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) { target.resize(result_size); result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size); - if (result_size > 0) + if (result_size > 0) { assert(result_size == target.size()); return; diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index f55642c1..37b00874 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -111,7 +111,7 @@ SPDLOG_API std::string getenv(const char *field); // Do fsync by FILE objectpointer. // Return true on success. -SPDLOG_API bool fsync(FILE * fp); +SPDLOG_API bool fsync(FILE *fp); } // namespace os } // namespace details diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h index 1f729ab9..e1e42268 100644 --- a/include/spdlog/details/synchronous_factory.h +++ b/include/spdlog/details/synchronous_factory.h @@ -13,7 +13,7 @@ class logger; struct synchronous_factory { template - static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) + static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { auto sink = std::make_shared(std::forward(args)...); auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h index 7d25f037..10894ee6 100644 --- a/include/spdlog/details/udp_client-windows.h +++ b/include/spdlog/details/udp_client-windows.h @@ -16,9 +16,9 @@ #include #if defined(_MSC_VER) -# pragma comment(lib, "Ws2_32.lib") -# pragma comment(lib, "Mswsock.lib") -# pragma comment(lib, "AdvApi32.lib") +# pragma comment(lib, "Ws2_32.lib") +# pragma comment(lib, "Mswsock.lib") +# pragma comment(lib, "AdvApi32.lib") #endif namespace spdlog { diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index abda5f4e..0802a5d9 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -85,13 +85,13 @@ public: void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; template - void log(source_loc loc, level::level_enum lvl, format_string_t fmt, Args &&... args) + void log(source_loc loc, level::level_enum lvl, format_string_t fmt, Args &&...args) { log_(loc, lvl, details::to_string_view(fmt), std::forward(args)...); } template - void log(level::level_enum lvl, format_string_t fmt, Args &&... args) + void log(level::level_enum lvl, format_string_t fmt, Args &&...args) { log(source_loc{}, lvl, fmt, std::forward(args)...); } @@ -141,50 +141,50 @@ public: } template - void trace(format_string_t fmt, Args &&... args) + void trace(format_string_t fmt, Args &&...args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(format_string_t fmt, Args &&... args) + void debug(format_string_t fmt, Args &&...args) { log(level::debug, fmt, std::forward(args)...); } template - void info(format_string_t fmt, Args &&... args) + void info(format_string_t fmt, Args &&...args) { log(level::info, fmt, std::forward(args)...); } template - void warn(format_string_t fmt, Args &&... args) + void warn(format_string_t fmt, Args &&...args) { log(level::warn, fmt, std::forward(args)...); } template - void error(format_string_t fmt, Args &&... args) + void error(format_string_t fmt, Args &&...args) { log(level::err, fmt, std::forward(args)...); } template - void critical(format_string_t fmt, Args &&... args) + void critical(format_string_t fmt, Args &&...args) { log(level::critical, fmt, std::forward(args)...); } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log(source_loc loc, level::level_enum lvl, wformat_string_t fmt, Args &&... args) + void log(source_loc loc, level::level_enum lvl, wformat_string_t fmt, Args &&...args) { log_(loc, lvl, details::to_string_view(fmt), std::forward(args)...); } template - void log(level::level_enum lvl, wformat_string_t fmt, Args &&... args) + void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) { log(source_loc{}, lvl, fmt, std::forward(args)...); } @@ -225,37 +225,37 @@ public: } template - void trace(wformat_string_t fmt, Args &&... args) + void trace(wformat_string_t fmt, Args &&...args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(wformat_string_t fmt, Args &&... args) + void debug(wformat_string_t fmt, Args &&...args) { log(level::debug, fmt, std::forward(args)...); } template - void info(wformat_string_t fmt, Args &&... args) + void info(wformat_string_t fmt, Args &&...args) { log(level::info, fmt, std::forward(args)...); } template - void warn(wformat_string_t fmt, Args &&... args) + void warn(wformat_string_t fmt, Args &&...args) { log(level::warn, fmt, std::forward(args)...); } template - void error(wformat_string_t fmt, Args &&... args) + void error(wformat_string_t fmt, Args &&...args) { log(level::err, fmt, std::forward(args)...); } template - void critical(wformat_string_t fmt, Args &&... args) + void critical(wformat_string_t fmt, Args &&...args) { log(level::critical, fmt, std::forward(args)...); } @@ -357,7 +357,7 @@ protected: // common implementation for after templated public api has been resolved template - void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args) + void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args) { bool log_enabled = should_log(lvl); bool traceback_enabled = tracer_.enabled(); @@ -382,7 +382,7 @@ protected: #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args) + void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&...args) { bool log_enabled = should_log(lvl); bool traceback_enabled = tracer_.enabled(); @@ -394,8 +394,7 @@ protected: { // format to wmemory_buffer and convert to utf8 wmemory_buf_t wbuf; - fmt_lib::vformat_to( - std::back_inserter(wbuf), fmt, fmt_lib::make_format_args(args...)); + fmt_lib::vformat_to(std::back_inserter(wbuf), fmt, fmt_lib::make_format_args(args...)); memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index acf1c536..4c87b21e 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -92,7 +92,7 @@ public: void format(const details::log_msg &msg, memory_buf_t &dest) override; template - pattern_formatter &add_flag(char flag, Args &&... args) + pattern_formatter &add_flag(char flag, Args &&...args) { custom_handlers_[flag] = details::make_unique(std::forward(args)...); return *this; diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index 26ccf8d3..c924fc5b 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -52,7 +52,7 @@ SPDLOG_INLINE void ansicolor_sink::log(const details::log_msg &msg // before color range print_range_(formatted, 0, msg.color_range_start); // in color range - print_ccode_(colors_[static_cast(msg.level)]); + print_ccode_(colors_.at(static_cast(msg.level))); print_range_(formatted, msg.color_range_start, msg.color_range_end); print_ccode_(reset); // after color range diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 25abb02b..0770380c 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -50,12 +50,12 @@ struct daily_filename_format_calculator static filename_t calc_filename(const filename_t &file_path, const tm &now_tm) { #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - std::wstringstream stream; + std::wstringstream stream; #else - std::stringstream stream; + std::stringstream stream; #endif - stream << std::put_time(&now_tm, file_path.c_str()); - return stream.str(); + stream << std::put_time(&now_tm, file_path.c_str()); + return stream.str(); } }; diff --git a/include/spdlog/sinks/kafka_sink.h b/include/spdlog/sinks/kafka_sink.h index 3c0680ef..ce740efc 100644 --- a/include/spdlog/sinks/kafka_sink.h +++ b/include/spdlog/sinks/kafka_sink.h @@ -28,12 +28,12 @@ struct kafka_sink_config { std::string server_addr; std::string produce_topic; - int32_t flush_timeout_ms = 1000; + int32_t flush_timeout_ms = 1000; kafka_sink_config(std::string addr, std::string topic, int flush_timeout_ms = 1000) - : server_addr{std::move(addr)} - ,produce_topic{std::move(topic)} - ,flush_timeout_ms(flush_timeout_ms) + : server_addr{std::move(addr)} + , produce_topic{std::move(topic)} + , flush_timeout_ms(flush_timeout_ms) {} }; @@ -42,8 +42,8 @@ class kafka_sink : public base_sink { public: kafka_sink(kafka_sink_config config) - : config_{std::move(config)} - { + : config_{std::move(config)} + { try { std::string errstr; @@ -75,7 +75,7 @@ public: { throw_spdlog_ex(fmt_lib::format("error create kafka instance: {}", e.what())); } - } + } ~kafka_sink() { @@ -85,7 +85,7 @@ public: protected: void sink_it_(const details::log_msg &msg) override { - producer_->produce(topic_.get(), 0, RdKafka::Producer::RK_MSG_COPY, (void *)msg.payload.data(), msg.payload.size(), NULL, NULL); + producer_->produce(topic_.get(), 0, RdKafka::Producer::RK_MSG_COPY, (void *)msg.payload.data(), msg.payload.size(), NULL, NULL); } void flush_() override @@ -104,7 +104,7 @@ private: using kafka_sink_mt = kafka_sink; using kafka_sink_st = kafka_sink; -} // namespace sinks +} // namespace sinks template inline std::shared_ptr kafka_logger_mt(const std::string &logger_name, spdlog::sinks::kafka_sink_config config) @@ -121,13 +121,13 @@ inline std::shared_ptr kafka_logger_st(const std::string &logger_name, s template inline std::shared_ptr kafka_logger_async_mt(std::string logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); + return Factory::template create(logger_name, config); } template inline std::shared_ptr kafka_logger_async_st(std::string logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); + return Factory::template create(logger_name, config); } -} // namespace spdlog +} // namespace spdlog diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index 34d5f34e..bf68ae88 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -3,7 +3,6 @@ #pragma once - #if defined(_WIN32) # include @@ -16,11 +15,11 @@ # include // Avoid including windows.h (https://stackoverflow.com/a/30741042) -#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) +# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString); -#else +# else extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString); -#endif +# endif extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace spdlog { @@ -46,13 +45,13 @@ protected: memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); formatted.push_back('\0'); // add a null terminator for OutputDebugString -#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) +# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) wmemory_buf_t wformatted; details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted); OutputDebugStringW(wformatted.data()); -#else +# else OutputDebugStringA(formatted.data()); -#endif +# endif } void flush_() override {} diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 2f72e6cb..a1cfd12c 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -11,7 +11,7 @@ // auto edit = new QTextEdit(); // setCentralWidget(edit); // auto logger = spdlog::qt_color_logger_mt("my_logger", my_edit_widget); - +// // Warning: those sinks won't be notified if the target widget is destroyed. // If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, // and then use a standard signal/slot. @@ -72,7 +72,6 @@ private: QMetaMethod meta_method_; }; - // QT color sink to QTextEdit. // Color location is determined by the sink log pattern like in the rest of spdlog sinks. // Colors can be modified if needed using sink->set_color(level, qtTextCharFormat). @@ -81,104 +80,104 @@ template class qt_color_sink : public base_sink { public: - qt_color_sink(QTextEdit *qt_text_edit) : qt_text_edit_(qt_text_edit) + qt_color_sink(QTextEdit *qt_text_edit) + : qt_text_edit_(qt_text_edit) + { + if (!qt_text_edit_) { - if(!qt_text_edit_) - { - throw_spdlog_ex("qt_color_text_sink: text_edit is null"); - } - default_color_ = qt_text_edit_->currentCharFormat(); - // set colors - QTextCharFormat format; - // trace - format.setForeground(Qt::gray); - colors_.at(level::trace) = format; - // debug - format.setForeground(Qt::cyan); - colors_.at(level::debug) = format; - // info - format.setForeground(Qt::green); - colors_.at(level::info) = format; - // warn - format.setForeground(Qt::yellow); - colors_.at(level::warn) = format; - // err - format.setForeground(Qt::red); - colors_.at(level::err) = format; - // critical - format.setForeground(Qt::white); - format.setBackground(Qt::red); - colors_.at(level::critical) = format; + throw_spdlog_ex("qt_color_text_sink: text_edit is null"); } + default_color_ = qt_text_edit_->currentCharFormat(); + // set colors + QTextCharFormat format; + // trace + format.setForeground(Qt::gray); + colors_.at(level::trace) = format; + // debug + format.setForeground(Qt::cyan); + colors_.at(level::debug) = format; + // info + format.setForeground(Qt::green); + colors_.at(level::info) = format; + // warn + format.setForeground(Qt::yellow); + colors_.at(level::warn) = format; + // err + format.setForeground(Qt::red); + colors_.at(level::err) = format; + // critical + format.setForeground(Qt::white); + format.setBackground(Qt::red); + colors_.at(level::critical) = format; + } - ~qt_color_sink() - { - flush_(); - } + ~qt_color_sink() + { + flush_(); + } - void set_default_color(QTextCharFormat format) - { - //std::lock_guard lock(base_sink::mutex_); - default_color_ = format; - } + void set_default_color(QTextCharFormat format) + { + // std::lock_guard lock(base_sink::mutex_); + default_color_ = format; + } - void set_level_color(level::level_enum color_level, QTextCharFormat format) - { - //std::lock_guard lock(base_sink::mutex_); - colors_.at(static_cast(color_level)) = format; - } + void set_level_color(level::level_enum color_level, QTextCharFormat format) + { + // std::lock_guard lock(base_sink::mutex_); + colors_.at(static_cast(color_level)) = format; + } - QTextCharFormat& get_level_color(level::level_enum color_level) - { - std::lock_guard lock(base_sink::mutex_); - return colors_.at(static_cast(color_level)); - } + QTextCharFormat &get_level_color(level::level_enum color_level) + { + std::lock_guard lock(base_sink::mutex_); + return colors_.at(static_cast(color_level)); + } - QTextCharFormat& get_default_color() - { - std::lock_guard lock(base_sink::mutex_); - return default_color_; - } + QTextCharFormat &get_default_color() + { + std::lock_guard lock(base_sink::mutex_); + return default_color_; + } protected: - void sink_it_(const details::log_msg &msg) override + void sink_it_(const details::log_msg &msg) override + { + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + string_view_t str = string_view_t(formatted.data(), formatted.size()); + + // apply the color to the color range in the formatted message. + auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); + if (msg.color_range_end > msg.color_range_start) { - memory_buf_t formatted; - base_sink::formatter_->format(msg, formatted); - string_view_t str = string_view_t(formatted.data(), formatted.size()); - - // apply the color to the color range in the formatted message. - auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); - if(msg.color_range_end > msg.color_range_start) - { - QTextCursor cursor(qt_text_edit_->document()); - cursor.movePosition(QTextCursor::End); - - // insert the text before the color range - cursor.setCharFormat(default_color_); - cursor.insertText(payload.left(msg.color_range_start)); - - // insert the colorized text - auto color = colors_.at(static_cast(msg.level)); - cursor.setCharFormat(color); - cursor.insertText(payload.mid(msg.color_range_start, msg.color_range_end - msg.color_range_start)); - - // insert the text after the color range with default format - cursor.setCharFormat(default_color_); - cursor.insertText(payload.mid(msg.color_range_end)); - } - else // no color range - { - qt_text_edit_->append(payload.trimmed()); - } + QTextCursor cursor(qt_text_edit_->document()); + cursor.movePosition(QTextCursor::End); + + // insert the text before the color range + cursor.setCharFormat(default_color_); + cursor.insertText(payload.left(msg.color_range_start)); + + // insert the colorized text + auto color = colors_.at(static_cast(msg.level)); + cursor.setCharFormat(color); + cursor.insertText(payload.mid(msg.color_range_start, msg.color_range_end - msg.color_range_start)); + + // insert the text after the color range with default format + cursor.setCharFormat(default_color_); + cursor.insertText(payload.mid(msg.color_range_end)); } + else // no color range + { + qt_text_edit_->append(payload.trimmed()); + } + } - void flush_() override {} - QTextEdit *qt_text_edit_; + void flush_() override {} + QTextEdit *qt_text_edit_; QTextCharFormat default_color_; - std::array colors_; - - }; + std::array colors_; +}; #include "spdlog/details/null_mutex.h" #include @@ -194,13 +193,15 @@ using qt_color_sink_st = qt_color_sink; // create logger using QTextEdit object template -inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") +inline std::shared_ptr qt_logger_mt( + const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } template -inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") +inline std::shared_ptr qt_logger_st( + const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") { return Factory::template create(logger_name, qt_object, meta_method); } diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h index 9e8e6ddf..c1754370 100644 --- a/include/spdlog/sinks/stdout_sinks-inl.h +++ b/include/spdlog/sinks/stdout_sinks-inl.h @@ -72,7 +72,7 @@ SPDLOG_INLINE void stdout_sink_base::log(const details::log_msg &m memory_buf_t formatted; formatter_->format(msg, formatted); ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_); -#endif // WIN32 +#endif // WIN32 ::fflush(file_); // flush every line to terminal } diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h index def4b135..d23d00a8 100644 --- a/include/spdlog/sinks/win_eventlog_sink.h +++ b/include/spdlog/sinks/win_eventlog_sink.h @@ -241,12 +241,12 @@ protected: details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf); LPCWSTR lp_wstr = buf.data(); - succeeded = static_cast(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_, - current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr)); + succeeded = static_cast(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), + event_id_, current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr)); #else LPCSTR lp_str = formatted.data(); - succeeded = static_cast(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_, - current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr)); + succeeded = static_cast(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), + event_id_, current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr)); #endif if (!succeeded) diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 6b7b221a..fbfe5fb8 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -31,7 +31,7 @@ using default_factory = synchronous_factory; // Example: // spdlog::create("logger_name", "dailylog_filename", 11, 59); template -inline std::shared_ptr create(std::string logger_name, SinkArgs &&... sink_args) +inline std::shared_ptr create(std::string logger_name, SinkArgs &&...sink_args) { return default_factory::create(std::move(logger_name), std::forward(sink_args)...); } @@ -141,49 +141,49 @@ SPDLOG_API void set_default_logger(std::shared_ptr default_logge SPDLOG_API void apply_logger_env_levels(std::shared_ptr logger); template -inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&... args) +inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&...args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, format_string_t fmt, Args &&... args) +inline void log(level::level_enum lvl, format_string_t fmt, Args &&...args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(format_string_t fmt, Args &&... args) +inline void trace(format_string_t fmt, Args &&...args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(format_string_t fmt, Args &&... args) +inline void debug(format_string_t fmt, Args &&...args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(format_string_t fmt, Args &&... args) +inline void info(format_string_t fmt, Args &&...args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(format_string_t fmt, Args &&... args) +inline void warn(format_string_t fmt, Args &&...args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(format_string_t fmt, Args &&... args) +inline void error(format_string_t fmt, Args &&...args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(format_string_t fmt, Args &&... args) +inline void critical(format_string_t fmt, Args &&...args) { default_logger_raw()->critical(fmt, std::forward(args)...); } @@ -202,49 +202,49 @@ inline void log(level::level_enum lvl, const T &msg) #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template -inline void log(source_loc source, level::level_enum lvl, wformat_string_t fmt, Args &&... args) +inline void log(source_loc source, level::level_enum lvl, wformat_string_t fmt, Args &&...args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, wformat_string_t fmt, Args &&... args) +inline void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(wformat_string_t fmt, Args &&... args) +inline void trace(wformat_string_t fmt, Args &&...args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(wformat_string_t fmt, Args &&... args) +inline void debug(wformat_string_t fmt, Args &&...args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(wformat_string_t fmt, Args &&... args) +inline void info(wformat_string_t fmt, Args &&...args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(wformat_string_t fmt, Args &&... args) +inline void warn(wformat_string_t fmt, Args &&...args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(wformat_string_t fmt, Args &&... args) +inline void error(wformat_string_t fmt, Args &&...args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(wformat_string_t fmt, Args &&... args) +inline void critical(wformat_string_t fmt, Args &&...args) { default_logger_raw()->critical(fmt, std::forward(args)...); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 131b22f8..942ec519 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,16 +14,12 @@ if(PkgConfig_FOUND) endif() find_package(Catch2 3 QUIET) -if (Catch2_FOUND) +if(Catch2_FOUND) message(STATUS "Packaged version of Catch will be used.") else() message(STATUS "Bundled version of Catch will be downloaded and used.") include(FetchContent) - FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.3.2 - ) + FetchContent_Declare(Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.3.2) FetchContent_MakeAvailable(Catch2) endif() diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index a698c47b..78032482 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -1,6 +1,6 @@ /* -* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE -*/ + * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ #include "includes.h" #include @@ -11,114 +11,114 @@ class failing_sink : public spdlog::sinks::base_sink { protected: - void sink_it_(const spdlog::details::log_msg &) final - { - throw std::runtime_error("some error happened during log"); - } + void sink_it_(const spdlog::details::log_msg &) final + { + throw std::runtime_error("some error happened during log"); + } - void flush_() final - { - throw std::runtime_error("some error happened during flush"); - } + void flush_() final + { + throw std::runtime_error("some error happened during flush"); + } }; -struct custom_ex {}; +struct custom_ex +{}; #if !defined(SPDLOG_USE_STD_FORMAT) // std formt doesn't fully support tuntime strings TEST_CASE("default_error_handler", "[errors]") { - prepare_logdir(); - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::create("test-error", filename, true); - logger->set_pattern("%v"); - logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1); - logger->info("Test message {}", 2); - logger->flush(); - using spdlog::details::os::default_eol; - REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); - REQUIRE(count_lines(SIMPLE_LOG) == 1); + auto logger = spdlog::create("test-error", filename, true); + logger->set_pattern("%v"); + logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1); + logger->info("Test message {}", 2); + logger->flush(); + using spdlog::details::os::default_eol; + REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); + REQUIRE(count_lines(SIMPLE_LOG) == 1); } - TEST_CASE("custom_error_handler", "[errors]") { - prepare_logdir(); - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::create("logger", filename, true); - logger->flush_on(spdlog::level::info); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); - logger->info("Good message #1"); + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + auto logger = spdlog::create("logger", filename, true); + logger->flush_on(spdlog::level::info); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + logger->info("Good message #1"); - REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex); - logger->info("Good message #2"); - require_message_count(SIMPLE_LOG, 2); + REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex); + logger->info("Good message #2"); + require_message_count(SIMPLE_LOG, 2); } #endif TEST_CASE("default_error_handler2", "[errors]") { - spdlog::drop_all(); - auto logger = spdlog::create("failed_logger"); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); - REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); + spdlog::drop_all(); + auto logger = spdlog::create("failed_logger"); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); } TEST_CASE("flush_error_handler", "[errors]") { - spdlog::drop_all(); - auto logger = spdlog::create("failed_logger"); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); - REQUIRE_THROWS_AS(logger->flush(), custom_ex); + spdlog::drop_all(); + auto logger = spdlog::create("failed_logger"); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + REQUIRE_THROWS_AS(logger->flush(), custom_ex); } #if !defined(SPDLOG_USE_STD_FORMAT) TEST_CASE("async_error_handler", "[errors]") { - prepare_logdir(); - std::string err_msg("log failed with some msg"); + prepare_logdir(); + std::string err_msg("log failed with some msg"); - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG); - { - spdlog::init_thread_pool(128, 1); - auto logger = spdlog::create_async("logger", filename, true); - logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("test_logs/custom_err.txt"); - if (!ofs) - { - throw std::runtime_error("Failed open test_logs/custom_err.txt"); - } - ofs << err_msg; - }); - logger->info("Good message #1"); - logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"); - logger->info("Good message #2"); - spdlog::drop("logger"); // force logger to drain the queue and shutdown - } - spdlog::init_thread_pool(128, 1); - require_message_count(SIMPLE_ASYNC_LOG, 2); - REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG); + { + spdlog::init_thread_pool(128, 1); + auto logger = spdlog::create_async("logger", filename, true); + logger->set_error_handler([=](const std::string &) { + std::ofstream ofs("test_logs/custom_err.txt"); + if (!ofs) + { + throw std::runtime_error("Failed open test_logs/custom_err.txt"); + } + ofs << err_msg; + }); + logger->info("Good message #1"); + logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"); + logger->info("Good message #2"); + spdlog::drop("logger"); // force logger to drain the queue and shutdown + } + spdlog::init_thread_pool(128, 1); + require_message_count(SIMPLE_ASYNC_LOG, 2); + REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg); } #endif // Make sure async error handler is executed TEST_CASE("async_error_handler2", "[errors]") { - prepare_logdir(); - std::string err_msg("This is async handler error message"); - { - spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs")); - spdlog::init_thread_pool(128, 1); - auto logger = spdlog::create_async("failed_logger"); - logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("test_logs/custom_err2.txt"); - if (!ofs) - throw std::runtime_error("Failed open test_logs/custom_err2.txt"); - ofs << err_msg; - }); - logger->info("Hello failure"); - spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown - } + prepare_logdir(); + std::string err_msg("This is async handler error message"); + { + spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs")); + spdlog::init_thread_pool(128, 1); + auto logger = spdlog::create_async("failed_logger"); + logger->set_error_handler([=](const std::string &) { + std::ofstream ofs("test_logs/custom_err2.txt"); + if (!ofs) + throw std::runtime_error("Failed open test_logs/custom_err2.txt"); + ofs << err_msg; + }); + logger->info("Hello failure"); + spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown + } - spdlog::init_thread_pool(128, 1); - REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg); + spdlog::init_thread_pool(128, 1); + REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg); } diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index b89f51d4..bafea884 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -6,7 +6,7 @@ using spdlog::details::to_string_view; // log to str and return it template -static std::string log_to_str(const std::string &msg, const Args &... args) +static std::string log_to_str(const std::string &msg, const Args &...args) { std::ostringstream oss; auto oss_sink = std::make_shared(oss); From f0e1f22bbc2d4c34f032bd473efd42487294297c Mon Sep 17 00:00:00 2001 From: gabime Date: Wed, 7 Jun 2023 13:37:58 +0300 Subject: [PATCH 18/31] Updated README.md with qt color example --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index d7434356..2b02422f 100644 --- a/README.md +++ b/README.md @@ -420,6 +420,21 @@ void replace_default_logger_example() } ``` +--- +#### Log to Qt with nice colors +```c++ +#include "spdlog/spdlog.h" +#include "spdlog/sinks/qt_sinks.h" +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + setMinimumSize(640, 480); + auto log_widget = new QTextEdit(this); + setCentralWidget(log_widget); + auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget); + logger->info("Some info message"); +} +``` + --- ## Benchmarks From 32bab0e1030aa92c6e64fe9da7ea75531cfd9c6c Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 7 Jun 2023 13:47:50 +0300 Subject: [PATCH 19/31] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2b02422f..822480bf 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ $ cmake .. && make -j * syslog. * Windows event log. * Windows debugger (```OutputDebugString(..)```). + * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. * Log filtering - log levels can be modified at runtime as well as compile time. * Support for loading log levels from argv or environment var. From 3a7188505f99bb3deeaa4decfbb8774a8e0f8a10 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Thu, 8 Jun 2023 01:12:25 +0300 Subject: [PATCH 20/31] Added lock to qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index a1cfd12c..7e4fb513 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -118,13 +118,13 @@ public: void set_default_color(QTextCharFormat format) { - // std::lock_guard lock(base_sink::mutex_); + std::lock_guard lock(base_sink::mutex_); default_color_ = format; } void set_level_color(level::level_enum color_level, QTextCharFormat format) { - // std::lock_guard lock(base_sink::mutex_); + std::lock_guard lock(base_sink::mutex_); colors_.at(static_cast(color_level)) = format; } From c17b5d9cd1692b27056215b54aa91f515bec2100 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 9 Jun 2023 12:41:36 +0300 Subject: [PATCH 21/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 7e4fb513..13ce9ce5 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -12,9 +12,8 @@ // setCentralWidget(edit); // auto logger = spdlog::qt_color_logger_mt("my_logger", my_edit_widget); // -// Warning: those sinks won't be notified if the target widget is destroyed. -// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, -// and then use a standard signal/slot. +// Warning: those sinks won't be notified if the target widget is destroyed. +// Make sure the sink get destryoed, or not used after the widget is gone. // #include "spdlog/common.h" From 4fb4e2bd86b3c36d2d1d6b4d132605471cf48231 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 9 Jun 2023 12:44:54 +0300 Subject: [PATCH 22/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 13ce9ce5..5c29bb5d 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -13,7 +13,7 @@ // auto logger = spdlog::qt_color_logger_mt("my_logger", my_edit_widget); // // Warning: those sinks won't be notified if the target widget is destroyed. -// Make sure the sink get destryoed, or not used after the widget is gone. +// Use a permanent QObject or make sure the sink is not used after the widget is gone. // #include "spdlog/common.h" From 199cc0a6d89d304c4330578286b68910f8ad30c8 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 9 Jun 2023 12:59:41 +0300 Subject: [PATCH 23/31] Update qt_sinks.h --- include/spdlog/sinks/qt_sinks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 5c29bb5d..7714043d 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -86,8 +86,8 @@ public: { throw_spdlog_ex("qt_color_text_sink: text_edit is null"); } + // set color for each log level default_color_ = qt_text_edit_->currentCharFormat(); - // set colors QTextCharFormat format; // trace format.setForeground(Qt::gray); From 4f4da7f114ef4b643afdba711aa5331e082bec91 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 10 Jun 2023 02:50:19 +0300 Subject: [PATCH 24/31] Revert qt_sinks changes and color support, since they are not thread safe --- include/spdlog/sinks/qt_sinks.h | 176 +++----------------------------- 1 file changed, 17 insertions(+), 159 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 7714043d..d61c6bec 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -4,16 +4,12 @@ #pragma once // -// Custom sink for QPlainTextEdit or QTextEdit and its children(QTextBrowser...etc) -// Building and using requires Qt library. -// Recommended: Use the `qt_color_sink` to get nice coloring per level -// For example -// auto edit = new QTextEdit(); -// setCentralWidget(edit); -// auto logger = spdlog::qt_color_logger_mt("my_logger", my_edit_widget); +// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser... +// etc) Building and using requires Qt library. // -// Warning: those sinks won't be notified if the target widget is destroyed. -// Use a permanent QObject or make sure the sink is not used after the widget is gone. +// Warning: the qt_sink won't be notified if the target widget is destroyed. +// If the widget's lifetime can be shorter than the logger's one, you should provide some permanent QObject, +// and then use a standard signal/slot. // #include "spdlog/common.h" @@ -21,32 +17,22 @@ #include "spdlog/details/synchronous_factory.h" #include "spdlog/sinks/base_sink.h" -#include - -#include #include #include -#include +// +// qt_sink class +// namespace spdlog { namespace sinks { template class qt_sink : public base_sink { public: - // qt object is the object that receives the log messages (e.g QPlainTextEdit or QTextEdit) - // meta_method_name is the name of the slot to be called on the qt_object for every log message (e.g "append(QString)"). - qt_sink(QObject *qt_object, const std::string &meta_method_name) + qt_sink(QObject *qt_object, const std::string &meta_method) { - // store the meta method object for later usage qt_object_ = qt_object; - auto *metaobject = qt_object_->metaObject(); - auto methodIndex = metaobject->indexOfMethod(meta_method_name.c_str()); - if (methodIndex == -1) - { - throw_spdlog_ex("qt_sink: qt_object does not have meta_method " + meta_method_name); - } - meta_method_ = metaobject->method(methodIndex); + meta_method_ = meta_method; } ~qt_sink() @@ -60,180 +46,52 @@ protected: memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); string_view_t str = string_view_t(formatted.data(), formatted.size()); - auto payload = QString::fromUtf8(str.data(), static_cast(str.size())).trimmed(); - meta_method_.invoke(qt_object_, Qt::AutoConnection, Q_ARG(QString, payload)); + QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, + Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); } void flush_() override {} private: QObject *qt_object_ = nullptr; - QMetaMethod meta_method_; -}; - -// QT color sink to QTextEdit. -// Color location is determined by the sink log pattern like in the rest of spdlog sinks. -// Colors can be modified if needed using sink->set_color(level, qtTextCharFormat). -// Note: Only ascii (latin1) is supported by this sink. -template -class qt_color_sink : public base_sink -{ -public: - qt_color_sink(QTextEdit *qt_text_edit) - : qt_text_edit_(qt_text_edit) - { - if (!qt_text_edit_) - { - throw_spdlog_ex("qt_color_text_sink: text_edit is null"); - } - // set color for each log level - default_color_ = qt_text_edit_->currentCharFormat(); - QTextCharFormat format; - // trace - format.setForeground(Qt::gray); - colors_.at(level::trace) = format; - // debug - format.setForeground(Qt::cyan); - colors_.at(level::debug) = format; - // info - format.setForeground(Qt::green); - colors_.at(level::info) = format; - // warn - format.setForeground(Qt::yellow); - colors_.at(level::warn) = format; - // err - format.setForeground(Qt::red); - colors_.at(level::err) = format; - // critical - format.setForeground(Qt::white); - format.setBackground(Qt::red); - colors_.at(level::critical) = format; - } - - ~qt_color_sink() - { - flush_(); - } - - void set_default_color(QTextCharFormat format) - { - std::lock_guard lock(base_sink::mutex_); - default_color_ = format; - } - - void set_level_color(level::level_enum color_level, QTextCharFormat format) - { - std::lock_guard lock(base_sink::mutex_); - colors_.at(static_cast(color_level)) = format; - } - - QTextCharFormat &get_level_color(level::level_enum color_level) - { - std::lock_guard lock(base_sink::mutex_); - return colors_.at(static_cast(color_level)); - } - - QTextCharFormat &get_default_color() - { - std::lock_guard lock(base_sink::mutex_); - return default_color_; - } - -protected: - void sink_it_(const details::log_msg &msg) override - { - memory_buf_t formatted; - base_sink::formatter_->format(msg, formatted); - string_view_t str = string_view_t(formatted.data(), formatted.size()); - - // apply the color to the color range in the formatted message. - auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); - if (msg.color_range_end > msg.color_range_start) - { - QTextCursor cursor(qt_text_edit_->document()); - cursor.movePosition(QTextCursor::End); - - // insert the text before the color range - cursor.setCharFormat(default_color_); - cursor.insertText(payload.left(msg.color_range_start)); - - // insert the colorized text - auto color = colors_.at(static_cast(msg.level)); - cursor.setCharFormat(color); - cursor.insertText(payload.mid(msg.color_range_start, msg.color_range_end - msg.color_range_start)); - - // insert the text after the color range with default format - cursor.setCharFormat(default_color_); - cursor.insertText(payload.mid(msg.color_range_end)); - } - else // no color range - { - qt_text_edit_->append(payload.trimmed()); - } - } - - void flush_() override {} - QTextEdit *qt_text_edit_; - QTextCharFormat default_color_; - std::array colors_; + std::string meta_method_; }; #include "spdlog/details/null_mutex.h" #include using qt_sink_mt = qt_sink; using qt_sink_st = qt_sink; -using qt_color_sink_mt = qt_color_sink; -using qt_color_sink_st = qt_color_sink; } // namespace sinks // // Factory functions // - -// create logger using QTextEdit object template -inline std::shared_ptr qt_logger_mt( - const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") +inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { return Factory::template create(logger_name, qt_object, meta_method); } template -inline std::shared_ptr qt_logger_st( - const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append(QString)") +inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { return Factory::template create(logger_name, qt_object, meta_method); } -// create logger using QPlainTextEdit object template inline std::shared_ptr qt_logger_mt( - const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText(QString)") + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") { return Factory::template create(logger_name, qt_object, meta_method); } template inline std::shared_ptr qt_logger_st( - const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText(QString)") + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") { return Factory::template create(logger_name, qt_object, meta_method); } -// create color qt logger using QTextEdit object -template -inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_object) -{ - return Factory::template create(logger_name, qt_object); -} - -template -inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_object) -{ - return Factory::template create(logger_name, qt_object); -} - -// create logger with other QObject object template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { From 5a6b6cafa8d4aee3e6d0dd16a2cae9169141c831 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 10 Jun 2023 02:52:27 +0300 Subject: [PATCH 25/31] Update README.md --- README.md | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 822480bf..72d2dd7b 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ $ cmake .. && make -j * syslog. * Windows event log. * Windows debugger (```OutputDebugString(..)```). - * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). + * Log to Qt widgets * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. * Log filtering - log levels can be modified at runtime as well as compile time. * Support for loading log levels from argv or environment var. @@ -422,21 +422,8 @@ void replace_default_logger_example() ``` --- -#### Log to Qt with nice colors -```c++ -#include "spdlog/spdlog.h" -#include "spdlog/sinks/qt_sinks.h" -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) -{ - setMinimumSize(640, 480); - auto log_widget = new QTextEdit(this); - setCentralWidget(log_widget); - auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget); - logger->info("Some info message"); -} -``` ---- + ## Benchmarks Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz From 91280df07e7591ce1fd0fadb0cf7132b01ec7ea1 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 15:07:00 +0300 Subject: [PATCH 26/31] wip color_qt_sink --- include/spdlog/sinks/qt_sinks.h | 201 +++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 2 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index d61c6bec..11ea41ac 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -16,9 +16,12 @@ #include "spdlog/details/log_msg.h" #include "spdlog/details/synchronous_factory.h" #include "spdlog/sinks/base_sink.h" +#include +#include #include #include +#include // // qt_sink class @@ -45,9 +48,14 @@ protected: { memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); + auto start = std::chrono::system_clock::now(); string_view_t str = string_view_t(formatted.data(), formatted.size()); QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); + auto elapsed = std::chrono::system_clock::now() - start; + + std::cout << "qt_sink::sink_it_ took " + << std::chrono::duration_cast(elapsed).count() << " micros\n"; } void flush_() override {} @@ -57,15 +65,189 @@ private: std::string meta_method_; }; +// QT color sink to QTextEdit. +// Color location is determined by the sink log pattern like in the rest of spdlog sinks. +// Colors can be modified if needed using sink->set_color(level, qtTextCharFormat). +// Note: Only ascii (latin1) is supported by this sink. + template + class qt_color_sink : public base_sink + { + public: + qt_color_sink(QTextEdit *qt_text_edit, int max_lines) + : qt_text_edit_(qt_text_edit) + { + if (!qt_text_edit_) + { + throw_spdlog_ex("qt_color_text_sink: text_edit is null"); + } + max_lines_ = max_lines; + default_color_ = qt_text_edit_->currentCharFormat(); + // set colors + QTextCharFormat format; + // trace + format.setForeground(Qt::gray); + colors_.at(level::trace) = format; + // debug + format.setForeground(Qt::cyan); + colors_.at(level::debug) = format; + // info + format.setForeground(Qt::green); + colors_.at(level::info) = format; + // warn + format.setForeground(Qt::yellow); + colors_.at(level::warn) = format; + // err + format.setForeground(Qt::red); + colors_.at(level::err) = format; + // critical + format.setForeground(Qt::white); + format.setBackground(Qt::red); + colors_.at(level::critical) = format; + } + + ~qt_color_sink() + { + flush_(); + } + + void set_default_color(QTextCharFormat format) + { + // std::lock_guard lock(base_sink::mutex_); + default_color_ = format; + } + + void set_level_color(level::level_enum color_level, QTextCharFormat format) + { + // std::lock_guard lock(base_sink::mutex_); + colors_.at(static_cast(color_level)) = format; + } + + QTextCharFormat &get_level_color(level::level_enum color_level) + { + std::lock_guard lock(base_sink::mutex_); + return colors_.at(static_cast(color_level)); + } + + QTextCharFormat &get_default_color() + { + std::lock_guard lock(base_sink::mutex_); + return default_color_; + } + + protected: + struct invoke_params + { + invoke_params(int max_lines, QTextEdit *q_text_edit, QString payload, QTextCharFormat default_color, + QTextCharFormat level_color, int color_range_start, int color_range_end) + : max_lines(max_lines), + q_text_edit(q_text_edit), + payload(std::move(payload)), + default_color(default_color), + level_color(level_color), + color_range_start(color_range_start), + color_range_end(color_range_end) + { + } + int max_lines; + QTextEdit *q_text_edit; + QString payload; + QTextCharFormat default_color; + QTextCharFormat level_color; + int color_range_start; + int color_range_end; + }; + void sink_it_(const details::log_msg &msg) override + { + + using this_type = qt_color_sink; + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + + auto start = std::chrono::system_clock::now(); + string_view_t str = string_view_t(formatted.data(), formatted.size()); + // apply the color to the color range in the formatted message. + auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); + + // if color needed, apply it as + if (msg.color_range_end > msg.color_range_start) + { + invoke_params params { + max_lines_, // max lines + qt_text_edit_, // text edit to append to + std::move(payload), // text to append + default_color_, // default color + colors_.at(msg.level), // color to apply + msg.color_range_start, // color range start + msg.color_range_end}; // color range end + QMetaObject::invokeMethod( + qt_text_edit_, + [params]() {invoke_method_(params);}, + Qt::AutoConnection); + } + else // no color range, just append the text + { + auto *qt_text_edit = qt_text_edit_; + QMetaObject::invokeMethod( + qt_text_edit_, + [qt_text_edit, payload]() {qt_text_edit->append(payload.trimmed());}, + Qt::AutoConnection); + } + auto elapsed = std::chrono::duration_cast(std::chrono::system_clock::now() - start); + std::cout << "qt_color_sink::sink_it_ took " << elapsed.count() << " micros" << std::endl; + } + + void flush_() override {} + + // make this static so even if the sink get destroyed we can still can use it + static void invoke_method_(invoke_params params) + { + auto *document = params.q_text_edit->document(); + QTextCursor cursor(document); + + // remove first blocks if number of blocks exceeds max_lines + while(document->blockCount() > params.max_lines) + { + cursor.select(QTextCursor::BlockUnderCursor); + cursor.removeSelectedText(); + cursor.deleteChar(); // delete the newline after the block + } + + + cursor.movePosition(QTextCursor::End); + + // insert the text before the color range + cursor.setCharFormat(params.default_color); + cursor.insertText(params.payload.left(params.color_range_start)); + + // insert the colorized text + + cursor.setCharFormat(params.level_color); + cursor.insertText(params.payload.mid(params.color_range_start, params.color_range_end - params.color_range_start)); + + // insert the text after the color range with default format + cursor.setCharFormat(params.default_color); + cursor.insertText(params.payload.mid(params.color_range_end)); + } + + QTextEdit *qt_text_edit_; + int max_lines_; + QTextCharFormat default_color_; + std::array colors_; + }; + #include "spdlog/details/null_mutex.h" #include using qt_sink_mt = qt_sink; -using qt_sink_st = qt_sink; +using qt_sink_st = qt_sink; +using qt_color_sink_mt = qt_color_sink; +using qt_color_sink_st = qt_color_sink; } // namespace sinks // // Factory functions // + +// log to QTextEdit template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") { @@ -78,6 +260,7 @@ inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTex return Factory::template create(logger_name, qt_object, meta_method); } +// log to QPlainTextEdit template inline std::shared_ptr qt_logger_mt( const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") @@ -91,7 +274,7 @@ inline std::shared_ptr qt_logger_st( { return Factory::template create(logger_name, qt_object, meta_method); } - +// log to QObject template inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { @@ -103,4 +286,18 @@ inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObj { return Factory::template create(logger_name, qt_object, meta_method); } + +// log to QTextEdit with colorize output +template +inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines=5000) +{ + return Factory::template create(logger_name, qt_text_edit, max_lines); +} + +template +inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines=5000) +{ + return Factory::template create(logger_name, qt_text_edit); +} + } // namespace spdlog From ca44ce50ab56d0ab31c5a8b5d1cd19b63a8383a1 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 16:40:46 +0300 Subject: [PATCH 27/31] Cleaned qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 41 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 11ea41ac..b626d537 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -17,11 +17,9 @@ #include "spdlog/details/synchronous_factory.h" #include "spdlog/sinks/base_sink.h" #include -#include #include #include -#include // // qt_sink class @@ -48,14 +46,9 @@ protected: { memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); - auto start = std::chrono::system_clock::now(); string_view_t str = string_view_t(formatted.data(), formatted.size()); QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); - auto elapsed = std::chrono::system_clock::now() - start; - - std::cout << "qt_sink::sink_it_ took " - << std::chrono::duration_cast(elapsed).count() << " micros\n"; } void flush_() override {} @@ -68,19 +61,20 @@ private: // QT color sink to QTextEdit. // Color location is determined by the sink log pattern like in the rest of spdlog sinks. // Colors can be modified if needed using sink->set_color(level, qtTextCharFormat). +// max_lines is the maximum number of lines that the sink will hold before removing the oldest lines. // Note: Only ascii (latin1) is supported by this sink. template class qt_color_sink : public base_sink { public: qt_color_sink(QTextEdit *qt_text_edit, int max_lines) - : qt_text_edit_(qt_text_edit) + : qt_text_edit_(qt_text_edit), max_lines_(max_lines) { if (!qt_text_edit_) { throw_spdlog_ex("qt_color_text_sink: text_edit is null"); } - max_lines_ = max_lines; + default_color_ = qt_text_edit_->currentCharFormat(); // set colors QTextCharFormat format; @@ -163,13 +157,12 @@ private: memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); - auto start = std::chrono::system_clock::now(); string_view_t str = string_view_t(formatted.data(), formatted.size()); // apply the color to the color range in the formatted message. auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); // if color needed, apply it as - if (msg.color_range_end > msg.color_range_start) + //if (msg.color_range_end > msg.color_range_start) { invoke_params params { max_lines_, // max lines @@ -184,21 +177,14 @@ private: [params]() {invoke_method_(params);}, Qt::AutoConnection); } - else // no color range, just append the text - { - auto *qt_text_edit = qt_text_edit_; - QMetaObject::invokeMethod( - qt_text_edit_, - [qt_text_edit, payload]() {qt_text_edit->append(payload.trimmed());}, - Qt::AutoConnection); - } - auto elapsed = std::chrono::duration_cast(std::chrono::system_clock::now() - start); - std::cout << "qt_color_sink::sink_it_ took " << elapsed.count() << " micros" << std::endl; } void flush_() override {} - // make this static so even if the sink get destroyed we can still can use it + // Add colored text to the text edit widget. This method is invoked in the GUI thread. + // It is a static method to ensure that it is handled correctly even if the sink is destroyed prematurely + // before it is invoked. + static void invoke_method_(invoke_params params) { auto *document = params.q_text_edit->document(); @@ -212,15 +198,20 @@ private: cursor.deleteChar(); // delete the newline after the block } - cursor.movePosition(QTextCursor::End); + cursor.setCharFormat(params.default_color); + + // if color range not specified or not not valid, just append the text with default color + if(params.color_range_end <= params.color_range_start) + { + cursor.insertText(params.payload); + return; + } // insert the text before the color range - cursor.setCharFormat(params.default_color); cursor.insertText(params.payload.left(params.color_range_start)); // insert the colorized text - cursor.setCharFormat(params.level_color); cursor.insertText(params.payload.mid(params.color_range_start, params.color_range_end - params.color_range_start)); From 36eb173030921d7854b0712d092e034ff0845ca2 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 16:46:50 +0300 Subject: [PATCH 28/31] Updated README.md with qt color example --- README.md | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 72d2dd7b..45537d72 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Very fast, header-only/compiled, C++ logging library. [![ci](https://github.com/gabime/spdlog/actions/workflows/ci.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/ci.yml)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true&branch=v1.x)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) -## Install +## Install #### Header-only version Copy the include [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. @@ -12,14 +12,14 @@ $ git clone https://github.com/gabime/spdlog.git $ cd spdlog && mkdir build && cd build $ cmake .. && make -j ``` - - see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use. + +see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use. ## Platforms - * Linux, FreeBSD, OpenBSD, Solaris, AIX - * Windows (msvc 2013+, cygwin) - * macOS (clang 3.5+) - * Android +* Linux, FreeBSD, OpenBSD, Solaris, AIX +* Windows (msvc 2013+, cygwin) +* macOS (clang 3.5+) +* Android ## Package managers: * Debian: `sudo apt install libspdlog-dev` @@ -44,18 +44,18 @@ $ cmake .. && make -j * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * Multi/Single threaded loggers. * Various log targets: - * Rotating log files. - * Daily log files. - * Console logging (colors supported). - * syslog. - * Windows event log. - * Windows debugger (```OutputDebugString(..)```). - * Log to Qt widgets - * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. + * Rotating log files. + * Daily log files. + * Console logging (colors supported). + * syslog. + * Windows event log. + * Windows debugger (```OutputDebugString(..)```). + * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). + * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. * Log filtering - log levels can be modified at runtime as well as compile time. * Support for loading log levels from argv or environment var. * [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display them later on demand. - + ## Usage samples #### Basic usage @@ -271,7 +271,7 @@ void async_example() ``` --- -#### Asynchronous logger with multi sinks +#### Asynchronous logger with multi sinks ```c++ #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/rotating_file_sink.h" @@ -349,7 +349,7 @@ void err_handler_example() ``` --- -#### syslog +#### syslog ```c++ #include "spdlog/sinks/syslog_sink.h" void syslog_example() @@ -360,7 +360,7 @@ void syslog_example() } ``` --- -#### Android example +#### Android example ```c++ #include "spdlog/sinks/android_sink.h" void android_example() @@ -422,8 +422,21 @@ void replace_default_logger_example() ``` --- +#### Log to Qt with nice colors +```c++ +#include "spdlog/spdlog.h" +#include "spdlog/sinks/qt_sinks.h" +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + setMinimumSize(640, 480); + auto log_widget = new QTextEdit(this); + setCentralWidget(log_widget); + auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget); + logger->info("Some info message"); +} +``` - +--- ## Benchmarks Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz From 9ce729519174a9acc79ae0bdb9615b8cc9fdf53e Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 16:58:40 +0300 Subject: [PATCH 29/31] Make max_line explicit for qt_color_sink --- README.md | 3 ++- include/spdlog/sinks/qt_sinks.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 45537d72..f793e90f 100644 --- a/README.md +++ b/README.md @@ -431,7 +431,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) setMinimumSize(640, 480); auto log_widget = new QTextEdit(this); setCentralWidget(log_widget); - auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget); + int max_lines = 500; // keep widget to max 500 lines. remove old lines if needed. + auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines); logger->info("Some info message"); } ``` diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index b626d537..6c0c7c24 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -280,15 +280,15 @@ inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObj // log to QTextEdit with colorize output template -inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines=5000) +inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines) { return Factory::template create(logger_name, qt_text_edit, max_lines); } template -inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines=5000) +inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, QTextEdit *qt_text_edit, int max_lines) { - return Factory::template create(logger_name, qt_text_edit); + return Factory::template create(logger_name, qt_text_edit, max_lines); } } // namespace spdlog From a29cef5787739ec8c80081f10d1e015052d65afb Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 17:13:37 +0300 Subject: [PATCH 30/31] Make max_line explicit for qt_color_sink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f793e90f..35c03422 100644 --- a/README.md +++ b/README.md @@ -431,7 +431,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) setMinimumSize(640, 480); auto log_widget = new QTextEdit(this); setCentralWidget(log_widget); - int max_lines = 500; // keep widget to max 500 lines. remove old lines if needed. + int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed. auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines); logger->info("Some info message"); } From da1e671d4285fce4b2b1603be90afc652c4f4b65 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 17 Jun 2023 18:09:08 +0300 Subject: [PATCH 31/31] Clean qt_color_sink --- include/spdlog/sinks/qt_sinks.h | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 6c0c7c24..14a95bcc 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -157,26 +157,24 @@ private: memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); - string_view_t str = string_view_t(formatted.data(), formatted.size()); + const string_view_t str = string_view_t(formatted.data(), formatted.size()); // apply the color to the color range in the formatted message. auto payload = QString::fromLatin1(str.data(), static_cast(str.size())); - // if color needed, apply it as - //if (msg.color_range_end > msg.color_range_start) - { - invoke_params params { - max_lines_, // max lines - qt_text_edit_, // text edit to append to - std::move(payload), // text to append - default_color_, // default color - colors_.at(msg.level), // color to apply - msg.color_range_start, // color range start - msg.color_range_end}; // color range end - QMetaObject::invokeMethod( - qt_text_edit_, - [params]() {invoke_method_(params);}, - Qt::AutoConnection); - } + invoke_params params { + max_lines_, // max lines + qt_text_edit_, // text edit to append to + std::move(payload), // text to append + default_color_, // default color + colors_.at(msg.level), // color to apply + msg.color_range_start, // color range start + msg.color_range_end}; // color range end + + QMetaObject::invokeMethod( + qt_text_edit_, + [params]() {invoke_method_(params);}, + Qt::AutoConnection); + } void flush_() override {}