一、介绍

在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,所以需要乘以1024if(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对象jsonjsonObject.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);
}

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

  1. 2.在某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每一个类提供一个日志记录代理类,在代理类中输出日志,例如在业务方法 method() 调用之前输出“方法 method() 被

    2.在某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每一个类提供一个日志记录代理类,在代理类中输出日志,例如在业务方法 method() 调用之前输出"方法 metho ...

  2. QT中QDatetime中文系统中获取英文系统的结果

    QT中QDatetime中文系统中获取英文系统的结果 如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 我们在中文系统下开发QT程序,如 ...

  3. Android 常见界面控件(ListView、RecyclerView、自定义View篇)

    Android 常见界面控件(ListView.RecyclerView.自定义View篇) 目录 3.3 ListView的使用 3.3.1 ListView控件的简单使用 3.3.2 常用数据适配 ...

  4. 解决使用高分辨率笔记本分辨率放大100%以上运行程序界面控件不跟随方大方式qt+gtk+ui

    一.前言 解决使用高分辨率笔记本分辨率放大100%以上运行程序界面控件不跟随方大方式.使用100%时,qt界面太小,瞅着累眼睛,方大了界面,程序编译出来控件都跟着方大,布局都乱了,但是牺牲了qt cr ...

  5. Qt中如何获取系统图标

    Qt中获取系统图标.类型是通过QFileIconProvider来实现的,具体如下: 一.Qt获取系统文件图标 1.获取文件夹图标 QFileIconProvider icon_provider; Q ...

  6. QT中Widget去除系统提供工具以及系统默认边框

    1.直接去掉系统提供的窗口边框,不能移动和改变窗口的大小. this->setWindowFlags(Qt::FramelessWindowHint);//直接去掉系统提供的窗口边框,不能移动和 ...

  7. Qt编写视频监控系统70-OSD标签和图形信息(支持写入到文件)

    一.前言 作为一个完整的视频监控系统,用户还需要自定义一些OSD标签信息显示在对应通道上面,而且不止一个OSD标签信息,位置可以在四个角或者指定坐标显示.最开始本系统设计的时候,由于本人擅长的是pai ...

  8. qt工程在linux系统里颜色显示错误_【飞凌嵌入式RK3399开发板试用体验】+QT开发环境搭建测试(二)...

    作者:飞扬的青春 在拿到开发板之后,已经体验了Android操作系统,接下来就是体验Linux下的开发,本次以QT的一个小案例来测试下. 首先是自己先搭建了一个Ubuntu18.04的虚拟机,使用真机 ...

  9. 日志查看器:显示日志记录信息的快速WPF控件

    对于长时间运行的后台任务,用户了解当前执行的步骤很有帮助.LogViewer可以安全地多线程收集此信息,并将其显示为可滚动文本.LogViewer允许后台线程在不使用任何WPF代码的情况下写入格式化文 ...

  10. c语言中管理员信息注册,regsvr32注册控件如果使用管理员身份执行

    在win7或以上系统中,由于安全控制,使用cmd命令行就不太方便,因为权限问题,比方说最简单的,输入"regsvr32 mscomctl.ocx"(注册树控件),就会提示" ...

最新文章

  1. 把有限的精力放在最有意义的事上
  2. 前端一HTML:十五: 权重的比较
  3. shell最大出现和连续出现次数_shell脚本分析 nginx日志访问次数最多及最耗时的页面(慢查询)...
  4. python国际象棋ai程序_用Python编写一个国际象棋AI程序
  5. 网页无法调用java9_JAVA 9 (内部类,异常,包)
  6. PP视频如何查看上下集的快捷键
  7. Spring Boot笔记-自定义配置项默认值设置
  8. 下载、安装Keil 5
  9. mysql mgr写入_社区投稿 | MySQL MGR一致性读写特性解读
  10. python 高阶函数之filter
  11. xshell7评估版安装使用
  12. SharePoint中CAML日期格式
  13. AUTOCAD——坐标标注
  14. 视频文件压缩成什么格式最小?
  15. 【软件构造】正则表达式合法性检测
  16. XP下grub4dos硬盘安装和启动FreeBSD-8.0-i386+GNOME桌面
  17. 如何在命令行卸载mysql_如何彻底卸载MySQL
  18. 《爱情公寓》电影,让我十年的情怀,一瞬间都喂了狗
  19. html中背景条纹效果,CSS制作Web页面条纹背景样式的技巧分享
  20. 专知原创和整理了一些中英文机器学习从入门到精通的资料,和大家分享一下

热门文章

  1. c# formApp的web browser的兼容性设置
  2. 基于PHP和YII框架技术的班级管理系统 | 饭饭博客
  3. php liger 表格排序,jQuery LigerUI 使用教程表格篇(1)
  4. Kotlin协成的简单理解
  5. 增长黑客手册——02
  6. 计算机卸载打不开,电脑卸载wps后excel打不开怎么办
  7. 初次使用Vscode,遇到了一个极具没有水平的问题,解决之后瞬间感到无比尴尬
  8. tar的--exclude选项中PATTERN详解
  9. ITA和意大利荣耀与共,助力蓝衣军团欧洲杯决赛战胜英格兰!
  10. mysql如何上传音频文件_如何上传本地音乐获取MP3外链(欢迎分享和转载)