spdlog功能整理.md 12 KB

#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即可,主要是用来调用,开源库相关

头文件

#ifndef LOGGER_H
#define LOGGER_H

#include <memory>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/fmt/ostr.h>
#include <QString>
#include <QDialog>


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<spdlog::logger> m_logger;
    static QString m_logFilePath;
    static QString m_logColor;
};



#endif // LOGGER_H

cpp

#include "logger.h"

#include <QStandardPaths>
#include <QDir>
#include <QDate>
#include <QDebug>
#include <QCoreApplication>


std::shared_ptr<spdlog::logger> 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<spdlog::sinks::stdout_color_sink_mt>();
    auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(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<spdlog::logger>("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("<font color='%1'>%2 %3</font>").arg(log_color, log_type, msg);
    m_logColor.clear();

    Log::output.append(log+"<br>");
    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]]

头文件

#ifndef DEBUGDIALOG_H
#define DEBUGDIALOG_H

#include <QDialog>

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文件

#include "debugdialog.h"
#include "ui_debugdialog.h"
#include "logger.h"

#include <QAction>
#include <QApplication>
#include <QIcon>
#include <QScrollBar>
#include <QMenu>
#include <QDebug>
#include <QInputDialog>
#include <QShortcut>

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主函数调用和测试

#include <QDir>
#include <QStandardPaths>
#include "logger.h"
#include "debugdialog.h"
#include <QDebug>

#include <QApplication>

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]]