使用spdlog接管QT的打印,封装日志宏

一、源码

logoutput.h

#ifndef LOGOUTPUT_H
#define LOGOUTPUT_H

#include <QObject>
#include <spdlog/spdlog.h>

// 全局 spdlog logger
extern std::shared_ptr<spdlog::logger> qspdLogger;

// 用法1:接管qt的打印信息
class logOutput : public QObject
{
    Q_OBJECT
public:
    static logOutput *Instance();
    explicit logOutput(QObject *parent = 0);
    ~logOutput();

public:
    void start();                           // 安装日志钩子
    void stop();                            // 卸载日志钩子
private:
    static QScopedPointer<logOutput> m_self;
    bool m_isRun;

    QString m_filename; // 文件名
    int  m_maxSize;    // 文件最大大小 单位kb
};

//用法2:封装宏,方便直接使用spdlog
#define LOG_TRACE(format,...) qspdLogger->trace(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);
#define LOG_DEBUG(format,...) qspdLogger->debug(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);
#define LOG_INFO(format,...)  qspdLogger->info(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);
#define LOG_WARN(format,...)  qspdLogger->warn(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);
#define LOG_ERROR(format,...) qspdLogger->error(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);
#define LOG_FATAL(format,...) qspdLogger->critical(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__);

#endif // LOGOUTPUT_H

logoutput.cpp

#include "logoutput.h"
#include <QMutex>
#include <spdlog/sinks/rotating_file_sink.h>

// 全局 spdlog logger
std::shared_ptr<spdlog::logger> qspdLogger;

// 自定义消息处理函数
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(context)
    // 将 QString 转换为 UTF-8 编码的 std::string
    QByteArray byteArray = msg.toUtf8();
    std::string utf8Msg(byteArray.constData(), byteArray.size());

    switch (type) {
    case QtDebugMsg:
        qspdLogger->debug(utf8Msg);
        break;
    case QtInfoMsg:
        qspdLogger->info(utf8Msg);
        break;
    case QtWarningMsg:
        qspdLogger->warn(utf8Msg);
        break;
    case QtCriticalMsg:
        qspdLogger->critical(utf8Msg);
        break;
    case QtFatalMsg:
        qspdLogger->critical(utf8Msg);
        abort();
    }
}

QScopedPointer<logOutput> logOutput::m_self;
logOutput *logOutput::Instance()
{
    if (m_self.isNull()) {
        static QMutex mutex;
        QMutexLocker locker(&mutex);
        if (m_self.isNull()) {
            m_self.reset(new logOutput);
        }
    }

    return m_self.data();
}

logOutput::logOutput(QObject *parent) : QObject(parent)
{
    m_isRun = false;
}

logOutput::~logOutput()
{
}

//安装钩子
void logOutput::start()
{
    if (m_isRun)
        return;

    m_isRun = true;

    // 初始化 spdlog,日志文件:/log/server.log,大小:10MB,保留数量:20
    qspdLogger = spdlog::rotating_logger_mt("file_logger", "log/log.txt", 10*1024*1024, 20);
    spdlog::set_level(spdlog::level::debug);
    spdlog::flush_on(spdlog::level::debug);
    spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e [%l] %v");
    qInstallMessageHandler(outputMessage);
}

//卸载钩子
void logOutput::stop()
{
    if (!m_isRun)
        return;

    m_isRun = false;
    qInstallMessageHandler(0);
}

二、使用方法

1.在main中,或者自己的初始化函数中增加如下语句:

logOutput::Instance()->start();

2.有2种打印方式

方式1:使用QT自带的打印语句,如qDebug、qInfo等

QString a = "test";
int b;
qDebug() << "test" << a << b;

方式2:使用LOG宏定义语句

std::string a;
int b;
LOG_ERROR("a:{},b:{}", a, b);

3.如果spdlog用了我的扩展版(扩展spdlog1.15.0,适配QString和msvc2015-优快云博客),宏定义也可支持QString的输出

std::string a;
int b;
QString qstr;
LOG_ERROR("a:{},b:{},QString:{}", a, b, qstr);

<think>我们参考了用户提供的三个引用,这些引用都与Qt日志处理相关。用户的需求是“实现实时打印日志的方法”。根据引用内容,核心是通过`qInstallMessageHandler`安装自定义消息处理器,将日志输出到文件或窗口等目标,并实现实时显示。 结合引用[2],我们看到一个实现方式:利用定义来格式化日志输出,包括时间、文件名、函数名、行号等信息。引用[3]则提到了一个函数`OutputMessage`,这可能是自定义消息处理器的实现函数。 因此,实现实时打印日志的关键步骤是: 1. 编写一个自定义的消息处理函数,该函数将接收到的日志信息实时输出(例如输出到控制台、文件或自定义的日志窗口)。 2. 使用`qInstallMessageHandler`安装这个自定义处理函数,这样Qt日志输出(如qDebug, qInfo等)就会重定向到这个函数。 3. 在自定义处理函数中,我们可以将日志信息实时显示在界面上(比如通过信号槽机制发送到UI线程的文本控件),同时也可以写入文件(按日期、大小分割,过期删除等)。 下面我们给出一个示例,展示如何自定义消息处理器并将日志实时显示在应用程序的UI界面上(假设我们有一个QPlainTextEdit控件用于显示日志): </think>### Qt实现实时打印日志的方法 在Qt中实现实时打印日志的核心是通过**自定义消息处理器**重定向日志输出,结合**信号槽机制**实现实时显示。以下是具体实现步骤: #### 1. **创建自定义消息处理器** ```cpp #include <QFile> #include <QTextStream> #include <QMutex> // 全局互斥锁防止多线程冲突 QMutex logMutex; void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QMutexLocker locker(&logMutex); // 线程安全锁定 // 实时输出到控制台 QByteArray localMsg = msg.toLocal8Bit(); fprintf(stderr, "%s\n", localMsg.constData()); // 实时打印到stderr // 写入文件(可选) QFile logFile("app_log.txt"); if (logFile.open(QIODevice::WriteOnly | QIODevice::Append)) { QTextStream ts(&logFile); ts << QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss") << " [" << context.function << "] " << msg << "\n"; } } ``` #### 2. **安装消息处理器(在main函数中)** ```cpp int main(int argc, char *argv[]) { QApplication a(argc, argv); // 安装自定义处理器 qInstallMessageHandler(messageHandler); // [^1] // 示例日志输出 qDebug() << "调试信息: 应用程序启动"; qInfo() << "系统状态: 初始化完成"; return a.exec(); } ``` #### 3. **实现UI实时显示(关键步骤)** ```cpp // 自定义Logger类(头文件) class Logger : public QObject { Q_OBJECT public: static Logger* instance(); void log(const QString &msg); // 日志接收接口 signals: void newLog(QString msg); // 日志更新信号 private: explicit Logger(QObject *parent = nullptr); }; // 在messageHandler中触发日志信号 void messageHandler(...) { ... emit Logger::instance()->newLog(msg); // 发送实时信号 [^3] } // UI界面连接信号(如QPlainTextEdit) QObject::connect(Logger::instance(), &Logger::newLog, ui->logTextEdit, &QPlainTextEdit::appendPlainText); // [^2] ``` #### 4. **增强日志格式(可选)** ```cpp // 使用定义增强日志信息 [^2] #define LOG_REALTIME(msg) \ qDebug().noquote() << QString("[%1][%2:%3] %4") \ .arg(QDateTime::currentDateTime().toString("HH:mm:ss.zzz")) \ .arg(__FILE__).arg(__LINE__).arg(msg) ``` ### 技术要点解析 1. **实时性保障** - 通过`qInstallMessageHandler`接管Qt全局日志系统 - 使用`信号槽机制`跨线程更新UI(自动排队执行) - 直接输出到`stderr`保证控制台实时性 2. **线程安全设计** - `QMutex`防止多线程日志冲突 - `QMutexLocker`自动锁管理(RAII模式) 3. **扩展功能建议** - 添加日志分级过滤(DEBUG/INFO/WARN等级别) - 实现日志文件轮转(按日期/大小分割) - 增加网络传输支持(引用[2]的远程调试) > 通过此方案,所有`qDebug()`、`qInfo()`等输出将自动实时显示在UI控件中,同时保存到文件。实际应用中需注意:**信号槽连接必须使用Qt::AutoConnection**(默认),确保跨线程安全[^1][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

billcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值