挂钩QT的日志系统

QT有自己的日志打印函数族,通常我们使用qDebug()、qInfo()、qCritical()输出程序的工作日志,最终它们会被送到标准错误输出。对控制台应用程序来说,就是命令行环境窗口,对GUI桌面程序来说,如果不在开发模式,可能需要DebugView这个工具才能看到。

QT开放了全局函数qInstallMessageHandler(),使用它可以将日志重定向到你指定的回调。声明如下:

typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

回调函数接收三个参数:

参数1为日志级别,对应qDebug()、qInfo()的语义:

enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtInfoMsg, QtSystemMsg = QtCriticalMsg };

参数2为日志上下文,包含文件、行号、函数名等信息:

class QMessageLogContext
{
public:
    int version;
    int line;
    const char *file;
    const char *function;
    const char *category;
};

参数3为输出的日志字符串。

如果为qInstallMessageHandler()指定NULL参数,即表示取消挂钩。

用法讲完,接下来看一个具体的实现。在下面的实现中,挂钩了QT的日志输出,并且做了两方面的处理:
  1. 使用系统调用::OutputDebugStringA()重放日志,但是请记住不能再调用任何qXXX()函数。
  2. 过滤Debug级别日志,然后输出到文件。
头文件:
#ifndef CSYSTEMLOGGER_H
#define CSYSTEMLOGGER_H

// 系统日志记录 =》

#include 

class CSystemLogger
{
public:
    static CSystemLogger* getInstance();

    // 调试日志回调
    static void onQDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);

    explicit CSystemLogger(QObject *parent = nullptr);
    virtual ~CSystemLogger();

    // 添加日志行
    bool appendLog(const std::string& strLogLine);

private:
    // 获取文件名
    std::string __getLogFileName();

    // 格式化字符串
    std::string __toString(const char* sFormat, ...);
};

#endif // CSYSTEMLOGGER_H
实现文件:
#include "SystemLogger.h"

#include 
#include 
#include 
#include 
#include 
#include 

CSystemLogger *CSystemLogger::getInstance()
{
    static CSystemLogger* s_pInstance = NULL;

    if (s_pInstance == NULL)
    {
        s_pInstance = new CSystemLogger();
    }

    return s_pInstance;
}

// 调试日志回调
void CSystemLogger::onQDebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(context);

    std::string strMsgType = "unkown";
    switch (type)
    {
        case QtDebugMsg:
        {
            strMsgType = "debug";
            break;
        }
        case QtWarningMsg:
        {
            strMsgType = "warning";
            break;
        }
        case QtCriticalMsg:
        {
            strMsgType = "critical";
            break;
        }
        case QtFatalMsg:
        {
            strMsgType = "fatal";
            break;
        }
        case QtInfoMsg:
        {
            strMsgType = "info";
            break;
        }
    }

    std::string strLogLine = CFuncHelper::toString("%s [%s] %s\n", 
        QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd").toStdString().c_str(), strMsgType.c_str(), msg.toStdString().c_str());

    // 保留调试日志
    ::OutputDebugStringA(strLogLine.c_str());

    // Debug级别日志不输出到文件
    if (type == QtDebugMsg)
    {
        return;
    }

    // 追加日志行
    CSystemLogger::getInstance()->appendLog(strLogLine);
}

CSystemLogger::CSystemLogger(QObject *parent) : QObject(parent)
{
    // 注册日志回调
    qInstallMessageHandler(CSystemLogger::onQDebugMessageHandler);
}

CSystemLogger::~CSystemLogger()
{
    // 取消注册日志回调
    qInstallMessageHandler(NULL);
}

// 添加日志行
bool CSystemLogger::appendLog(const std::string &strLogLine)
{
    std::string strLogFile = __getLogFileName();

    std::ofstream ofs(strLogFile, std::ios::app);
    if (!ofs)
    {
        ::OutputDebugStringA( __toString("@CSystemLogger::appendLog open file:[%s] failed!\n", strLogFile.c_str()).c_str() );
        return false;
    }

    ofs << strLogLine;
    return true;
}

// 获取文件名
std::string CSystemLogger::__getLogFileName()
{
    QDir dir;
    if (!dir.exists("log"))
    {
        dir.mkdir("log");
    }

    return __toString("log\\%s.log", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd").toStdString().c_str());
}

// 格式化字符串
std::string CSystemLogger::__toString(const char* sFormat, ...)
{
    char sLine[1024] = { 0 };

    va_list va;
    va_start(va, sFormat);
    vsnprintf(sLine, sizeof(sLine)-1, sFormat, va);
    va_end(va);

    return std::string(sLine);
};
使用:

在main()函数开头增加:

// 挂钩系统日志
std::unique_ptr autoSysLog(CSystemLogger::getInstance());

你可能感兴趣的:(挂钩QT的日志系统)