diff --git a/lib/qspdlog/qspdlog.pri b/lib/qspdlog/qspdlog.pri
new file mode 100644
index 00000000..21dd9a2b
--- /dev/null
+++ b/lib/qspdlog/qspdlog.pri
@@ -0,0 +1,18 @@
+SOURCES += \
+ $$PWD/spdlog/src/async.cpp \
+ $$PWD/spdlog/src/bundled_fmtlib_format.cpp \
+ $$PWD/spdlog/src/cfg.cpp \
+ $$PWD/spdlog/src/color_sinks.cpp \
+ $$PWD/spdlog/src/file_sinks.cpp \
+ $$PWD/spdlog/src/spdlog.cpp \
+ $$PWD/spdlog/src/stdout_sinks.cpp \
+ $$PWD/qspdlogger.cpp
+
+HEADERS += \
+ $$PWD/qspdlogger.h
+
+INCLUDEPATH += \
+ $$PWD/spdlog/include \
+ $$PWD
+
+DEFINES += SPDLOG_COMPILED_LIB
diff --git a/lib/qspdlog/qspdlogger.cpp b/lib/qspdlog/qspdlogger.cpp
new file mode 100644
index 00000000..d56e4d15
--- /dev/null
+++ b/lib/qspdlog/qspdlogger.cpp
@@ -0,0 +1,329 @@
+/*
+ * This file is part of the https://github.com/QQxiaoming/quardCRT.git
+ * project.
+ *
+ * Copyright (C) 2025 Quard <2014500726@smail.xtu.edu.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+#include "qspdlogger.h"
+#include "spdlog/async.h"
+#include "spdlog/sinks/stdout_color_sinks.h"
+#include "spdlog/sinks/rotating_file_sink.h"
+#include "spdlog/sinks/basic_file_sink.h"
+#include "spdlog/sinks/udp_sink.h"
+#include "spdlog/sinks/tcp_sink.h"
+#include "spdlog/sinks/dist_sink.h"
+#include
+#include
+#include
+#include
+
+static const QMap level_map = {
+ {QtDebugMsg, spdlog::level::debug},
+ {QtInfoMsg, spdlog::level::info},
+ {QtWarningMsg, spdlog::level::warn},
+ {QtFatalMsg, spdlog::level::err},
+ {QtCriticalMsg, spdlog::level::critical},
+};
+
+void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(type)) {
+ spd_level = level_map[type];
+ }
+ if(context.file == nullptr) {
+ spdlog::source_loc loc = {"UnKnow", 0, "UnKnow"};
+ spdlog::log(loc, spd_level, msg.toStdString());
+ } else {
+ spdlog::source_loc loc = {context.file, context.line, context.function};
+ spdlog::log(loc, spd_level, msg.toStdString());
+ }
+}
+
+QSpdLogger* QSpdLogger::self = nullptr;
+
+QSpdLogger* QSpdLogger::Instance() {
+ if(!self) {
+ QMutex muter;
+ QMutexLocker clocker(&muter);
+
+ if(!self) {
+ self = new QSpdLogger();
+ }
+ }
+ return self;
+}
+
+void QSpdLogger::installMessageHandler() {
+ try {
+ spdlog::init_thread_pool(8192, 1);
+ std::shared_ptr dist_sink = std::make_shared();
+ std::vector sinks = {dist_sink};
+ std::shared_ptr logger = std::make_shared("QSpdLogger", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
+ std::shared_ptr console_sink = std::make_shared();
+ dist_sink->add_sink(console_sink);
+ spdlog::set_default_logger(logger);
+ spdlog::set_level(spdlog::level::trace);
+ setGlobalLogPattern(logPattern);
+ } catch (const spdlog::spdlog_ex &ex) {
+ setGlobalLogPattern(logPattern);
+ spdlog::log(spdlog::level::err, "TySpdLog initialization failed\n");
+ }
+ qInstallMessageHandler(outputMessage);
+}
+
+void QSpdLogger::uninstallMessageHandler() {
+ qInstallMessageHandler(nullptr);
+ spdlog::drop_all();
+}
+
+int QSpdLogger::addFileSink(QString filename, uint32_t max_size, uint32_t max_files) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename.toStdString()) {
+ return -1;
+ }
+ }
+ if (auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename.toStdString()) {
+ return -1;
+ }
+ }
+ }
+ if(max_size == 0 || max_files == 0) {
+ std::shared_ptr file_sink = std::make_shared(filename.toStdString());
+ file_sink->set_pattern(logPattern.toStdString());
+ dist_sink->add_sink(file_sink);
+ } else {
+ std::shared_ptr file_sink = std::make_shared(filename.toStdString(), max_size, max_files);
+ file_sink->set_pattern(logPattern.toStdString());
+ dist_sink->add_sink(file_sink);
+ }
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return 0;
+}
+
+int QSpdLogger::removeFileSink(QString filename) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename.toStdString()) {
+ dist_sink->remove_sink(file_sink);
+ return 0;
+ }
+ }
+ if(auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename.toStdString()) {
+ dist_sink->remove_sink(file_sink);
+ return 0;
+ }
+ }
+ }
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return -1;
+}
+
+int QSpdLogger::addUdpSink(QString ip, uint16_t port) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto udp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::udp_sink_config config = udp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ return -1;
+ }
+ }
+ }
+ std::shared_ptr udp_sink = std::make_shared(spdlog::sinks::udp_sink_config(ip.toStdString(), port));
+ udp_sink->set_pattern(logPattern.toStdString());
+ dist_sink->add_sink(udp_sink);
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return 0;
+}
+
+int QSpdLogger::removeUdpSink(QString ip, uint16_t port) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto udp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::udp_sink_config config = udp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ dist_sink->remove_sink(udp_sink);
+ return 0;
+ }
+ }
+ }
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return -1;
+}
+
+int QSpdLogger::addTcpSink(QString ip, uint16_t port) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto tcp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::tcp_sink_config config = tcp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ return -1;
+ }
+ }
+ }
+ std::shared_ptr tcp_sink = std::make_shared(spdlog::sinks::tcp_sink_config(ip.toStdString(), port));
+ tcp_sink->set_pattern(logPattern.toStdString());
+ dist_sink->add_sink(tcp_sink);
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return 0;
+}
+
+int QSpdLogger::removeTcpSink(QString ip, uint16_t port) {
+ try {
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto tcp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::tcp_sink_config config = tcp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ dist_sink->remove_sink(tcp_sink);
+ return 0;
+ }
+ }
+ }
+ } catch (const spdlog::spdlog_ex &ex) {
+ return -1;
+ }
+ return -1;
+}
+
+int QSpdLogger::setGlobalLogPattern(const QString &format) {
+ logPattern = format;
+ spdlog::set_pattern(logPattern.toStdString());
+ return 0;
+}
+
+void QSpdLogger::clearGlobalLogPattern(void) {
+ spdlog::set_pattern("%v");
+}
+
+void QSpdLogger::setLogLevel(QtMsgType level) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(level)) {
+ spd_level = level_map[level];
+ }
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ (*it)->set_level(spd_level);
+ }
+}
+
+void QSpdLogger::setStdLogLevel(QtMsgType level) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(level)) {
+ spd_level = level_map[level];
+ }
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto console_sink = std::dynamic_pointer_cast(*it)) {
+ console_sink->set_level(spd_level);
+ }
+ }
+}
+
+void QSpdLogger::setFileSinkLogLevel(QString filename, QtMsgType level) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(level)) {
+ spd_level = level_map[level];
+ }
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename) {
+ file_sink->set_level(spd_level);
+ }
+ }
+ if (auto file_sink = std::dynamic_pointer_cast(*it)) {
+ if (file_sink->filename() == filename) {
+ file_sink->set_level(spd_level);
+ }
+ }
+ }
+}
+
+void QSpdLogger::setUdpSinkLogLevel(QString ip, uint16_t port, QtMsgType level) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(level)) {
+ spd_level = level_map[level];
+ }
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto udp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::udp_sink_config config = udp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ udp_sink->set_level(spd_level);
+ }
+ }
+ }
+}
+
+void QSpdLogger::setTcpSinkLogLevel(QString ip, uint16_t port, QtMsgType level) {
+ spdlog::level::level_enum spd_level = spdlog::level::info;
+ if(level_map.contains(level)) {
+ spd_level = level_map[level];
+ }
+ std::shared_ptr dist_sink = std::dynamic_pointer_cast(spdlog::default_logger()->sinks().front());
+ std::vector sinks = dist_sink->sinks();
+ for (auto it = sinks.begin(); it != sinks.end(); ++it) {
+ if (auto tcp_sink = std::dynamic_pointer_cast(*it)) {
+ const spdlog::sinks::tcp_sink_config config = tcp_sink->config();
+ if (config.server_host == ip.toStdString() && config.server_port == port) {
+ tcp_sink->set_level(spd_level);
+ }
+ }
+ }
+}
+
+QSpdLogger::QSpdLogger(QObject *parent) : QObject(parent) {
+ static QSpdLogger::GC gc;
+}
+
+QSpdLogger::~QSpdLogger() { }
+
+QSpdLogger::GC::~GC() {
+ if (self != nullptr) {
+ delete self;
+ self = nullptr;
+ }
+}
diff --git a/lib/qspdlog/qspdlogger.h b/lib/qspdlog/qspdlogger.h
new file mode 100644
index 00000000..8b290930
--- /dev/null
+++ b/lib/qspdlog/qspdlogger.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the https://github.com/QQxiaoming/quardCRT.git
+ * project.
+ *
+ * Copyright (C) 2025 Quard <2014500726@smail.xtu.edu.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+#ifndef QSPDQSpdLogger_H
+#define QSPDQSpdLogger_H
+
+#include
+#include
+#include "spdlog/spdlog.h"
+
+class QSpdLogger: public QObject
+{
+ Q_OBJECT
+private:
+ explicit QSpdLogger(QObject *parent = nullptr);
+ ~QSpdLogger();
+ static QSpdLogger* self;
+ QString logPattern = "(%Y-%m-%d %T:%f) [%10t] [%^%10l%$] [%s:%#:%!] %v";
+
+public:
+ static QSpdLogger* Instance();
+ void installMessageHandler();
+ void uninstallMessageHandler();
+ int addFileSink(QString filename, uint32_t max_size = 0, uint32_t max_files = 0);
+ int removeFileSink(QString filename);
+ int addUdpSink(QString ip, uint16_t port);
+ int removeUdpSink(QString ip, uint16_t port);
+ int addTcpSink(QString ip, uint16_t port);
+ int removeTcpSink(QString ip, uint16_t port);
+ int setGlobalLogPattern(const QString &format);
+ void clearGlobalLogPattern(void);
+ void setLogLevel(QtMsgType level);
+ void setStdLogLevel(QtMsgType level);
+ void setFileSinkLogLevel(QString filename, QtMsgType level);
+ void setUdpSinkLogLevel(QString ip, uint16_t port, QtMsgType level);
+ void setTcpSinkLogLevel(QString ip, uint16_t port, QtMsgType level);
+
+signals:
+ void sigDebugStrData(const QString &);
+ void sigDebugHtmlData(const QString &);
+
+private:
+ class GC
+ {
+ public:
+ ~GC();
+ };
+};
+
+#endif // QSPDQSpdLogger_H
diff --git a/lib/qspdlog/spdlog/include/spdlog/async.h b/lib/qspdlog/spdlog/include/spdlog/async.h
new file mode 100644
index 00000000..e96abd19
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/async.h
@@ -0,0 +1,100 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+//
+// Async logging using global thread pool
+// All loggers created here share same global thread pool.
+// Each log message is pushed to a queue along with a shared pointer to the
+// logger.
+// If a logger deleted while having pending messages in the queue, it's actual
+// destruction will defer
+// until all its messages are processed by the thread pool.
+// This is because each message in the queue holds a shared_ptr to the
+// originating logger.
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace spdlog {
+
+namespace details {
+static const size_t default_async_q_size = 8192;
+}
+
+// async logger factory - creates async loggers backed with thread pool.
+// if a global thread pool doesn't already exist, create it with default queue
+// size of 8192 items and single thread.
+template
+struct async_factory_impl {
+ template
+ static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) {
+ auto ®istry_inst = details::registry::instance();
+
+ // create global thread pool if not already exists..
+
+ auto &mutex = registry_inst.tp_mutex();
+ std::lock_guard tp_lock(mutex);
+ auto tp = registry_inst.get_tp();
+ if (tp == nullptr) {
+ tp = std::make_shared(details::default_async_q_size, 1U);
+ registry_inst.set_tp(tp);
+ }
+
+ auto sink = std::make_shared(std::forward(args)...);
+ auto new_logger = std::make_shared(std::move(logger_name), std::move(sink),
+ std::move(tp), OverflowPolicy);
+ registry_inst.initialize_logger(new_logger);
+ return new_logger;
+ }
+};
+
+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) {
+ 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) {
+ return async_factory_nonblock::create(std::move(logger_name),
+ std::forward(sink_args)...);
+}
+
+// set global thread pool.
+inline void init_thread_pool(size_t q_size,
+ size_t thread_count,
+ std::function on_thread_start,
+ std::function on_thread_stop) {
+ auto tp = std::make_shared(q_size, thread_count, on_thread_start,
+ on_thread_stop);
+ details::registry::instance().set_tp(std::move(tp));
+}
+
+inline void init_thread_pool(size_t q_size,
+ size_t thread_count,
+ std::function on_thread_start) {
+ init_thread_pool(q_size, thread_count, on_thread_start, [] {});
+}
+
+inline void init_thread_pool(size_t q_size, size_t thread_count) {
+ init_thread_pool(
+ q_size, thread_count, [] {}, [] {});
+}
+
+// get the global thread pool.
+inline std::shared_ptr thread_pool() {
+ return details::registry::instance().get_tp();
+}
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/async_logger-inl.h b/lib/qspdlog/spdlog/include/spdlog/async_logger-inl.h
new file mode 100644
index 00000000..1e794798
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/async_logger-inl.h
@@ -0,0 +1,84 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+#include
+#include
+
+SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
+ sinks_init_list sinks_list,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy)
+ : async_logger(std::move(logger_name),
+ sinks_list.begin(),
+ sinks_list.end(),
+ std::move(tp),
+ overflow_policy) {}
+
+SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
+ sink_ptr single_sink,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy)
+ : async_logger(
+ std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) {}
+
+// send the log message to the thread pool
+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 {
+ 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_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())
+}
+
+//
+// backend functions - called from the thread pool to do the actual job
+//
+SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) {
+ for (auto &sink : sinks_) {
+ if (sink->should_log(msg.level)) {
+ SPDLOG_TRY { sink->log(msg); }
+ SPDLOG_LOGGER_CATCH(msg.source)
+ }
+ }
+
+ if (should_flush_(msg)) {
+ backend_flush_();
+ }
+}
+
+SPDLOG_INLINE void spdlog::async_logger::backend_flush_() {
+ for (auto &sink : sinks_) {
+ SPDLOG_TRY { sink->flush(); }
+ SPDLOG_LOGGER_CATCH(source_loc())
+ }
+}
+
+SPDLOG_INLINE std::shared_ptr spdlog::async_logger::clone(std::string new_name) {
+ auto cloned = std::make_shared(*this);
+ cloned->name_ = std::move(new_name);
+ return cloned;
+}
diff --git a/lib/qspdlog/spdlog/include/spdlog/async_logger.h b/lib/qspdlog/spdlog/include/spdlog/async_logger.h
new file mode 100644
index 00000000..846c4c6f
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/async_logger.h
@@ -0,0 +1,74 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Fast asynchronous logger.
+// Uses pre allocated queue.
+// Creates a single back thread to pop messages from the queue and log them.
+//
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message
+// 2. Push a new copy of the message to a queue (or block the caller until
+// space is available in the queue)
+// Upon destruction, logs all remaining messages in the queue before
+// destructing..
+
+#include
+
+namespace spdlog {
+
+// Async overflow policy - block by default.
+enum class async_overflow_policy {
+ block, // Block until message can be enqueued
+ overrun_oldest, // Discard oldest message in the queue if full when trying to
+ // add new item.
+ discard_new // Discard new message if the queue is full when trying to add new item.
+};
+
+namespace details {
+class thread_pool;
+}
+
+class SPDLOG_API async_logger final : public std::enable_shared_from_this,
+ public logger {
+ friend class details::thread_pool;
+
+public:
+ template
+ async_logger(std::string logger_name,
+ It begin,
+ It end,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block)
+ : logger(std::move(logger_name), begin, end),
+ thread_pool_(std::move(tp)),
+ overflow_policy_(overflow_policy) {}
+
+ async_logger(std::string logger_name,
+ sinks_init_list sinks_list,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+ async_logger(std::string logger_name,
+ sink_ptr single_sink,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+ std::shared_ptr clone(std::string new_name) override;
+
+protected:
+ void sink_it_(const details::log_msg &msg) override;
+ void flush_() override;
+ void backend_sink_it_(const details::log_msg &incoming_log_msg);
+ void backend_flush_();
+
+private:
+ std::weak_ptr thread_pool_;
+ async_overflow_policy overflow_policy_;
+};
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "async_logger-inl.h"
+#endif
diff --git a/lib/qspdlog/spdlog/include/spdlog/cfg/argv.h b/lib/qspdlog/spdlog/include/spdlog/cfg/argv.h
new file mode 100644
index 00000000..7de2f83e
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/cfg/argv.h
@@ -0,0 +1,40 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include
+#include
+
+//
+// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
+//
+// set all loggers to debug level:
+// example.exe "SPDLOG_LEVEL=debug"
+
+// set logger1 to trace level
+// example.exe "SPDLOG_LEVEL=logger1=trace"
+
+// turn off all logging except for logger1 and logger2:
+// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+
+// search for SPDLOG_LEVEL= in the args and use it to init the levels
+inline void load_argv_levels(int argc, const char **argv) {
+ const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
+ for (int i = 1; i < argc; i++) {
+ std::string arg = argv[i];
+ if (arg.find(spdlog_level_prefix) == 0) {
+ auto levels_string = arg.substr(spdlog_level_prefix.size());
+ helpers::load_levels(levels_string);
+ }
+ }
+}
+
+inline void load_argv_levels(int argc, char **argv) {
+ load_argv_levels(argc, const_cast(argv));
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/cfg/env.h b/lib/qspdlog/spdlog/include/spdlog/cfg/env.h
new file mode 100644
index 00000000..6e554145
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/cfg/env.h
@@ -0,0 +1,36 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include
+#include
+#include
+
+//
+// Init levels and patterns from env variables SPDLOG_LEVEL
+// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
+// Note - fallback to "info" level on unrecognized levels
+//
+// Examples:
+//
+// set global level to debug:
+// export SPDLOG_LEVEL=debug
+//
+// turn off all logging except for logger1:
+// export SPDLOG_LEVEL="*=off,logger1=debug"
+//
+
+// turn off all logging except for logger1 and logger2:
+// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+inline void load_env_levels() {
+ auto env_val = details::os::getenv("SPDLOG_LEVEL");
+ if (!env_val.empty()) {
+ helpers::load_levels(env_val);
+ }
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/cfg/helpers-inl.h b/lib/qspdlog/spdlog/include/spdlog/cfg/helpers-inl.h
new file mode 100644
index 00000000..93650a2e
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/cfg/helpers-inl.h
@@ -0,0 +1,107 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+
+// inplace convert to lowercase
+inline std::string &to_lower_(std::string &str) {
+ std::transform(str.begin(), str.end(), str.begin(), [](char ch) {
+ return static_cast((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch);
+ });
+ return str;
+}
+
+// inplace trim spaces
+inline std::string &trim_(std::string &str) {
+ const char *spaces = " \n\r\t";
+ str.erase(str.find_last_not_of(spaces) + 1);
+ str.erase(0, str.find_first_not_of(spaces));
+ return str;
+}
+
+// return (name,value) trimmed pair from given "name=value" string.
+// return empty string on missing parts
+// "key=val" => ("key", "val")
+// " key = val " => ("key", "val")
+// "key=" => ("key", "")
+// "val" => ("", "val")
+
+inline std::pair extract_kv_(char sep, const std::string &str) {
+ auto n = str.find(sep);
+ std::string k, v;
+ if (n == std::string::npos) {
+ v = str;
+ } else {
+ k = str.substr(0, n);
+ v = str.substr(n + 1);
+ }
+ return std::make_pair(trim_(k), trim_(v));
+}
+
+// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
+// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
+inline std::unordered_map extract_key_vals_(const std::string &str) {
+ std::string token;
+ std::istringstream token_stream(str);
+ std::unordered_map rv{};
+ while (std::getline(token_stream, token, ',')) {
+ if (token.empty()) {
+ continue;
+ }
+ auto kv = extract_kv_('=', token);
+ rv[kv.first] = kv.second;
+ }
+ return rv;
+}
+
+SPDLOG_INLINE void load_levels(const std::string &input) {
+ if (input.empty() || input.size() > 512) {
+ return;
+ }
+
+ auto key_vals = extract_key_vals_(input);
+ std::unordered_map levels;
+ level::level_enum global_level = level::info;
+ bool global_level_found = false;
+
+ for (auto &name_level : key_vals) {
+ auto &logger_name = name_level.first;
+ auto level_name = to_lower_(name_level.second);
+ auto level = level::from_str(level_name);
+ // ignore unrecognized level names
+ if (level == level::off && level_name != "off") {
+ continue;
+ }
+ if (logger_name.empty()) // no logger name indicate global level
+ {
+ global_level_found = true;
+ global_level = level;
+ } else {
+ levels[logger_name] = level;
+ }
+ }
+
+ details::registry::instance().set_levels(std::move(levels),
+ global_level_found ? &global_level : nullptr);
+}
+
+} // namespace helpers
+} // namespace cfg
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/cfg/helpers.h b/lib/qspdlog/spdlog/include/spdlog/cfg/helpers.h
new file mode 100644
index 00000000..c0238189
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/cfg/helpers.h
@@ -0,0 +1,29 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+//
+// Init levels from given string
+//
+// Examples:
+//
+// set global level to debug: "debug"
+// turn off all logging except for logger1: "off,logger1=debug"
+// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
+//
+SPDLOG_API void load_levels(const std::string &txt);
+} // namespace helpers
+
+} // namespace cfg
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "helpers-inl.h"
+#endif // SPDLOG_HEADER_ONLY
diff --git a/lib/qspdlog/spdlog/include/spdlog/common-inl.h b/lib/qspdlog/spdlog/include/spdlog/common-inl.h
new file mode 100644
index 00000000..a8a0453c
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/common-inl.h
@@ -0,0 +1,68 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+namespace spdlog {
+namespace level {
+
+#if __cplusplus >= 201703L
+constexpr
+#endif
+ static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
+
+static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
+
+SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
+ return level_string_views[l];
+}
+
+SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
+ return short_level_names[l];
+}
+
+SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT {
+ auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
+ if (it != std::end(level_string_views))
+ return static_cast(std::distance(std::begin(level_string_views), it));
+
+ // check also for "warn" and "err" before giving up..
+ if (name == "warn") {
+ return level::warn;
+ }
+ if (name == "err") {
+ return level::err;
+ }
+ return level::off;
+}
+} // namespace level
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
+ : msg_(std::move(msg)) {}
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) {
+#ifdef SPDLOG_USE_STD_FORMAT
+ msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
+#else
+ memory_buf_t outbuf;
+ fmt::format_system_error(outbuf, last_errno, msg.c_str());
+ msg_ = fmt::to_string(outbuf);
+#endif
+}
+
+SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT { return msg_.c_str(); }
+
+SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno) {
+ SPDLOG_THROW(spdlog_ex(msg, last_errno));
+}
+
+SPDLOG_INLINE void throw_spdlog_ex(std::string msg) { SPDLOG_THROW(spdlog_ex(std::move(msg))); }
+
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/common.h b/lib/qspdlog/spdlog/include/spdlog/common.h
new file mode 100644
index 00000000..aca483c2
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/common.h
@@ -0,0 +1,411 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef SPDLOG_USE_STD_FORMAT
+ #include
+ #if __cpp_lib_format >= 202207L
+ #include
+ #else
+ #include
+ #endif
+#endif
+
+#ifdef SPDLOG_COMPILED_LIB
+ #undef SPDLOG_HEADER_ONLY
+ #if defined(SPDLOG_SHARED_LIB)
+ #if defined(_WIN32)
+ #ifdef spdlog_EXPORTS
+ #define SPDLOG_API __declspec(dllexport)
+ #else // !spdlog_EXPORTS
+ #define SPDLOG_API __declspec(dllimport)
+ #endif
+ #else // !defined(_WIN32)
+ #define SPDLOG_API __attribute__((visibility("default")))
+ #endif
+ #else // !defined(SPDLOG_SHARED_LIB)
+ #define SPDLOG_API
+ #endif
+ #define SPDLOG_INLINE
+#else // !defined(SPDLOG_COMPILED_LIB)
+ #define SPDLOG_API
+ #define SPDLOG_HEADER_ONLY
+ #define SPDLOG_INLINE inline
+#endif // #ifdef SPDLOG_COMPILED_LIB
+
+#include
+
+#if !defined(SPDLOG_USE_STD_FORMAT) && \
+ FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
+ #define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
+ #define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+ #include
+ #endif
+#else
+ #define SPDLOG_FMT_RUNTIME(format_string) format_string
+ #define SPDLOG_FMT_STRING(format_string) format_string
+#endif
+
+// visual studio up to 2013 does not support noexcept nor constexpr
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+ #define SPDLOG_NOEXCEPT _NOEXCEPT
+ #define SPDLOG_CONSTEXPR
+#else
+ #define SPDLOG_NOEXCEPT noexcept
+ #define SPDLOG_CONSTEXPR constexpr
+#endif
+
+// If building with std::format, can just use constexpr, otherwise if building with fmt
+// SPDLOG_CONSTEXPR_FUNC needs to be set the same as FMT_CONSTEXPR to avoid situations where
+// a constexpr function in spdlog could end up calling a non-constexpr function in fmt
+// depending on the compiler
+// If fmt determines it can't use constexpr, we should inline the function instead
+#ifdef SPDLOG_USE_STD_FORMAT
+ #define SPDLOG_CONSTEXPR_FUNC constexpr
+#else // Being built with fmt
+ #if FMT_USE_CONSTEXPR
+ #define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
+ #else
+ #define SPDLOG_CONSTEXPR_FUNC inline
+ #endif
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+ #define SPDLOG_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+ #define SPDLOG_DEPRECATED __declspec(deprecated)
+#else
+ #define SPDLOG_DEPRECATED
+#endif
+
+// disable thread local on msvc 2013
+#ifndef SPDLOG_NO_TLS
+ #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
+ #define SPDLOG_NO_TLS 1
+ #endif
+#endif
+
+#ifndef SPDLOG_FUNCTION
+ #define SPDLOG_FUNCTION static_cast(__FUNCTION__)
+#endif
+
+#ifdef SPDLOG_NO_EXCEPTIONS
+ #define SPDLOG_TRY
+ #define SPDLOG_THROW(ex) \
+ do { \
+ printf("spdlog fatal error: %s\n", ex.what()); \
+ std::abort(); \
+ } while (0)
+ #define SPDLOG_CATCH_STD
+#else
+ #define SPDLOG_TRY try
+ #define SPDLOG_THROW(ex) throw(ex)
+ #define SPDLOG_CATCH_STD \
+ catch (const std::exception &) { \
+ }
+#endif
+
+namespace spdlog {
+
+class formatter;
+
+namespace sinks {
+class sink;
+}
+
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+using filename_t = std::wstring;
+ // allow macro expansion to occur in SPDLOG_FILENAME_T
+ #define SPDLOG_FILENAME_T_INNER(s) L##s
+ #define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
+#else
+using filename_t = std::string;
+ #define SPDLOG_FILENAME_T(s) s
+#endif
+
+using log_clock = std::chrono::system_clock;
+using sink_ptr = std::shared_ptr;
+using sinks_init_list = std::initializer_list;
+using err_handler = std::function;
+#ifdef SPDLOG_USE_STD_FORMAT
+namespace fmt_lib = std;
+
+using string_view_t = std::string_view;
+using memory_buf_t = std::string;
+
+template
+ #if __cpp_lib_format >= 202207L
+using format_string_t = std::format_string;
+ #else
+using format_string_t = std::string_view;
+ #endif
+
+template
+struct is_convertible_to_basic_format_string
+ : std::integral_constant>::value> {};
+
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = std::wstring_view;
+using wmemory_buf_t = std::wstring;
+
+template
+ #if __cpp_lib_format >= 202207L
+using wformat_string_t = std::wformat_string;
+ #else
+using wformat_string_t = std::wstring_view;
+ #endif
+ #endif
+ #define SPDLOG_BUF_TO_STRING(x) x
+#else // use fmt lib instead of std::format
+namespace fmt_lib = fmt;
+
+using string_view_t = fmt::basic_string_view;
+using memory_buf_t = fmt::basic_memory_buffer;
+
+template
+using format_string_t = fmt::format_string;
+
+template
+using remove_cvref_t = typename std::remove_cv::type>::type;
+
+template
+ #if FMT_VERSION >= 90101
+using fmt_runtime_string = fmt::runtime_format_string;
+ #else
+using fmt_runtime_string = fmt::basic_runtime;
+ #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
+template
+struct is_convertible_to_basic_format_string
+ : std::integral_constant>::value ||
+ std::is_same, fmt_runtime_string>::value> {
+};
+
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = fmt::basic_string_view;
+using wmemory_buf_t = fmt::basic_memory_buffer;
+
+template
+using wformat_string_t = fmt::wformat_string;
+ #endif
+ #define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
+#endif
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+ #ifndef _WIN32
+ #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+ #endif // _WIN32
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+template
+struct is_convertible_to_any_format_string
+ : std::integral_constant::value ||
+ is_convertible_to_basic_format_string::value> {};
+
+#if defined(SPDLOG_NO_ATOMIC_LEVELS)
+using level_t = details::null_atomic_int;
+#else
+using level_t = std::atomic;
+#endif
+
+#define SPDLOG_LEVEL_TRACE 0
+#define SPDLOG_LEVEL_DEBUG 1
+#define SPDLOG_LEVEL_INFO 2
+#define SPDLOG_LEVEL_WARN 3
+#define SPDLOG_LEVEL_ERROR 4
+#define SPDLOG_LEVEL_CRITICAL 5
+#define SPDLOG_LEVEL_OFF 6
+
+#if !defined(SPDLOG_ACTIVE_LEVEL)
+ #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#endif
+
+// Log level enum
+namespace level {
+enum level_enum : int {
+ trace = SPDLOG_LEVEL_TRACE,
+ debug = SPDLOG_LEVEL_DEBUG,
+ info = SPDLOG_LEVEL_INFO,
+ warn = SPDLOG_LEVEL_WARN,
+ err = SPDLOG_LEVEL_ERROR,
+ critical = SPDLOG_LEVEL_CRITICAL,
+ off = SPDLOG_LEVEL_OFF,
+ n_levels
+};
+
+#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
+#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
+#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
+#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
+#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
+#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
+#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
+
+#if !defined(SPDLOG_LEVEL_NAMES)
+ #define SPDLOG_LEVEL_NAMES \
+ { \
+ SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, \
+ SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, \
+ SPDLOG_LEVEL_NAME_OFF \
+ }
+#endif
+
+#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
+
+ #define SPDLOG_SHORT_LEVEL_NAMES \
+ { "T", "D", "I", "W", "E", "C", "O" }
+#endif
+
+SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
+
+} // namespace level
+
+//
+// Color mode used by sinks with color support.
+//
+enum class color_mode { always, automatic, never };
+
+//
+// Pattern time - specific time getting to use for pattern_formatter.
+// local time by default
+//
+enum class pattern_time_type {
+ local, // log localtime
+ utc // log utc
+};
+
+//
+// Log exception
+//
+class SPDLOG_API spdlog_ex : public std::exception {
+public:
+ explicit spdlog_ex(std::string msg);
+ spdlog_ex(const std::string &msg, int last_errno);
+ const char *what() const SPDLOG_NOEXCEPT override;
+
+private:
+ std::string msg_;
+};
+
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
+
+struct source_loc {
+ SPDLOG_CONSTEXPR source_loc() = default;
+ SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
+ : filename{filename_in},
+ line{line_in},
+ funcname{funcname_in} {}
+
+ SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line <= 0; }
+ const char *filename{nullptr};
+ int line{0};
+ const char *funcname{nullptr};
+};
+
+struct file_event_handlers {
+ file_event_handlers()
+ : before_open(nullptr),
+ after_open(nullptr),
+ before_close(nullptr),
+ after_close(nullptr) {}
+
+ std::function before_open;
+ std::function after_open;
+ std::function before_close;
+ std::function after_close;
+};
+
+namespace details {
+
+// to_string_view
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
+ SPDLOG_NOEXCEPT {
+ return spdlog::string_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str)
+ SPDLOG_NOEXCEPT {
+ return str;
+}
+
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf)
+ SPDLOG_NOEXCEPT {
+ return spdlog::wstring_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str)
+ SPDLOG_NOEXCEPT {
+ return str;
+}
+#endif
+
+#ifndef SPDLOG_USE_STD_FORMAT
+template
+inline fmt::basic_string_view to_string_view(fmt::basic_format_string fmt) {
+ return fmt;
+}
+#elif __cpp_lib_format >= 202207L
+template
+SPDLOG_CONSTEXPR_FUNC std::basic_string_view to_string_view(
+ std::basic_format_string fmt) SPDLOG_NOEXCEPT {
+ return fmt.get();
+}
+#endif
+
+// make_unique support for pre c++14
+#if __cplusplus >= 201402L // C++14 and beyond
+using std::enable_if_t;
+using std::make_unique;
+#else
+template
+using enable_if_t = typename std::enable_if::type;
+
+template
+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)...));
+}
+#endif
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template ::value, int> = 0>
+constexpr T conditional_static_cast(U value) {
+ return static_cast(value);
+}
+
+template ::value, int> = 0>
+constexpr T conditional_static_cast(U value) {
+ return value;
+}
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "common-inl.h"
+#endif
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/backtracer-inl.h b/lib/qspdlog/spdlog/include/spdlog/details/backtracer-inl.h
new file mode 100644
index 00000000..43d10024
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/backtracer-inl.h
@@ -0,0 +1,63 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+namespace spdlog {
+namespace details {
+SPDLOG_INLINE backtracer::backtracer(const backtracer &other) {
+ std::lock_guard lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = other.messages_;
+}
+
+SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT {
+ std::lock_guard lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+}
+
+SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) {
+ std::lock_guard lock(mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+ return *this;
+}
+
+SPDLOG_INLINE void backtracer::enable(size_t size) {
+ std::lock_guard lock{mutex_};
+ enabled_.store(true, std::memory_order_relaxed);
+ messages_ = circular_q{size};
+}
+
+SPDLOG_INLINE void backtracer::disable() {
+ std::lock_guard lock{mutex_};
+ enabled_.store(false, std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); }
+
+SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) {
+ std::lock_guard lock{mutex_};
+ messages_.push_back(log_msg_buffer{msg});
+}
+
+SPDLOG_INLINE bool backtracer::empty() const {
+ std::lock_guard lock{mutex_};
+ return messages_.empty();
+}
+
+// pop all items in the q and apply the given fun on each of them.
+SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) {
+ std::lock_guard lock{mutex_};
+ while (!messages_.empty()) {
+ auto &front_msg = messages_.front();
+ fun(front_msg);
+ messages_.pop_front();
+ }
+}
+} // namespace details
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/backtracer.h b/lib/qspdlog/spdlog/include/spdlog/details/backtracer.h
new file mode 100644
index 00000000..541339cd
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/backtracer.h
@@ -0,0 +1,45 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+
+// Store log messages in circular buffer.
+// Useful for storing debug data in case of error/warning happens.
+
+namespace spdlog {
+namespace details {
+class SPDLOG_API backtracer {
+ mutable std::mutex mutex_;
+ std::atomic enabled_{false};
+ circular_q messages_;
+
+public:
+ backtracer() = default;
+ backtracer(const backtracer &other);
+
+ backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
+ backtracer &operator=(backtracer other);
+
+ void enable(size_t size);
+ void disable();
+ bool enabled() const;
+ void push_back(const log_msg &msg);
+ bool empty() const;
+
+ // pop all items in the q and apply the given fun on each of them.
+ void foreach_pop(std::function fun);
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "backtracer-inl.h"
+#endif
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/circular_q.h b/lib/qspdlog/spdlog/include/spdlog/details/circular_q.h
new file mode 100644
index 00000000..29e9d255
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/circular_q.h
@@ -0,0 +1,115 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// circular q view of std::vector.
+#pragma once
+
+#include
+#include
+
+#include "spdlog/common.h"
+
+namespace spdlog {
+namespace details {
+template
+class circular_q {
+ size_t max_items_ = 0;
+ typename std::vector::size_type head_ = 0;
+ typename std::vector::size_type tail_ = 0;
+ size_t overrun_counter_ = 0;
+ std::vector v_;
+
+public:
+ using value_type = T;
+
+ // empty ctor - create a disabled queue with no elements allocated at all
+ circular_q() = default;
+
+ explicit circular_q(size_t max_items)
+ : max_items_(max_items + 1) // one item is reserved as marker for full q
+ ,
+ v_(max_items_) {}
+
+ circular_q(const circular_q &) = default;
+ circular_q &operator=(const circular_q &) = default;
+
+ // move cannot be default,
+ // since we need to reset head_, tail_, etc to zero in the moved object
+ circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
+
+ circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT {
+ copy_moveable(std::move(other));
+ return *this;
+ }
+
+ // push back, overrun (oldest) item if no room left
+ void push_back(T &&item) {
+ if (max_items_ > 0) {
+ v_[tail_] = std::move(item);
+ tail_ = (tail_ + 1) % max_items_;
+
+ if (tail_ == head_) // overrun last item if full
+ {
+ head_ = (head_ + 1) % max_items_;
+ ++overrun_counter_;
+ }
+ }
+ }
+
+ // Return reference to the front item.
+ // If there are no elements in the container, the behavior is undefined.
+ const T &front() const { return v_[head_]; }
+
+ T &front() { return v_[head_]; }
+
+ // Return number of elements actually stored
+ size_t size() const {
+ if (tail_ >= head_) {
+ return tail_ - head_;
+ } else {
+ return max_items_ - (head_ - tail_);
+ }
+ }
+
+ // Return const reference to item by index.
+ // If index is out of range 0…size()-1, the behavior is undefined.
+ const T &at(size_t i) const {
+ assert(i < size());
+ return v_[(head_ + i) % max_items_];
+ }
+
+ // Pop item from front.
+ // If there are no elements in the container, the behavior is undefined.
+ void pop_front() { head_ = (head_ + 1) % max_items_; }
+
+ bool empty() const { return tail_ == head_; }
+
+ bool full() const {
+ // head is ahead of the tail by 1
+ if (max_items_ > 0) {
+ return ((tail_ + 1) % max_items_) == head_;
+ }
+ return false;
+ }
+
+ size_t overrun_counter() const { return overrun_counter_; }
+
+ void reset_overrun_counter() { overrun_counter_ = 0; }
+
+private:
+ // copy from other&& and reset it to disabled state
+ void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT {
+ max_items_ = other.max_items_;
+ head_ = other.head_;
+ tail_ = other.tail_;
+ overrun_counter_ = other.overrun_counter_;
+ v_ = std::move(other.v_);
+
+ // put &&other in disabled, but valid state
+ other.max_items_ = 0;
+ other.head_ = other.tail_ = 0;
+ other.overrun_counter_ = 0;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/console_globals.h b/lib/qspdlog/spdlog/include/spdlog/details/console_globals.h
new file mode 100644
index 00000000..9c552106
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/console_globals.h
@@ -0,0 +1,28 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+struct console_mutex {
+ using mutex_t = std::mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+
+struct console_nullmutex {
+ using mutex_t = null_mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/file_helper-inl.h b/lib/qspdlog/spdlog/include/spdlog/details/file_helper-inl.h
new file mode 100644
index 00000000..37d1d46f
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/file_helper-inl.h
@@ -0,0 +1,152 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
+ : event_handlers_(event_handlers) {}
+
+SPDLOG_INLINE file_helper::~file_helper() { close(); }
+
+SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) {
+ close();
+ filename_ = fname;
+
+ auto *mode = SPDLOG_FILENAME_T("ab");
+ auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+
+ if (event_handlers_.before_open) {
+ event_handlers_.before_open(filename_);
+ }
+ for (int tries = 0; tries < open_tries_; ++tries) {
+ // create containing folder if not exists already.
+ os::create_dir(os::dir_name(fname));
+ if (truncate) {
+ // Truncate by opening-and-closing a tmp file in "wb" mode, always
+ // opening the actual log-we-write-to in "ab" mode, since that
+ // interacts more politely with eternal processes that might
+ // rotate/truncate the file underneath us.
+ std::FILE *tmp;
+ if (os::fopen_s(&tmp, fname, trunc_mode)) {
+ continue;
+ }
+ std::fclose(tmp);
+ }
+ if (!os::fopen_s(&fd_, fname, mode)) {
+ if (event_handlers_.after_open) {
+ event_handlers_.after_open(filename_, fd_);
+ }
+ return;
+ }
+
+ details::os::sleep_for_millis(open_interval_);
+ }
+
+ throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing",
+ errno);
+}
+
+SPDLOG_INLINE void file_helper::reopen(bool truncate) {
+ if (filename_.empty()) {
+ throw_spdlog_ex("Failed re opening file - was not opened before");
+ }
+ this->open(filename_, truncate);
+}
+
+SPDLOG_INLINE void file_helper::flush() {
+ if (std::fflush(fd_) != 0) {
+ throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::sync() {
+ if (!os::fsync(fd_)) {
+ throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::close() {
+ if (fd_ != nullptr) {
+ if (event_handlers_.before_close) {
+ event_handlers_.before_close(filename_, fd_);
+ }
+
+ std::fclose(fd_);
+ fd_ = nullptr;
+
+ if (event_handlers_.after_close) {
+ event_handlers_.after_close(filename_);
+ }
+ }
+}
+
+SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
+ if (fd_ == nullptr) return;
+ size_t msg_size = buf.size();
+ auto data = buf.data();
+ if (std::fwrite(data, 1, msg_size, fd_) != msg_size) {
+ throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE size_t file_helper::size() const {
+ if (fd_ == nullptr) {
+ throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+ }
+ return os::filesize(fd_);
+}
+
+SPDLOG_INLINE const filename_t &file_helper::filename() const { return filename_; }
+
+//
+// return file path and its extension:
+//
+// "mylog.txt" => ("mylog", ".txt")
+// "mylog" => ("mylog", "")
+// "mylog." => ("mylog.", "")
+// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+//
+// the starting dot in filenames is ignored (hidden files):
+//
+// ".mylog" => (".mylog". "")
+// "my_folder/.mylog" => ("my_folder/.mylog", "")
+// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+SPDLOG_INLINE std::tuple file_helper::split_by_extension(
+ const filename_t &fname) {
+ auto ext_index = fname.rfind('.');
+
+ // no valid extension found - return whole path and empty string as
+ // extension
+ if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+ auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
+ if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // finally - return a valid base and extension tuple
+ return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/file_helper.h b/lib/qspdlog/spdlog/include/spdlog/details/file_helper.h
new file mode 100644
index 00000000..f0e5d180
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/file_helper.h
@@ -0,0 +1,61 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+// Helper class for file sinks.
+// When failing to open a file, retry several times(5) with a delay interval(10 ms).
+// Throw spdlog_ex exception on errors.
+
+class SPDLOG_API file_helper {
+public:
+ file_helper() = default;
+ explicit file_helper(const file_event_handlers &event_handlers);
+
+ file_helper(const file_helper &) = delete;
+ file_helper &operator=(const file_helper &) = delete;
+ ~file_helper();
+
+ void open(const filename_t &fname, bool truncate = false);
+ void reopen(bool truncate);
+ void flush();
+ void sync();
+ void close();
+ void write(const memory_buf_t &buf);
+ size_t size() const;
+ const filename_t &filename() const;
+
+ //
+ // return file path and its extension:
+ //
+ // "mylog.txt" => ("mylog", ".txt")
+ // "mylog" => ("mylog", "")
+ // "mylog." => ("mylog.", "")
+ // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+ //
+ // the starting dot in filenames is ignored (hidden files):
+ //
+ // ".mylog" => (".mylog". "")
+ // "my_folder/.mylog" => ("my_folder/.mylog", "")
+ // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+ static std::tuple split_by_extension(const filename_t &fname);
+
+private:
+ const int open_tries_ = 5;
+ const unsigned int open_interval_ = 10;
+ std::FILE *fd_{nullptr};
+ filename_t filename_;
+ file_event_handlers event_handlers_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "file_helper-inl.h"
+#endif
diff --git a/lib/qspdlog/spdlog/include/spdlog/details/fmt_helper.h b/lib/qspdlog/spdlog/include/spdlog/details/fmt_helper.h
new file mode 100644
index 00000000..61306003
--- /dev/null
+++ b/lib/qspdlog/spdlog/include/spdlog/details/fmt_helper.h
@@ -0,0 +1,141 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef SPDLOG_USE_STD_FORMAT
+ #include