Qt 日志重定向并缓存到文件

logUtils.h

#ifndef LOGUTILS_H
#define LOGUTILS_H

#include <QString>
#include <QDateTime>

class LogUtils final
{
public:
    explicit LogUtils() = delete;
    static void checkAppCacheLogDir(const QString &subDirName = "");
    static QString appCacheLogPath();
    static QString localDateTime();
    static QString localDate();
    static QString localDataTimeCSV();
    static uint lastTimeStamp(const QDateTime &dateTime, uint dayCount);
    static QDateTime lastDateTime(const QDateTime &dateTime, uint dayCount);
    static bool containLastDay(const QDateTime &src, const QDateTime &dst, uint dayCount);
    static QDateTime toDayZero();
};

#endif // LOGUTILS_H

logUtils.cpp

#include "logutils.h"

#include <QDateTime>
#include <QMutex>
#include <QDebug>
#include <QStandardPaths>
#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>

namespace GlobalPrivate
{
    const QString cachePath = QStandardPaths::locate(QStandardPaths::CacheLocation,
                                                     "",
                                                     QStandardPaths::LocateDirectory);
    const QString deepinCachePath = cachePath + "deepin" + QDir::separator();
}

/**
 * @brief checkAppCacheLogDir
 *  检查应用程序缓存日志的文件夹
 * @param subDirName 日志目录下的子目录
 *  默认为空,则检查顶层目录
 */
void LogUtils::checkAppCacheLogDir(const QString &subDirName)
{
    if (!QFileInfo::exists(GlobalPrivate::deepinCachePath))
        QDir().mkdir(appCacheLogPath());

    if (!QFileInfo::exists(appCacheLogPath()))
        QDir().mkdir(appCacheLogPath());

    if (subDirName.isEmpty()) return;

    if (!QFileInfo::exists(appCacheLogPath() + QDir::separator() + subDirName))
        QDir().mkdir(appCacheLogPath() + QDir::separator() + subDirName);
}

/**
 * @brief appCacheLogPath
 *  获取当前应用程序的缓存日志路径,
 * @return QString 返回的结果总是一个Dir类型的字符路径
 */
QString LogUtils::appCacheLogPath()
{
    return GlobalPrivate::deepinCachePath + QCoreApplication::applicationName();
}

/**
 * @brief localDateTime 获取年/月/日/时间
 * @return QString 格式化字符串后的时间
 */
QString LogUtils::localDateTime()
{
    return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
}

/**
 * @brief localDate 获取年/月/日
 * @return QString 格式化字符串后的时间
 */
QString LogUtils::localDate()
{
    return QDate::currentDate().toString("yyyy-MM-dd");
}

/**
 * @brief localDate 获取年/月/日/时间,
 *  年/月/日/与时间之间以逗号分割
 * @return QString 格式化字符串后的时间
 */
QString LogUtils::localDataTimeCSV()
{
    return QDateTime::currentDateTime().toString("yyyy-MM-dd,hh:mm:ss,zzz");
}

/**
 * @brief lastTimeStamp 获取输入时间之前指定天数时间戳,
 * 最小单位s
 * @param dateTime 时间
 * @param dayCount 向前的天数
 * @return uint 时间戳
 */
uint LogUtils::lastTimeStamp(const QDateTime &dateTime, uint dayCount)
{
    return dateTime.toTime_t() - (86400 * dayCount);
}

/**
 * @brief lastDateTime 获取输入时间之前指定天数时间,
 * 最小单位s
 * @param dateTime 时间
 * @param dayCount 向前的天数
 * @return QDateTime 时间
 */
QDateTime LogUtils::lastDateTime(const QDateTime &dateTime, uint dayCount)
{
    return QDateTime::fromTime_t(lastTimeStamp(dateTime,dayCount));
}

/**
 * @brief containLastDay 判断时间是否包含时间(天)范围
 * 最小单位s
 * @param src 基准时间
 * @param dst 对比时间
 * @param dayCount 往前推的天数
 * @return bool 是否包含的结果
 *
 * |------dst--------src----|
 * |------dayCount----|
 * return true;
 *
 * |-----------dst-------------src|
 *                  |-dayCount--|
 * return false
 */
bool LogUtils::containLastDay(const QDateTime &src, const QDateTime &dst, uint dayCount)
{
    uint srcStamp = src.toTime_t();
    uint dstStamp = dst.toTime_t();

    return dstStamp - (86400 * dayCount) < srcStamp && srcStamp <= dstStamp;
}

/**
 * @brief toDayZero 获取今天的00:00:00的时间
 * @return
 */
QDateTime LogUtils::toDayZero()
{
    QDateTime dateTime;
    dateTime.setDate(QDate::currentDate());
    dateTime.setTime(QTime(0,0,0));
    return dateTime;
}

frameworklog.h

#include <QDebug>
#include <QLoggingCategory>

/**
 * @brief Framework 可进行相关调用,如下
 * @code
 * qCDebug(Framework) << "hello";    //调试信息打印
 * qCInfo(Framework) << "hello";    //信息打印
 * qCDWarning(Framework) << "hello";     //警告打印
 * qCCritical(Framework) << "hello";     //关键日志打印
 * @endcode
 */
Q_DECLARE_LOGGING_CATEGORY(Framework)

/**
 * @brief FrameworkLog 宏函数,可进行相关打印调用,如下
 * @code
 * FDebug() << "hello";    //调试信息打印
 * FInfo() << "hello";    //信息打印
 * FWarning() << "hello";     //警告打印
 * FCritical() << "hello";     //关键日志打印
 * @endcode
 */
#define FDebug() qCDebug(Framework)
#define FInfo() qCInfo(Framework)
#define FWarning() qCDWarning(Framework)
#define FCritical() qCCritical(Framework)
#endif

/**
 * @brief The FrameworkLog class
 *  框架日志打印模块,内部封装输出重定向与日志格式化
 */
class FrameworkLog final
{
public:
    explicit FrameworkLog() = delete;
    static void enableFrameworkLog(bool enabled = true);
    static void setLogCacheDayCount(uint dayCount);
    static uint logCacheDayCount();
    static void initialize();
};

frameworklog.cpp

#include "frameworklog.h"
#include "logutils.h"

#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QCoreApplication>
#include <QtConcurrent>

Q_LOGGING_CATEGORY(Framework, "Framework")

namespace GlobalPrivate
{
    static QFile file;
    static uint dayCount = 7;
    static QMutex mutex;

    QString formatFrameworkLogOut(QtMsgType type, const QMessageLogContext &context, const QString &msg)
    {
        auto fileNameList = QString(context.file).split(QDir::separator());
        auto currentName = fileNameList[fileNameList.size() - 1];

        if (type == QtMsgType::QtDebugMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][Debug]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        if (type == QtMsgType::QtInfoMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][Info]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        if (type == QtMsgType::QtCriticalMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][Critical]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        if (type == QtMsgType::QtWarningMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][Warning]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        if (type == QtMsgType::QtSystemMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][System]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        if (type == QtMsgType::QtFatalMsg)
            return "[" + QString(context.category) + "]["
                    + LogUtils::localDateTime() + "][Fatal]["
                    + currentName + " "
                    + context.function + " "
                    + QString::number(context.line) + "]"
                    + msg;
        return msg;
    }

    static void rmExpiredLogs()
    {
        QtConcurrent::run([=](){
            QDirIterator itera(LogUtils::appCacheLogPath(),QDir::Files);
            while(itera.hasNext()) {
                itera.next();
                auto list = itera.fileName().split("_");
                if (itera.fileInfo().suffix() == "log"
                        && list.count() == 2
                        && !LogUtils::containLastDay(
                            QDateTime(QDate::fromString(list[0],"yyyy-MM-dd"),
                                      QTime(0,0,0,0)),
                            LogUtils::toDayZero(),
                            GlobalPrivate::dayCount))
                {

                    auto outMsg = formatFrameworkLogOut(QtMsgType::QtInfoMsg,
                                                        QMessageLogContext{
                                                            __FILE__,
                                                            __LINE__,
                                                            __FUNCTION__,
                                                            Framework().categoryName()
                                                        },
                                                        QString("remove true(%0) not last week log: %1")
                                                        .arg(QDir().remove(itera.path() + QDir::separator() + itera.fileName()))
                                                        .arg(itera.fileName().toLocal8Bit().data())
                                                        );

                    fprintf(stderr, "%s\n", outMsg.toUtf8().data());
                }
            }
        });
    }

    void redirectGlobalDebug(QtMsgType type,
                             const QMessageLogContext &context,
                             const QString &msg)
    {
        QMutexLocker locker(&mutex);
        QString logMsg = GlobalPrivate::formatFrameworkLogOut(type,context,msg);
        if (type == QtMsgType::QtDebugMsg)
            fprintf(stdin,"%s\n",logMsg.toUtf8().data());
        if (type == QtMsgType::QtInfoMsg)
            fprintf(stderr,"%s\n",logMsg.toUtf8().data());
        if (type == QtMsgType::QtSystemMsg)
            fprintf(stdin,"%s\n",logMsg.toUtf8().data());
        if (type == QtMsgType::QtCriticalMsg)
            fprintf(stderr,"%s\n",logMsg.toUtf8().data());
        if (type == QtMsgType::QtWarningMsg)
            fprintf(stderr,"%s\n",logMsg.toUtf8().data());
        if (type == QtMsgType::QtFatalMsg)
            fprintf(stderr,"%s\n",logMsg.toUtf8().data());

        // cache/deepin/qApp->applicationName()
        LogUtils::checkAppCacheLogDir();

        if (GlobalPrivate::file.fileName().isEmpty()) {
            GlobalPrivate::file.setFileName(LogUtils::appCacheLogPath()
                                            + QDir::separator()
                                            + LogUtils::localDate()
                                            + "_" + QCoreApplication::applicationName()
                                            + ".log");

            auto outMsg = GlobalPrivate::formatFrameworkLogOut(QtMsgType::QtInfoMsg,
                                                               QMessageLogContext{
                                                                   __FILE__,
                                                                   __LINE__,
                                                                   __FUNCTION__,
                                                                   Framework().categoryName()
                                                               },
                                                               "Current redirect log file path: "
                                                               + GlobalPrivate::file.fileName()
                                                               );

            fprintf(stderr, "%s\n", outMsg.toUtf8().data());

            //清除超出时间段的日志
            GlobalPrivate::rmExpiredLogs();
        }

        if (!GlobalPrivate::file.open(QFile::Append|QFile::ReadOnly)) {

            auto outMsg = GlobalPrivate::formatFrameworkLogOut(QtMsgType::QtInfoMsg,
                                                               QMessageLogContext{
                                                                   __FILE__,
                                                                   __LINE__,
                                                                   __FUNCTION__,
                                                                   Framework().categoryName()
                                                               },
                                                               "Failed, open redirect log file"
                                                               + GlobalPrivate::file.fileName()
                                                               + " "
                                                               + GlobalPrivate::file.errorString()
                                                               );

            fprintf(stderr, "%s\n", outMsg.toUtf8().data());

            return;
        }

        GlobalPrivate::file.write((logMsg + ("\n")).toLocal8Bit().data());
        GlobalPrivate::file.flush();
        GlobalPrivate::file.close();
    }

} // namespace GlobalPrivate

/**
 * @brief enableFrameworkLog 开启框架日志打印
 * @param enabled true为开启,false则关闭
 */
void FrameworkLog::enableFrameworkLog(bool enabled)
{
    if (enabled) {
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.warning=true"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.debug=true"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.info=true"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.critical=true"));

    } else {
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.warning=false"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.debug=false"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.info=false"));
        QLoggingCategory::setFilterRules(QLatin1String("FrameworkLog.critical=false"));
    }
}

/**
 * @brief setLogCacheDayCount 设置日志缓存时间,
 *  需要在调用其他函数之前调用
 * @param uint 缓存的天数
 */
void FrameworkLog::setLogCacheDayCount(uint dayCount)
{
    static QMutex mutex;
    mutex.lock();
    GlobalPrivate::dayCount = dayCount;
    mutex.unlock();
}

/**
 * @brief logCacheDayCount 获取设置的日志缓存时间
 * @return uint 缓存的天数,默认缓存7天
 */
uint FrameworkLog::logCacheDayCount()
{
    return GlobalPrivate::dayCount;
}

/**
 * @brief initialize 初始化框架日志打印模块
 */
void FrameworkLog::initialize()
{
    qInstallMessageHandler(&GlobalPrivate::redirectGlobalDebug);
}

调用FrameworkLog::initialize()即可重定向日志输出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值