qt中使用日志系统,自定义日志彩色输出,qt日志写入文件,自定义qt日志格式,同时提供Qt日志重定向功能(将qDebug信息输出到界面控件)

一、介绍

在qt中要使用qt自带的日志系统我们都知道实现回调函数QtMessageHandler,再用qInstallMessageHandler函数进行注册:

// 注册日志处理
qInstallMessageHandler();

同时在pro文件中加入:

DEFINES += QT_MESSAGELOGCONTEXT # 日志

题外话:一般在商业项目中,我们可能不会使用qt的日志系统,而是会采用第三方的日志系统,比如常用语java后端的log4j,在qt中有Log4Qt,这篇文章很详细:https://blog.csdn.net/robert_cysy/article/details/105446185

二、实现

基于此我们就可以写出我们自定义的日志系统了,我写的这个日志系统具有以下特点:

1、支持设置日志输出路径

2、支持日志打印格式设置

3、支持不同日志级别的不同样式设置

4、支持3种日志备份策略,采用延迟策略,不是用定时器实现周期备份,而是在具体打印内容时才触发,这么做的目的是减少定时的开销

5、支持分日志类型备份日志

6、支持备份策略参数的json文件地址存储自定义

7、支持回调函数注册,便于一些想将日志输出到自己项目界面展示的小伙伴

下面是我的实现,我们只需要引入相应的h和cpp文件即可:

log.h

#ifndef LOG_H
#define LOG_H

#include <QApplication>
#include <QReadWriteLock>
#include <QFile>

/**
 * @brief The Color class 颜色设置
 */
class ConsoleFont {
public:
    const static int DEFAULT; // 重置颜色设置
    const static int BOLD; // 加粗
    const static int UN_BOLD; // 去粗
    const static int UNDER_LINE; // 下滑线
    const static int GLINT; // 闪烁
    const static int INVERSE;// 反色
    const static int BOLD_2; // 加粗
    const static int NORMAL ; // 正常
    const static int UN_UNDER_LINE; // 去掉下滑线
    const static int STOP_GLINT; // 停止闪烁
    const static int INVERSE_2;// 反色

    const static int FORE_GROUND_BLACK;// 前景,黑色
    const static int FORE_GROUND_RED;// 前景,红色
    const static int FORE_GROUND_GREEN;// 前景,绿色
    const static int FORE_GROUND_ORANGE;// 前景,黄色
    const static int FORE_GROUND_BLUE; //前景,篮色
    const static int FORE_GROUND_PURPLE; //前景,紫色
    const static int FORE_GROUND_CYAN; //前景,青色
    const static int FORE_GROUND_WHITE; //前景,白色

    const static int BACK_GROUND_BLACK;// 背景,黑色
    const static int BACK_GROUND_RED;// 背景,红色
    const static int BACK_GROUND_GREEN;// 背景,绿色
    const static int BACK_GROUND_ORANGE;// 背景,黄色
    const static int BACK_GROUND_BLUE; //背景,篮色
    const static int BACK_GROUND_PURPLE; //背景,紫色
    const static int BACK_GROUND_CYAN; //背景,青色
    const static int BACK_GROUND_WHITE; //背景,白色

    /**
     * @brief setConsoleFont 设置控制台字体样式
     * @param codes 字体样式code集合,用{}设置数组的方式进行传参
     * @param msg 信息
     * @return 设置样式后的内容
     */
    const static QString setConsoleFont(std::initializer_list<int> codes, QString msg);
};

/**
 * @brief The LogType enum 日志输出类型
 */
enum LogOutType {
    STANDARD_OUTPUT, // 标准输出流
    STANDARD_ERROR_OUTPUT // 标准错误输出流
};

/**
 * @brief The LogSwitchoverType enum 日志切换方式(即备份方式)
 */
enum LogSwitchoverType{
    ALL_DAY, // 指定天数备份,即相隔天数的凌晨00:00:00(对应单位是天d)
    TIME_PERIOD, // 指定时间间隔备份,即从项目启动开始后的间隔时间周期(对应单位是秒s)
    FILE_SIZE // 指定文件大小备份(对应单位是kb)
};


/**
 * 回调函数,日志类型、日志上下文,日志信息、格式化之后的信息,传入对象上下文
 */
typedef void (*LogInfoCallBackHandler)(QtMsgType, const QMessageLogContext &, const QString &,const QString &,void *);

/**
 * @brief The CallBackInfo class 回调信息
 */
class CallBackInfo{
public:
    CallBackInfo() {}
    CallBackInfo(QString unique, LogInfoCallBackHandler hander, void* context) {
        this->unique = unique;
        this->hander = hander;
        this->context = context;
    }
    QString unique; // 唯一标识
    LogInfoCallBackHandler hander; // 回调函数
    void* context; // 对象上下文
};

/**
 * 回调信息线程安全集合
 */
class CallBackInfoSafeList{
public:
    CallBackInfoSafeList() {}
    void add(CallBackInfo info){
        QWriteLocker lock(readWriteLock);
        this->mList.append(info);
    }
    QList<CallBackInfo> getList(){
        QReadLocker lock(readWriteLock);
        return mList;
    }
    CallBackInfo get(int index){
        QReadLocker lock(readWriteLock);
        return mList.value(index);
    }
private:
    QList<CallBackInfo> mList; // 回调信息集合
    mutable QReadWriteLock *readWriteLock = new QReadWriteLock(); // 读写锁
};

/**
 * @brief The Log class 日志
 */
class Log
{
public:
    Log() {}
    const static QString INFO_FORMAT_TIME; // 格式化输出日期表示
    const static QString INFO_FORMAT_LEVEL; // 格式化输出日志级别表示
    const static QString INFO_FORMAT_CODE_FILE; // 格式化输出代码文件地址表示
    const static QString INFO_FORMAT_CODE_LINE; // 格式化输出代码所在行表示
    const static QString INFO_FORMAT_CODE_FUNCTION; // 格式化输出代码所在方法函数表示
    const static QString INFO_FORMAT_CODE_MSG; // 格式化输出代码打印内容表示

    /**
     * @brief setFormat 设置日志输出内容格式化
     * @param infoFormat 内容格式化参数
     * @param timeFormat 日期格式化
     */
    static void setFormat(QString infoFormat, QString timeFormat = "yyyy-MM-dd hh:mm:ss:zzz");
    /**
     * @brief setLevelColor 设置不同日志级别的字体格式
     * @param type 日志类型
     * @param levelColor 字体格式
     */
    static void setLevelFont(QtMsgType type, std::initializer_list<int> levelFont);
    /**
     * @brief setOutLogPath 设置输出地址
     * @param outLogPath 日志输出地址
     */
    static void setOutLogPath(QString outLogPath);
    /**
     * @brief setOutLogParamPath 设置日志备份参数json存储地址
     * @param logParamPath 日志备份参数json存储地址
     */
    static void setOutLogParamJsonPath(QString logParamJsonPath);
    /**
     * @brief setLogSwitchover 设置日志切换备份方式
     * @param logSwitchoverType 日志备份类型
     * @param switchoverNum 备份时间或大小(ALL_DAY则是天数;TIME_PERIOD是时间间隔,单位秒;FILE_SIZE是文件大小,单位时kb)
     */
    static void setLogSwitchover(LogSwitchoverType logSwitchoverType, long long switchoverNum);
    /**
     * @brief setDistinguishLevel 设置备份输出是否区分日志级别单独输出
     * @param isDistinguishLevel 是否区分
     */
    static void setDistinguishLevel(bool isDistinguishLevel);
    /**
     * @brief messageOutput 日志输出注册函数
     * @param type 日志信息类型
     * @param context 日志信息上下文
     * @param msg 输出内容信息
     */
    static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
    /**
     * @brief registLogInfoCallBack 注册回调函数,注册函数使用链式调用,只要注册的函数都会进行调用
     * @param unique 唯一标识
     * @param hander 回调函数
     * @param contex 上下文,可以传入自己想操作的对象
     * @return
     */
    static LogInfoCallBackHandler registLogInfoCallBack(QString unique, LogInfoCallBackHandler hander,void* context = nullptr);
private:
    /**
     * @brief msgTypeMap 日志类型与标识、输出类型映射
     */
    static QMap<QtMsgType,QPair<QString, LogOutType>> msgTypeMap;
    /**
     * @brief levelFontMap 针对不同日志级别的字体显示设置映射
     */
    static QMap<QtMsgType,std::initializer_list<int>> levelFontMap;
    /**
     * @brief logSwitchoverTypeMap 备份类型与字符的映射
     */
    static QMap<LogSwitchoverType, QString> logSwitchoverTypeMap;
    /**
     * @brief outLogFile 输出file
     */
    static QFile* outLogFile;
    /**
     * @brief levelPaths 不同级别对应的输出路径
     */
    static QMap<QtMsgType, QFile*> levelPaths;
    /**
     * @brief isDistinguishLevel 是否区分日志级别输出
     */
    static bool isDistinguishLevel;
    /**
     * @brief infoFormat 日志输出内容格式化参数
     */
    static QString infoFormat;
    /**
     * @brief timeFormat 日期格式
     */
    static QString timeFormat;
    /**
     * @brief logParamJsonFile 日志备份策略参数保存json文件
     */
    static QFile* logParamJsonFile;
    /**
     * @brief logSwitchoverType 指定备份类型
     */
    static LogSwitchoverType logSwitchoverType;
    /**
     * @brief switchoverNum 指定备份时间或大小
     */
    static long long switchoverNum;
    /**
     * @brief switchoverTime 备份切换时间
     */
    static QDateTime switchoverTime;
    /**
     * @brief isLoadLogSwitchoverSetting 是否加载备份日志设置
     */
    static bool isLoadLogSwitchoverSetting;
    /**
     * @brief pCallback 存放回调函数的集合
     */
    static CallBackInfoSafeList pCallbacks;


    /**
     * @brief initMsgTypeMap 初始化日志类型与标识、输出类型映射
     * @return 日志类型与标识、输出类型映射
     */
    static QMap<QtMsgType,QPair<QString, LogOutType>> initMsgTypeMap();
    /**
     * @brief initLevelColors 初始化不同日志级别的字体显示设置映射
     */
    static QMap<QtMsgType,std::initializer_list<int>> initLevelFonts();
    /**
     * @brief initLogSwitchoverTypeMap 初始化备份类型与字符的映射
     */
    static QMap<LogSwitchoverType, QString> initLogSwitchoverTypeMap();
    /**
     * @brief writeLog 输出日志内容到文件
     * @param str 日志内容
     * @param QtMsgType 日志类型
     */
    static void writeLog(QString str, QtMsgType msgType);
    /**
     * @brief loadLogSwitchoverSetting 加载日志备份设置
     */
    static void loadLogSwitchoverSetting();
    /**
     * @brief logSwitchoverSettingHander 日志备份设置处理
     */
    static void logSwitchoverSettingHander();
    /**
     * @brief writeLogParamJson 向json文件中写入备份参数
     */
    static void writeLogParamJson();
    /**
     * @brief copyLogOutFile 备份日志文件
     */
    static void copyLogOutFile(QtMsgType msgType);
    /**
     * @brief copy 拷贝文件,目标文件存在则覆盖
     * @param srcFilepPath 源文件
     * @param targetFilePath 目标文件
     * @param 拷贝成功与否
     */
    static bool copy(QString srcFilepPath, QString targetFilePath);
    /**
     * @brief replace 替换字符串
     * @param src 原字符串
     * @param before 被替换的字符串
     * @param after 替换字符串
     * @return 被替换之后的字符串
     */
    static QString replace(QString src, QString before, QString after);
    /**
     * @brief isContainInfoFormat 判断是否包含格式化信息
     * @return 包含与否
     */
    static bool isContainInfoFormat(QString mInfoFormat = "");
    /**
     * @brief printSystemLog 打印日志系统自身的提示信息
     * @param level 级别(普通info,警告warn,错误error)
     * @param msg 信息
     * @return
     */
    static void printSystemLog(QString level, QString msg);
};


#endif // LOG_H

log.cpp

#include "log.h"
#include <iostream>
#include <QDebug>
#include <QtMessageHandler>
#include <QFile>
#include <QDir>
#include <QDateTime>
#include <qjsondocument.h>
#include <qjsonobject.h>

using namespace std;

const int ConsoleFont::DEFAULT = 0; // 重置颜色设置
const int ConsoleFont::BOLD = 1; // 加粗
const int ConsoleFont::UN_BOLD = 2; // 去粗
const int ConsoleFont::UNDER_LINE = 4; // 下滑线
const int ConsoleFont::GLINT = 5; // 闪烁
const int ConsoleFont::INVERSE = 7;// 反色
const int ConsoleFont::BOLD_2 = 21; // 加粗
const int ConsoleFont::NORMAL = 22; // 正常
const int ConsoleFont::UN_UNDER_LINE = 24; // 去掉下滑线
const int ConsoleFont::STOP_GLINT = 25; // 停止闪烁
const int ConsoleFont::INVERSE_2 = 27;// 反色

const int ConsoleFont::FORE_GROUND_BLACK = 30;// 前景,黑色
const int ConsoleFont::FORE_GROUND_RED = 31;// 前景,红色
const int ConsoleFont::FORE_GROUND_GREEN = 32;// 前景,绿色
const int ConsoleFont::FORE_GROUND_ORANGE = 33;// 前景,黄色
const int ConsoleFont::FORE_GROUND_BLUE = 34; //前景,篮色
const int ConsoleFont::FORE_GROUND_PURPLE = 35; //前景,紫色
const int ConsoleFont::FORE_GROUND_CYAN = 36; //前景,青色
const int ConsoleFont::FORE_GROUND_WHITE = 37; //前景,白色

const int ConsoleFont::BACK_GROUND_BLACK = 40;// 背景,黑色
const int ConsoleFont::BACK_GROUND_RED = 41;// 背景,红色
const int ConsoleFont::BACK_GROUND_GREEN = 42;// 背景,绿色
const int ConsoleFont::BACK_GROUND_ORANGE = 43;// 背景,黄色
const int ConsoleFont::BACK_GROUND_BLUE = 44; //背景,篮色
const int ConsoleFont::BACK_GROUND_PURPLE = 45; //背景,紫色
const int ConsoleFont::BACK_GROUND_CYAN = 46; //背景,青色
const int ConsoleFont::BACK_GROUND_WHITE = 47; //背景,白色

// 设置控制台字体样式
const QString ConsoleFont::setConsoleFont(std::initializer_list<int> codes, QString msg)
{
    if(codes.size() == 0) {
        return msg;
    }
    QString codeStr = "";
    int i = 0;
    for (auto code: codes)
    {
        if(i == 0) {
            codeStr = QString::number(code);
        } else {
            codeStr = codeStr + ";" + QString::number(code);
        }
        i++;
    }
    return "\033[" + codeStr + "m" + msg + "\033[0m";
}

const QString Log::INFO_FORMAT_TIME = "%time"; // 格式化输出日期表示
const QString Log::INFO_FORMAT_LEVEL = "%level"; // 格式化输出日志级别表示
const QString Log::INFO_FORMAT_CODE_FILE = "%file"; // 格式化输出代码文件地址表示
const QString Log::INFO_FORMAT_CODE_LINE = "%line"; // 格式化输出代码所在行表示
const QString Log::INFO_FORMAT_CODE_FUNCTION = "%function"; // 格式化输出代码所在方法函数表示
const QString Log::INFO_FORMAT_CODE_MSG = "%msg"; // 格式化输出代码打印内容表示


QMap<QtMsgType,QPair<QString, LogOutType>> Log::msgTypeMap = Log::initMsgTypeMap(); // 日志类型与日志标识、输出类型映射
QMap<QtMsgType,std::initializer_list<int>> Log::levelFontMap = Log::initLevelFonts(); // 日志类型与字体样式设置映射
QMap<LogSwitchoverType, QString> Log::logSwitchoverTypeMap = Log::initLogSwitchoverTypeMap(); // 初始化备份类型与字符的映射

QFile* Log::outLogFile = new QFile("logs/system.log");// 日志输出文件
QMap<QtMsgType, QFile*> Log::levelPaths = QMap<QtMsgType, QFile*>();// 不同级别的日志输出路径
bool Log::isDistinguishLevel = false; // 是否区分日志级别输出

QString Log::infoFormat = QString("%1 [%2] --- [%3:%4] %5: %6")
        .arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_FILE,
             Log::INFO_FORMAT_CODE_LINE, Log::INFO_FORMAT_CODE_FUNCTION, Log::INFO_FORMAT_CODE_MSG); // 日志信息格式化
QString Log::timeFormat = "yyyy-MM-dd hh:mm:ss:zzz"; // 时间格式化

QFile* Log::logParamJsonFile = new QFile("logdata/logparam.json"); // 日志参数保存json文件
LogSwitchoverType Log::logSwitchoverType = LogSwitchoverType::ALL_DAY; // 指定备份类型
long long Log::switchoverNum = 1; // 指定备份时间或大小
QDateTime Log::switchoverTime = QDateTime(); // 日志备份切换时间
bool Log::isLoadLogSwitchoverSetting = false; // 是否已经加载日志备份设置

CallBackInfoSafeList Log::pCallbacks = CallBackInfoSafeList(); // 回调函数集合

// 设置日志输出内容格式化
void Log::setFormat(QString infoFormat, QString timeFormat)
{
    if(infoFormat.trimmed().isEmpty()) {
        Log::printSystemLog("error", "设置日志格式化infoFormat为空,不进行设置");
        return;
    }
    if(!isContainInfoFormat(infoFormat)) {
        Log::printSystemLog("error", "设置日志格式化infoFormat不包含关键词:"+Log::INFO_FORMAT_CODE_MSG+",不进行设置");
        return;
    }
    if(timeFormat.trimmed().isEmpty()) {
        Log::printSystemLog("error", "设置日志格式化timeFormat为空,不进行设置");
        return;
    }
    Log::infoFormat = infoFormat;
    Log::timeFormat = timeFormat;
    Log::printSystemLog("info", "设置日志输出内容格式化:"+infoFormat+",日期格式:"+ timeFormat);
}

// 设置日志类型对应的字体样式
void Log::setLevelFont(QtMsgType type, std::initializer_list<int> levelFont)
{
    Log::levelFontMap.insert(type, levelFont);
    Log::printSystemLog("info", "设置日志级别"+msgTypeMap.value(type, QPair<QString, LogOutType>()).first+"样式");
}

// 设置输出路径
void Log::setOutLogPath(QString outLogPath)
{
    if(outLogPath.trimmed().isEmpty()) {
        Log::printSystemLog("error", "设置日志输出路径为空,不进行设置");
        return;
    }
    Log::outLogFile = new QFile(outLogPath);
    Log::printSystemLog("info", "设置日志输出路径:"+ outLogPath);
    // 如果要区分日志级别输出,则重新调用设置是否分日志级别输出
    if(Log::isDistinguishLevel) {
        Log::setDistinguishLevel(Log::isDistinguishLevel);
    }
}

// 设置日志备份参数json存储地址
void Log::setOutLogParamJsonPath(QString logParamJsonPath)
{
    if(logParamJsonPath.trimmed().isEmpty()) {
        Log::printSystemLog("error","设置日志备份参数json存储地址为空,不进行设置");
        return;
    }
    Log::logParamJsonFile = new QFile(logParamJsonPath);
    Log::printSystemLog("info","设置日志备份参数json存储地址:"+ logParamJsonPath);
}

// 设置日志切换类型和时间或大小
void Log::setLogSwitchover(LogSwitchoverType logSwitchoverType, long long switchoverNum)
{
    if(switchoverNum < 1) {
        Log::printSystemLog("error", "设置时间或大小切换限定:"+QString::number(switchoverNum)+",小于1,不进行设置");
        return;
    }
    Log::logSwitchoverType = logSwitchoverType;
    Log::switchoverNum = switchoverNum;
    Log::printSystemLog("info","设置日志切换类型:"+ Log::logSwitchoverTypeMap.value(logSwitchoverType,"")+",时间或大小切换限定:"+QString::number(switchoverNum));
}

// 是否分日志级别输出
void Log::setDistinguishLevel(bool isDistinguishLevel)
{
    // 关闭已经打开的流,和删除已经设置的文件
    for(QtMsgType type : Log::levelPaths.keys()) {
        QFile* file = Log::levelPaths.value(type);
        if(file->isOpen()) {
            file->close();
        }
        if(file->exists()) {
            file->remove();
        }
    }
    // 清空映射关系
    Log::levelPaths.clear();
    // 设置是否分日志级别输出
    Log::isDistinguishLevel = isDistinguishLevel;
    // 如果分日志级别输出,则创建对应的文件和打开文件
    if(isDistinguishLevel) {
        // 关闭默认输出路径
        if(outLogFile->isOpen()) {
            outLogFile->close();
        }
        // 根据设置的输出文件路径构造不同级别的文件
        QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
        QString fileName = fileInfo.fileName();
        QDir dir = fileInfo.absoluteDir();
        if(!dir.exists()) {
            dir.mkdir(dir.absolutePath());
        }
        QMap<QtMsgType, QString> prefixMap = QMap<QtMsgType, QString>();
        prefixMap.insert(QtDebugMsg, "debug_");
        //一般信息文件路径
        prefixMap.insert(QtInfoMsg, "info_");
        //一般的警告文件路径
        prefixMap.insert(QtWarningMsg, "warn_");
        //严重错误文件路径
        prefixMap.insert(QtCriticalMsg, "critical_");
        //致命错误文件路径
        prefixMap.insert(QtFatalMsg, "fail_");
        for(QtMsgType type : prefixMap.keys()) {
            //调试信息文件路径
            QString filePath = dir.absoluteFilePath(prefixMap.value(type) + fileName);
            QFile* file = new QFile(filePath);
            Log::levelPaths.insert(type, file);
        }
        Log::printSystemLog("info", "已设置分日志级别输出,构建分日志文件成功");
    }
}

// 日志注册函数
void Log::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    // 加载日志和写入相关联配置
    Log::loadLogSwitchoverSetting();

    // 下面开始解析信息
    QString txtMessage = "";
    QString nowTime = QDateTime::currentDateTime().toString(Log::timeFormat.trimmed().isEmpty() ? "yyyy-MM-dd hh:mm:ss:zzz" : Log::timeFormat);
    if(Log::msgTypeMap.contains(type)) {
        QPair<QString, LogOutType> infoPair = Log::msgTypeMap.value(type, Log::msgTypeMap.value(QtInfoMsg));
        // 格式化信息
        txtMessage = !Log::isContainInfoFormat() ? QString("%1 [%2] --- [%3:%4] %5: %6")
                                                   .arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_FILE,
                                                        Log::INFO_FORMAT_CODE_LINE, Log::INFO_FORMAT_CODE_FUNCTION, Log::INFO_FORMAT_CODE_MSG)
                                                 : infoFormat;
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_TIME, nowTime);
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_LEVEL, infoPair.first);
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_FILE, context.file);
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_LINE, QString::number(context.line));
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_FUNCTION, context.function);
        txtMessage = Log::replace(txtMessage, Log::INFO_FORMAT_CODE_MSG, msg);

        // 根据不同日志级别设置字体样式
        QString txtFontMessage = ConsoleFont::setConsoleFont(Log::levelFontMap.value(type, {ConsoleFont::DEFAULT}), txtMessage);
        // 根据输出日志类型选择输出到标准流或者是标准错误流
        if(infoPair.second == LogOutType::STANDARD_OUTPUT) {
            std::cout << txtFontMessage.toLocal8Bit().constData() << std::endl;
        } else {
            std::cerr << txtFontMessage.toLocal8Bit().constData() << std::endl;
        }
        // 写入到文件中
        writeLog(txtMessage, type);
        // 回调函数
        for(CallBackInfo callBack : Log::pCallbacks.getList()) {
            LogInfoCallBackHandler hander = callBack.hander;
            hander(type, context, msg, txtMessage, callBack.context);
        }
        if(type == QtFatalMsg) {
            Log::printSystemLog("error", "致命错误,立即终止:"+ msg);
            // 致命错误立即终止
            abort();
        }
    } else {
        std::cout << msg.toLocal8Bit().constData() << std::endl;
    }
}

// 注册回调函数
LogInfoCallBackHandler Log::registLogInfoCallBack(QString unique, LogInfoCallBackHandler hander, void *context)
{
    // 如果已经存在已经注册过的回调函数,则不在进行注册
    for(CallBackInfo info : pCallbacks.getList()) {
        if(info.unique == unique) {
            Log::printSystemLog("warn", "已经注册过该日志回调函数:" + unique+",不在进行注册操作");
            return hander;
        }
    }
    CallBackInfo callBackInfo(unique, hander, context);
    Log::pCallbacks.add(callBackInfo);
    Log::printSystemLog("info", "注册日志回调函数:" + unique + "成功");
    return hander;
}

// 初始化不同日志级别的输出级别标识和输出流
QMap<QtMsgType,QPair<QString, LogOutType>> Log::initMsgTypeMap()
{
    QMap<QtMsgType,QPair<QString, LogOutType>> msgTypeMap;
    //调试信息提示
    msgTypeMap.insert(QtDebugMsg,QPair<QString, LogOutType>("Debug", LogOutType::STANDARD_OUTPUT));
    //一般信息提示
    msgTypeMap.insert(QtInfoMsg,QPair<QString, LogOutType>("Info", LogOutType::STANDARD_OUTPUT));
    //一般的警告提示
    msgTypeMap.insert(QtWarningMsg,QPair<QString, LogOutType>("Waring", LogOutType::STANDARD_OUTPUT));
    //严重错误提示
    msgTypeMap.insert(QtCriticalMsg,QPair<QString, LogOutType>("Critical", LogOutType::STANDARD_ERROR_OUTPUT));
    //致命错误提示
    msgTypeMap.insert(QtFatalMsg,QPair<QString, LogOutType>("Fatal", LogOutType::STANDARD_ERROR_OUTPUT));
    return msgTypeMap;
}

// 初始化不同日志级别的字体样式参数
QMap<QtMsgType, std::initializer_list<int>> Log::initLevelFonts()
{
    QMap<QtMsgType, std::initializer_list<int>> levelColorMap;
    //调试信息字体样式
    levelColorMap.insert(QtDebugMsg,{ConsoleFont::FORE_GROUND_GREEN});
    //一般信息字体样式
    levelColorMap.insert(QtInfoMsg,{ConsoleFont::DEFAULT});
    //一般的警告信息字体样式
    levelColorMap.insert(QtWarningMsg,{ConsoleFont::FORE_GROUND_ORANGE});
    //严重错误字体样式
    levelColorMap.insert(QtCriticalMsg,{ConsoleFont::FORE_GROUND_RED, ConsoleFont::UNDER_LINE});
    //致命错误字体样式
    levelColorMap.insert(QtFatalMsg,{ConsoleFont::FORE_GROUND_RED, ConsoleFont::BOLD});
    return levelColorMap;
}

// 初始化备份类型与字符的映射
QMap<LogSwitchoverType, QString> Log::initLogSwitchoverTypeMap()
{
    QMap<LogSwitchoverType, QString> logSwitchoverTypeMap = QMap<LogSwitchoverType, QString>();
    logSwitchoverTypeMap.insert(LogSwitchoverType::ALL_DAY, "all_day");
    logSwitchoverTypeMap.insert(LogSwitchoverType::TIME_PERIOD, "time_period");
    logSwitchoverTypeMap.insert(LogSwitchoverType::FILE_SIZE, "file_size");
    return logSwitchoverTypeMap;
}

// 写入文件
void Log::writeLog(QString str, QtMsgType msgType)
{
    // 判断是否进行备份切换
    if(Log::logSwitchoverType == LogSwitchoverType::ALL_DAY || Log::logSwitchoverType == LogSwitchoverType::TIME_PERIOD) {
        // 如果当前时间超过了切换时间,则进行切换
        if(QDateTime::currentDateTime().operator >(Log::switchoverTime)) {
            // 备份日志文件
            Log::copyLogOutFile(msgType);
        }
    } else {
        long long fileSize = 0;
        if(Log::isDistinguishLevel) {
            // 计算所有日志文件大小之和
            for(QtMsgType type: Log::levelPaths.keys()) {
                QFile* msgFile = Log::levelPaths.value(type);
                fileSize = fileSize + msgFile->size();
            }
        } else {
            fileSize = Log::outLogFile->size();
        }
        // 当文件大小达到配置限制,则备份文件,因为size返回的是byte,而switchoverNum的单位是kb,所以需要乘以1024
        if(fileSize > (Log::switchoverNum*1024)) {
            // 备份日志文件
            Log::copyLogOutFile(msgType);
        }
    }
    // 写入日志到相应文件
    if(Log::isDistinguishLevel) {
        QFile* file = Log::levelPaths.value(msgType);
        if(!file->isOpen()) {
            file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
        }
        file->write((str+"\n").toUtf8());
        file->flush();
    } else {
        if(!Log::outLogFile->isOpen()) {
            Log::outLogFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
        }
        Log::outLogFile->write((str+"\n").toUtf8());
        Log::outLogFile->flush();
    }
}

// 备份日志文件
void Log::copyLogOutFile(QtMsgType msgType)
{
    QString nowTimeStr = QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz");
    QList<bool> copyStatus;
    QList<QtMsgType> msgTypes;
    if(Log::isDistinguishLevel) {
        // 创建备份文件夹
        QFile* file = Log::levelPaths.value(msgType);
        QFileInfo fileInfo = QFileInfo(file->fileName());
        QDir dir = fileInfo.absoluteDir();
        QString copyDirPath = dir.absoluteFilePath(nowTimeStr+"log");
        if(!dir.exists(copyDirPath)) {
            dir.mkdir(copyDirPath);
        }
        QDir copyDir(copyDirPath);
        // 将日志文件备份到备份文件夹下
        for(QtMsgType type: Log::levelPaths.keys()) {
            QFile* msgFile = Log::levelPaths.value(type);
            QFileInfo fileInfo = QFileInfo(msgFile->fileName());
            bool isSucceed = Log::copy(fileInfo.absoluteFilePath(), copyDir.absoluteFilePath(fileInfo.fileName()));
            copyStatus.append(isSucceed);
            if(isSucceed) {
                msgTypes.append(type);
            }
            Log::printSystemLog("info", "拷贝文件:"+fileInfo.absoluteFilePath() + "到"+ copyDir.absoluteFilePath(fileInfo.fileName())+(isSucceed ? "成功":"失败"));
        }
    } else {
        // 创建备份文件夹
        QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
        QDir dir = fileInfo.absoluteDir();
        QString copyDirPath = dir.absoluteFilePath(nowTimeStr+"log");
        if(!dir.exists(copyDirPath)) {
            dir.mkdir(copyDirPath);
        }
        QDir copyDir(copyDirPath);
        // 将日志文件备份到备份文件夹下
        bool isSucceed = Log::copy(fileInfo.absoluteFilePath(), copyDir.absoluteFilePath(fileInfo.fileName()));
        copyStatus.append(isSucceed);
        Log::printSystemLog("info", "拷贝文件:"+fileInfo.absoluteFilePath() + "到"+ copyDir.absoluteFilePath(fileInfo.fileName())+(isSucceed ? "成功":"失败"));
    }
    // 备份成功与否提示信息
    if(!copyStatus.contains(false)) {
        Log::printSystemLog("info", "备份日志文件成功");
    } else {
        Log::printSystemLog("error", "备份部分日志文件失败!");
    }
    // 如果备份全部成功,则清空日志文件和写入新的切换时间到日志参数配置文件
    if(Log::isDistinguishLevel) {
        for(QtMsgType type: msgTypes) {
            QFile* msgFile = Log::levelPaths.value(type);
            QFileInfo fileInfo = QFileInfo(msgFile->fileName());
            if(msgFile->resize(0)) {
                Log::printSystemLog("info", "清空日志文件内容成功:"+fileInfo.absoluteFilePath());
            } else {
                Log::printSystemLog("error", "清空日志文件内容失败:"+fileInfo.absoluteFilePath());
            }
        }
    } else {
        if(!copyStatus.contains(false)) {
            QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
            if(Log::outLogFile->resize(0)) {
                Log::printSystemLog("info", "清空日志文件内容成功:"+fileInfo.absoluteFilePath());
            } else {
                Log::printSystemLog("error", "清空日志文件内容失败:"+fileInfo.absoluteFilePath());
            }
        }
    }
    // 备份完成之后更新再次需要备份的时间,并写入备份参数配置文件中
    Log::writeLogParamJson();
}

// 加载备份策略的设置
void Log::loadLogSwitchoverSetting()
{
    if(Log::isLoadLogSwitchoverSetting) {
        return;
    }
    logSwitchoverSettingHander();
    Log::isLoadLogSwitchoverSetting = true;

    // 如果需要分日志输出,则先创建分日志文件
    if(Log::isDistinguishLevel) {
        for(QtMsgType type: Log::levelPaths.keys()) {
            QFile* file = Log::levelPaths.value(type);
            // 如果文件所在文件夹不存在则创建文件夹
            QFileInfo fileInfo = QFileInfo(file->fileName());
            QDir dir = fileInfo.absoluteDir();
            if(!dir.exists()) {
                dir.mkdir(dir.absolutePath());
            }
            file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
        }
    } else {
        // 如果文件所在文件夹不存在则创建文件夹
        QFileInfo fileInfo = QFileInfo(Log::outLogFile->fileName());
        QDir dir = fileInfo.absoluteDir();
        if(!dir.exists()) {
            dir.mkdir(dir.absolutePath());
        }
        Log::outLogFile->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
    }
}

// 备份参数处理,并同步更新json文件中的数据到内存,或将设置参数写入参数文件
void Log::logSwitchoverSettingHander()
{
    if(!Log::logParamJsonFile->exists()) { // 文件不存在则创建日志备份参数json文件并将参数写入文件
        Log::writeLogParamJson();
    } else {
        if(Log::logParamJsonFile->open(QIODevice::ReadWrite | QIODevice::Text)) { // 打开文件成功则读取json文件中的配置到内存中
            QByteArray json = Log::logParamJsonFile->readAll().trimmed();
            Log::logParamJsonFile->close();
            QJsonParseError jsonError;
            QJsonDocument jsonDoc(QJsonDocument::fromJson(json, &jsonError));
            if(jsonError.error == QJsonParseError::NoError)
            {
                QJsonObject rootObj = jsonDoc.object();
                QString logSwitchoverTypeStr = rootObj.value("logSwitchoverType").toString("");
                QString switchoverNumberStr =  rootObj.value("switchoverNum").toString("");
                long long switchoverNumber = switchoverNumberStr.trimmed().isEmpty() ? 1: switchoverNumberStr.toLongLong();
                QString switchoverTimeStr =  rootObj.value("switchoverTime").toString("");
                LogSwitchoverType switchoverType;
                QDateTime switchoverTime;
                for(LogSwitchoverType type: Log::logSwitchoverTypeMap.keys()) {
                    if(Log::logSwitchoverTypeMap.value(type) == logSwitchoverTypeStr) {
                        switchoverType = type;
                        if(type == LogSwitchoverType::ALL_DAY) {
                            switchoverTime = QDateTime::fromString(switchoverTimeStr, "yyyy-MM-dd");
                        } else if(type == LogSwitchoverType::TIME_PERIOD) {
                            switchoverTime = QDateTime::fromString(switchoverTimeStr, "yyyy-MM-dd hh:mm:ss");
                        }
                        break;
                    }
                }
                Log::printSystemLog("info", "读取转换备份配置Json文件成功:"+logParamJsonFile->fileName()+",内容是:"+QString(json));
                // 如果不和默认的一样,则拷贝一份,将默认的参数写入文件,这么做的目的是用户在之前设置过日志切换类型和时间或大小,后面又修改代码后又没有进行相应的设置,则需要用到默认设置
                if(switchoverType != Log::logSwitchoverType || switchoverNumber != Log::switchoverNum) {
                    Log::printSystemLog("warn", "读取配置和设置的设置过日志切换类型和时间或大小不同,写入最新配置:"+logParamJsonFile->fileName());
                    Log::writeLogParamJson();
                } else {
                    Log::logSwitchoverType = switchoverType;
                    Log::switchoverNum = switchoverNumber;
                    Log::switchoverTime = switchoverTime;
                }
            } else { // 解析失败,则将内存中日志备份参数写入文件
                Log::printSystemLog("error", "转换备份配置Json文件失败:"+logParamJsonFile->fileName()+",已转为默认配置");
                Log::writeLogParamJson();
            }
        } else { // 打开失败,则将内存中日志备份参数写入文件
            Log::printSystemLog("error", "打开备份配置Json文件失败:"+logParamJsonFile->fileName()+",已转为默认配置");
            Log::writeLogParamJson();
        }
    }
}

// 写入备份参数到日志备份配置json文件中
void Log::writeLogParamJson()
{
    QDateTime nowTime = QDateTime::currentDateTime();
    QString switchoverTimeStr = "";
    if(Log::logSwitchoverType == LogSwitchoverType::ALL_DAY) {
        // 指定天数之后
        Log::switchoverTime = nowTime.addDays(Log::switchoverNum);
        switchoverTimeStr = switchoverTime.toString("yyyy-MM-dd");
    } else if(Log::logSwitchoverType == LogSwitchoverType::TIME_PERIOD) {
        // 指定秒之后
        Log::switchoverTime = nowTime.addSecs(Log::switchoverNum);
        switchoverTimeStr = nowTime.toString("yyyy-MM-dd hh:mm:ss");
    }
    QJsonObject jsonObject;//构建json对象json
    jsonObject.insert("logSwitchoverType", Log::logSwitchoverTypeMap.value(Log::logSwitchoverType, ""));
    jsonObject.insert("switchoverNum", QString::number(Log::switchoverNum));
    jsonObject.insert("switchoverTime", switchoverTimeStr);
    QJsonDocument document;
    document.setObject(jsonObject);
    QString jsonStr(document.toJson(QJsonDocument::Indented));
    // 如果文件所在文件夹不存在则创建文件夹
    QFileInfo fileInfo = QFileInfo(Log::logParamJsonFile->fileName());
    QDir dir = fileInfo.absoluteDir();
    if(!dir.exists()) {
        dir.mkdir(dir.absolutePath());
    }
    if(Log::logParamJsonFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
        Log::logParamJsonFile->write(jsonStr.toUtf8());
        Log::logParamJsonFile->flush();
        Log::logParamJsonFile->close();
        printSystemLog("info", "写入备份日志参数到"+fileInfo.absoluteFilePath()+",成功;参数:"+jsonStr);
    } else {
        printSystemLog("error", "写入备份日志参数到"+fileInfo.absoluteFilePath()+",失败");
    }
}

// 拷贝文件
bool Log::copy(QString srcFilepPath, QString targetFilePath)
{
    QFile srcFile(srcFilepPath);
    QFile targetFile(targetFilePath);
    if(srcFile.exists()) {
        if(targetFile.exists()) {
            targetFile.remove();
        }
        return srcFile.copy(targetFilePath);
    }
    Log::printSystemLog("error", "需要备份文件不存在:" + srcFilepPath);
    return false;
}

// 替换字符串中的子字符串
QString Log::replace(QString src, QString before, QString after)
{
    if(src.isEmpty()) {
        return src;
    }
    if(!src.contains(before)) {
        return src;
    }
    QString repalceAfter = src;
    while (repalceAfter.contains(before)) {
        repalceAfter = repalceAfter.replace(src.indexOf(before), before.size(), after);
    }
    return repalceAfter;
}

// 是否包含必要的格式化信息
bool Log::isContainInfoFormat(QString mInfoFormat)
{
    QString infoFormat = Log::infoFormat;
    if(!mInfoFormat.trimmed().isEmpty()) {
        infoFormat = mInfoFormat;
    }
    if(infoFormat.trimmed().isEmpty()) {
        return false;
    }
    if(!infoFormat.contains(Log::INFO_FORMAT_CODE_MSG)) {
        return false;
    }
    return true;
}

// 日志系统信息样式及打印
void Log::printSystemLog(QString level, QString msg)
{
    if(level.trimmed().toLower() == "info") {
        QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_BLUE,ConsoleFont::BACK_GROUND_CYAN,ConsoleFont::BOLD}, msg);
        std::cout << info.toLocal8Bit().constData() << std::endl;
    } else if(level.trimmed().toLower() == "warn") {
        QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_ORANGE,ConsoleFont::BACK_GROUND_PURPLE,ConsoleFont::BOLD}, msg);
        std::cout << info.toLocal8Bit().constData() << std::endl;
    } else if(level.trimmed().toLower() == "error") {
        QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_RED,ConsoleFont::BACK_GROUND_GREEN,ConsoleFont::BOLD}, msg);
        std::cout << info.toLocal8Bit().constData() << std::endl;
    } else {
        QString info = ConsoleFont::setConsoleFont({ConsoleFont::FORE_GROUND_BLACK,ConsoleFont::BACK_GROUND_CYAN,ConsoleFont::BOLD}, msg);
        std::cout << info.toLocal8Bit().constData() << std::endl;
    }
}

三、使用

1、在pro文件中添加

DEFINES += QT_MESSAGELOGCONTEXT # 日志

2、在项目中引入上面的h和cpp文件

3、在main.cpp这样使用

#include "datamanagement/mainwindows/mainwindow.h"
#include <QApplication>
#include "log/log.h"
#include <QtMessageHandler>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    /*这一串日志参数设置可以不用设置,不设置会采用默认设置*/
    Log::setFormat(QString("[%1] [%2] : %3").arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_MSG),"yyyy/MM/dd hh:mm:ss");
    Log::setLevelFont(QtDebugMsg, {ConsoleFont::FORE_GROUND_BLUE});
    Log::setOutLogPath("log_test/mlog.log");
    Log::setLogSwitchover(LogSwitchoverType::FILE_SIZE, 5);
    Log::setOutLogParamJsonPath("logjson/logsetting.json");
    Log::setDistinguishLevel(true);
    /*----------------------------------------*/

    // 注册日志处理
    qInstallMessageHandler(Log::messageOutput);


    qWarning("测试一下qWarning");
    qInfo("测试一下qInfo");
    qCritical("测试一下qCritical");
    MainWindow* w = new MainWindow();
    w->show();

    return a.exec();
}

4、日志设置方法介绍

这些设置方法可以都不进行设置,这样会采用默认的方式

 /**
     * @brief setFormat 设置日志输出内容格式化
     * @param infoFormat 内容格式化参数
     * @param timeFormat 日期格式化
     */
    static void setFormat(QString infoFormat, QString timeFormat = "yyyy-MM-dd hh:mm:ss:zzz");
    /**
     * @brief setLevelColor 设置不同日志级别的字体格式
     * @param type 日志类型
     * @param levelColor 字体格式
     */
    static void setLevelFont(QtMsgType type, std::initializer_list<int> levelFont);
    /**
     * @brief setOutLogPath 设置输出地址
     * @param outLogPath 日志输出地址
     */
    static void setOutLogPath(QString outLogPath);
    /**
     * @brief setOutLogParamPath 设置日志备份参数json存储地址
     * @param logParamPath 日志备份参数json存储地址
     */
    static void setOutLogParamJsonPath(QString logParamJsonPath);
    /**
     * @brief setLogSwitchover 设置日志切换备份方式
     * @param logSwitchoverType 日志备份类型
     * @param switchoverNum 备份时间或大小(ALL_DAY则是天数;TIME_PERIOD是时间间隔,单位秒;FILE_SIZE是文件大小,单位时kb)
     */
    static void setLogSwitchover(LogSwitchoverType logSwitchoverType, long long switchoverNum);
    /**
     * @brief setDistinguishLevel 设置备份输出是否区分日志级别单独输出
     * @param isDistinguishLevel 是否区分
     */
    static void setDistinguishLevel(bool isDistinguishLevel);
    /**
     * @brief messageOutput 日志输出注册函数
     * @param type 日志信息类型
     * @param context 日志信息上下文
     * @param msg 输出内容信息
     */
    static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
    /**
     * @brief registLogInfoCallBack 注册回调函数,注册函数使用链式调用,只要注册的函数都会进行调用
     * @param unique 唯一标识
     * @param hander 回调函数
     * @param contex 上下文,可以传入自己想操作的对象
     * @return
     */
    static LogInfoCallBackHandler registLogInfoCallBack(QString unique, LogInfoCallBackHandler hander,void* context = nullptr);

这几个提供的公共方法介绍:

1)setFormat

这个方法是设置日志格式的方法,具体就是一个是日志格式参数,一个是时间格式参数

static void setFormat(QString infoFormat, QString timeFormat = "yyyy-MM-dd hh:mm:ss:zzz");

日志格式infoFormat中必须包含以下中关键词的一个和多个,其中INFO_FORMAT_CODE_MSG必须包含,最终这些关键词会被替换成相应的具体信息打印出来,时间格式timeFormat 可以不设置,不设置就会采用默认值

const static QString INFO_FORMAT_TIME; // 格式化输出日期表示
const static QString INFO_FORMAT_LEVEL; // 格式化输出日志级别表示
const static QString INFO_FORMAT_CODE_FILE; // 格式化输出代码文件地址表示
const static QString INFO_FORMAT_CODE_LINE; // 格式化输出代码所在行表示
const static QString INFO_FORMAT_CODE_FUNCTION; // 格式化输出代码所在方法函数表示
const static QString INFO_FORMAT_CODE_MSG; // 格式化输出代码打印内容表示

这样我们就可以这样设置,可以看到我们自己设置了相应格式,然后把我们想展示的位置用关键占位构成格式字符串传入:

Log::setFormat(QString("[%1] [%2] : %3").arg(Log::INFO_FORMAT_TIME, Log::INFO_FORMAT_LEVEL, Log::INFO_FORMAT_CODE_MSG),"yyyy/MM/dd hh:mm:ss");

效果:

 可以看到,这个就是我们当前例子设置的格式样子

2)setLevelFont

 这个方法是针对不同日志级别设置对应的字体样式

static void setLevelFont(QtMsgType type, std::initializer_list<int> levelFont);

 type指的就是日志的级别,levelFont指的是对应的样式,用{ }来进行设置

type主要是这5个:

QtDebugMsg, // 调试信息
QtWarningMsg, //一般警告
QtCriticalMsg, //严重错误
QtFatalMsg,  //致命错误
QtInfoMsg //一般提示

levelFont从这里面选取即可:

class ConsoleFont {
public:
    const static int DEFAULT; // 重置颜色设置
    const static int BOLD; // 加粗
    const static int UN_BOLD; // 去粗
    const static int UNDER_LINE; // 下滑线
    const static int GLINT; // 闪烁
    const static int INVERSE;// 反色
    const static int BOLD_2; // 加粗
    const static int NORMAL ; // 正常
    const static int UN_UNDER_LINE; // 去掉下滑线
    const static int STOP_GLINT; // 停止闪烁
    const static int INVERSE_2;// 反色

    const static int FORE_GROUND_BLACK;// 前景,黑色
    const static int FORE_GROUND_RED;// 前景,红色
    const static int FORE_GROUND_GREEN;// 前景,绿色
    const static int FORE_GROUND_ORANGE;// 前景,黄色
    const static int FORE_GROUND_BLUE; //前景,篮色
    const static int FORE_GROUND_PURPLE; //前景,紫色
    const static int FORE_GROUND_CYAN; //前景,青色
    const static int FORE_GROUND_WHITE; //前景,白色

    const static int BACK_GROUND_BLACK;// 背景,黑色
    const static int BACK_GROUND_RED;// 背景,红色
    const static int BACK_GROUND_GREEN;// 背景,绿色
    const static int BACK_GROUND_ORANGE;// 背景,黄色
    const static int BACK_GROUND_BLUE; //背景,篮色
    const static int BACK_GROUND_PURPLE; //背景,紫色
    const static int BACK_GROUND_CYAN; //背景,青色
    const static int BACK_GROUND_WHITE; //背景,白色
};

这样我们就可以这样进行设置:

Log::setLevelFont(QtDebugMsg, {ConsoleFont::FORE_GROUND_BLUE});
Log::setLevelFont(QtInfoMsg, {ConsoleFont::FORE_GROUND_GREEN});
Log::setLevelFont(QtWarningMsg, {ConsoleFont::FORE_GROUND_ORANGE, ConsoleFont::BOLD});
Log::setLevelFont(QtCriticalMsg, {ConsoleFont::FORE_GROUND_RED});
Log::setLevelFont(QtFatalMsg, {ConsoleFont::FORE_GROUND_RED});

调用(注意调用qFatal程序会被立即终止掉):

qDebug("测试一下qWarning");
qWarning("测试一下qWarning");
qInfo("测试一下qInfo");
qCritical("测试一下qCritical");
qFatal("致命错误,立即结束");

效果:

3)setOutLogPath

这个就是设置日志输出路径,如果不设置会采用默认值logs/system.log

static void setOutLogPath(QString outLogPath);

比如我设置

Log::setOutLogPath("log_test/mlog.log");

那么就会在构建项目路径下生成相应文件:

 4)setDistinguishLevel

这个是设定日志是否分日志级别输出,这个是和生成日志文件路径相配合的,会将指定的路径名分别指定为不同级别

static void setDistinguishLevel(bool isDistinguishLevel);

比如我设置:

Log::setDistinguishLevel(true);

就会在相应目录下生成五个不同级别的文件,不同日志级别的内容会写入不同文件:

 5)setLogSwitchover

这个是用来设置备份策略的,logSwitchoverType指定的是采用什么类型,switchoverNum指定的是对应策略的值大小

static void setLogSwitchover(LogSwitchoverType logSwitchoverType, long long switchoverNum);

logSwitchoverType有3个(注,表示时间的两个策略并不是定时的备份,而是在打印时会去判断这个时间是够超过了指定的时间就会进行备份,也就是说这是基于日志打印时进行的判断,只有日志打印时才会触发,也就是延迟方案):

enum LogSwitchoverType{
    ALL_DAY, // 指定天数备份,即相隔天数的凌晨00:00:00(对应单位是天d)
    TIME_PERIOD, // 指定时间间隔备份,即从项目启动开始后的间隔时间周期(对应单位是秒s)
    FILE_SIZE // 指定文件大小备份(对应单位是kb)
};

switchoverNum在不同类型下对应的意思是:

(ALL_DAY则是天数;TIME_PERIOD是时间间隔,单位秒;FILE_SIZE是文件大小,单位时kb)

比如我设置:

Log::setLogSwitchover(LogSwitchoverType::FILE_SIZE, 5);

表示的是日志文件超过5kb就备份,在日志输出目录下创建备份文件夹,文件会进行备份,同时日志输出文件会重新清空,效果如下:

6)setOutLogParamJsonPath

这个指的是设置日志备份参数json存储地址,不设置会采用默认值logdata/logparam.json,也就是备份参数的存储地址:

static void setOutLogParamJsonPath(QString logParamJsonPath);

比如我设置:

Log::setOutLogParamJsonPath("logjson/logsetting.json");

就会在相应的目录下生成相应的存储文件:

内容如下(不要手动的去修改,如果要删除的话,一般是你的备份策略或者值在代码层面进行了修改,这个可以删除,一般不建议删除,如果删除的话,程序在启动时就会进行重新的计算,比如切换备份时间,这样可能备份时如果采用了根据时间进行备份方式的可能备份周期又得重新开始,不符合你的预期,因为程序每次启动会去读取这个文件,如果不存在,会重新计算并生成文件):

7)registLogInfoCallBack

这个是日志系统回调函数注册,unique指定注册函数的唯一性,如果已经注册过同名unique,则再次调用这个函数,不会在进行注册,unique保证注册函数的唯一性和一次性,避免反复注册,hander就是我们要自己实现的回调函数,比如,我们要实现在界面显示日志信息等情况,可以用这个来实现,context 表示的上下文对象,因为我们要注册实现的函数必须是静态的,所以我们有时候想要操作一些对象,通过这个,就可以把我们想操作的对象传入,不传入默认是空指针,(同时注意:实现回调函数中不要出现qDebug、qWarning、qInfo、qCritical、qFatal,因为如果出现就会反复调用日志系统打印,日志系统又会调用回调函数,陷入死循环,造成内存泄漏)

static LogInfoCallBackHandler registLogInfoCallBack(QString unique, LogInfoCallBackHandler hander,void* context = nullptr);

LogInfoCallBackHandler 回调函数的定义:

/**
 * 回调函数,日志类型、日志上下文,日志信息、格式化之后的信息,传入对象上下文
 */
typedef void (*LogInfoCallBackHandler)(QtMsgType, const QMessageLogContext &, const QString &,const QString &,void *);

比如,我可以这样(注意,如果实现函数在类中,必须是静态函数,也可以是全局函数,一般建议是类静态函数):

在mainwindow.h中定义静态方法:

static void test(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg,const QString &formatMsg,void* context);

在mainwindow.cpp中实现(注意,实现回调函数中不要出现qDebug、qWarning、qInfo、qCritical、qFatal):

void MainWindow::test(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg,const QString &formatMsg,void* context)
{
    MainWindow* mainWindow = (MainWindow*) context;
    QString text = "瓜皮:"+formatMsg;
    printf((text+"\n").toLocal8Bit().constData());
}

在mainwindow.cpp构造中注册:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{

    ui->setupUi(this);

    Log::registLogInfoCallBack("main",MainWindow::test,this);
}

效果:

 8)messageOutput

这个是日志注册函数,用于向qt日志系统进行注册的回调函数

static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);

使用:

在main.cpp中进行注册:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


    // 注册日志处理
    qInstallMessageHandler(Log::messageOutput);
}

  • 17
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Qt ,可以使用 QDebug 和 qInstallMessageHandler 函数将日志写入文件。具体步骤如下: 1. 创建一个 QFile 对象,用于写入日志文件。例如: ```cpp QFile file("log.txt"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; ``` 2. 定义一个自定义的消息处理函数,用于将日志信息写入文件。例如: ```cpp void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); switch (type) { case QtDebugMsg: fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtInfoMsg: fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); abort(); } QTextStream out(&file); out << QString("[%1] %2\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg); out.flush(); } ``` 该函数将日志信息按照指定的格式写入文件。 3. 在 main 函数调用 qInstallMessageHandler 函数,将自定义的消息处理函数设置为全局的消息处理函数。例如: ```cpp int main(int argc, char *argv[]) { QApplication a(argc, argv); QFile file("log.txt"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return -1; qInstallMessageHandler(myMessageOutput); // ... return a.exec(); } ``` 这样,所有的日志信息都会被重定向到 log.txt 文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值