#qt spdlog库简介 spdlog 是一个高性能的 C++ 日志库,它设计时充分考虑了速度和易用性,具有以下特点 高效与快速:Spdlog 专注于提供极致的性能,在大量日志记录场景下也能保持较低的延迟和较高的吞吐量。 轻量化设计:Spdlog 是头文件(header-only)库,这意味着用户只需要包含相应的头文件即可开始使用,无需编译链接额外的库文件。 跨平台支持:它支持多种操作系统,包括但不限于 Windows、Linux 和 macOS,并且在这些平台上都能够良好运行。 丰富的日志级别:Spdlog 支持常见的日志级别,如 TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL 等,用户可以根据需要选择不同级别的日志输出。 格式化与定位信息:通过集成 fmt 库,Spdlog 允许用户自定义日志消息的格式,可以轻松地包含时间戳、线程ID、文件名、行号以及函数名等上下文信息。 多目标输出:可以将日志输出到控制台、普通文本文件、循环写入文件(rotating log files)、每日生成新文件(daily logs)、系统日志等目标,同时也支持异步写入以提高性能。 线程安全:对于多线程环境,Spdlog 提供了线程安全的日志接口,确保在并发环境下日志记录的正确性和完整性。 异步模式:提供可选的异步日志记录机制,能够将日志操作放入后台线程执行,从而避免阻塞主线程。 条件日志:根据预定义的条件开关,可以动态启用或禁用特定级别的日志输出。 git链接 ``` https://github.com/gabime/spdlog.git ``` 先从git链接,拉取对应的代码库, 需要用到的, 是这里的库文件 ![[Pasted image 20240706164624.png]] 新建一个测试工程,把开源代码,拷贝过来即可 ![[Pasted image 20240706164731.png]] 编译方式用的Qt默认MinGW ![[Pasted image 20240706170105.png]] 新建c++的一对文件,命名为 Logger即可,主要是用来调用,开源库相关 头文件 ``` cpp #ifndef LOGGER_H #define LOGGER_H #include #include #include #include #include #include #include #include #include namespace Log { extern QDialog* dialog; extern QString output; } class Logger { public: static void initialize(const QString& log_dir=""); // 初始化日志系统 static void shutdown(); // 清理日志系统 static void setLevel(spdlog::level::level_enum logLevel); // 设置日志级别 static void setDialog(QDialog* dialog); static void setColor(const QString& color) {m_logColor = color;} private: static void qtMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); private: static std::shared_ptr m_logger; static QString m_logFilePath; static QString m_logColor; }; #endif // LOGGER_H ``` cpp ``` cpp #include "logger.h" #include #include #include #include #include std::shared_ptr Logger::m_logger; QString Logger::m_logFilePath; QString Logger::m_logColor; namespace Log { QDialog* dialog=nullptr; QString output; } void Logger::initialize(const QString& log_dir) { QString name = QDate::currentDate().toString("yyyy-MM-dd") + ".log"; if(log_dir.isEmpty()){ // QDir dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); QDir dir = QCoreApplication::applicationDirPath(); dir.mkpath("logs"); dir.cd("logs"); m_logFilePath = dir.filePath(name); }else{ QDir dir(log_dir); dir.mkpath("."); m_logFilePath = dir.filePath(name); } // 创建标准输出的彩色日志 auto console_sink = std::make_shared(); auto file_sink = std::make_shared(m_logFilePath.toLocal8Bit().toStdString(), 1024*1024, 3); // 设置模式字符串,%^ 和 %$ 分别代表颜色范围的开始和结束 console_sink->set_pattern("%^[%H:%M:%S.%e] [%L] [%t] %v%$"); file_sink->set_pattern("[%H:%M:%S.%e] [%L] [%t] %v"); spdlog::sinks_init_list sink_list = { console_sink, file_sink }; m_logger = std::make_shared("logger", sink_list.begin(), sink_list.end()); m_logger->set_level(spdlog::level::info); // 默认日志级别 // spdlog::flush_every(std::chrono::seconds(3)); // 设置自动间隔 qInstallMessageHandler(qtMessageHandler); qInfo() << "*************************** Log Start ********************************"; } void Logger::shutdown() { qInfo() << "*************************** Log End ********************************\n"; spdlog::drop_all(); // 清理所有日志实例 } void Logger::setLevel(spdlog::level::level_enum logLevel) { m_logger->set_level(logLevel); } void Logger::setDialog(QDialog *dialog) { Log::dialog = dialog; } void Logger::qtMessageHandler(QtMsgType type, const QMessageLogContext &/*context*/, const QString &msg) { QByteArray local_msg = msg.toLocal8Bit(); QString log_color; QString log_type; switch(type) { case QtDebugMsg: log_color = "black"; log_type = "[D]"; m_logger->debug(local_msg.constData()); break; case QtWarningMsg: log_color = "orange"; log_type = "[W]"; m_logger->warn(local_msg.constData()); break; case QtCriticalMsg: log_color = "red"; log_type = "[E]"; m_logger->critical(local_msg.constData()); break; case QtFatalMsg: log_color = "red"; log_type = "[C]"; m_logger->error(local_msg.constData()); break; case QtInfoMsg: log_color = "blue"; log_type = "[I] "; m_logger->info(local_msg.constData()); break; default: break; } m_logger->flush(); if(!m_logColor.isEmpty()){ log_color = m_logColor; } QString log = QString("%2 %3").arg(log_color, log_type, msg); m_logColor.clear(); Log::output.append(log+"
"); if(Log::dialog){ // QMetaObject::invokeMethod(Log::dialog, "flush", Qt::QueuedConnection); QMetaObject::invokeMethod(Log::dialog, "append", Qt::QueuedConnection, Q_ARG(QString, log)); } } ``` 新建一个测试的Dialog窗体,命名是DebugDialog即可,主要是用来,测试spdlog框架,是否正常使用; 新建一个textedit文本输入 ![[Pasted image 20240706170941.png]] 如果需要,只读,勾选这个属性即可; ![[Pasted image 20240729143353.png]] 根节点设置一下宽高 ![[Pasted image 20240706170911.png]] 整布局,文本框,就会自动适配了 ![[Pasted image 20240706171007.png]] 头文件 ``` cpp #ifndef DEBUGDIALOG_H #define DEBUGDIALOG_H #include namespace Ui { class DebugDialog; } class DebugDialog : public QDialog { Q_OBJECT public: explicit DebugDialog(QWidget *parent = nullptr); ~DebugDialog(); protected: void closeEvent(QCloseEvent *event); private: void initShortcut(); void findSelectedText(bool up); QString getSelectedText(); public slots: void flush(); void append(const QString& log); private: Ui::DebugDialog *ui; }; #endif // DEBUGDIALOG_H ``` cpp文件 ``` cpp #include "debugdialog.h" #include "ui_debugdialog.h" #include "logger.h" #include #include #include #include #include #include #include #include DebugDialog::DebugDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::DebugDialog) { setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowMinMaxButtonsHint); ui->setupUi(this); this->resize(960, 540); // this->setWindowIcon(QIcon(QPixmap(":/icon/扫码-电脑-悬停.png"))); this->setWindowTitle("调试日志"); ui->textEdit->setContextMenuPolicy(Qt::NoContextMenu); Logger::setDialog(this); connect(qApp, &QApplication::aboutToQuit, this, &DebugDialog::deleteLater); connect(this, &DebugDialog::rejected, this, &DebugDialog::deleteLater); this->setShortcutEnabled(true); initShortcut(); flush(); } DebugDialog::~DebugDialog() { Logger::setDialog(nullptr); delete ui; } void DebugDialog::closeEvent(QCloseEvent *event) { Logger::setDialog(nullptr); QDialog::closeEvent(event); } void DebugDialog::initShortcut() { QShortcut *search = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this); connect(search, &QShortcut::activated, this, [this](){ // qDebug() << "搜索内容"; QInputDialog dialog(this); dialog.setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); dialog.setInputMode(QInputDialog::TextInput); dialog.setWindowTitle("查找"); dialog.setOkButtonText("确定"); dialog.setCancelButtonText("取消"); dialog.setLabelText("搜索内容:"); dialog.setTextValue(getSelectedText()); dialog.resize(this->width()/2, dialog.height()); if(dialog.exec() == QDialog::Accepted){ QString text = dialog.textValue(); if(text.isEmpty()) return; if(!ui->textEdit->find(text)){ ui->textEdit->moveCursor(QTextCursor::Start); ui->textEdit->find(text); } } }); QShortcut *find_up = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up), this); connect(find_up, &QShortcut::activated, this, [this](){ findSelectedText(true); }); QShortcut *find_down = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down), this); connect(find_down, &QShortcut::activated, this, [this](){ findSelectedText(false); }); } void DebugDialog::findSelectedText(bool up) { QString text = getSelectedText(); if(text.isEmpty()) return; QTextDocument::FindFlags flags; flags |= QTextDocument::FindCaseSensitively; // 设置区分大小标志 if (up) { flags |= QTextDocument::FindBackward; // 设置向后查找标志 } bool found = ui->textEdit->find(text, flags); if (!found) { // 如果没有找到,根据查找方向移动光标并重新查找 QTextCursor::MoveOperation moveOp = up ? QTextCursor::End : QTextCursor::Start; ui->textEdit->moveCursor(moveOp); ui->textEdit->find(text, flags); } } QString DebugDialog::getSelectedText() { QTextCursor cursor = ui->textEdit->textCursor(); // 获取当前文本光标 if (cursor.hasSelection()) { // 检查是否有选中的文本 QString text = cursor.selectedText(); // 获取选中的文本 // qDebug() << "Selected text:" << text; return text; } else { // qDebug() << "No text is selected"; return ""; } } void DebugDialog::flush() { QString output = Log::output; output.chop(4); ui->textEdit->setHtml(output); ui->textEdit->verticalScrollBar()->setValue(ui->textEdit->verticalScrollBar()->maximum()); } void DebugDialog::append(const QString &log) { ui->textEdit->append(log); ui->textEdit->verticalScrollBar()->setValue(ui->textEdit->verticalScrollBar()->maximum()); } ``` main主函数调用和测试 ``` cpp #include #include #include "logger.h" #include "debugdialog.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); // Dialog w; // w.show(); QDir dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); Logger::initialize(dir.absoluteFilePath("QiuQiuKit/logs")); Logger::setLevel(spdlog::level::debug); Logger::setColor("#F56C6C"); if(!Log::dialog){ DebugDialog *dialog = new DebugDialog; dialog->setWindowTitle("qiuqiu"); dialog->show(); }else{ if(Log::dialog->isMinimized()){ Log::dialog->showNormal(); }else{ Log::dialog->show(); } Log::dialog->raise(); } qDebug().noquote() << "Game.Log:" << QString("testMessage"); return a.exec(); } ``` ![[Pasted image 20240706171156.png]]