引言
项目中需求一日志模块,主要实现两大功能:1.自动打印信息至日志文件;2.软件意外退出时保留信息以便跟踪问题。
本文结合了 Qt 自定义日志工具 和 让程序在崩溃时体面的退出之CallStack 提供的方法,补充实现了文章中未具体给出的管理日志文件大小和数量的功能。
环境:vs2012+Qt5.2(注:Qt5.5之后引入qInfo(),影响不大)
一、日志实现方法
基本原理是使用 qInstallMessageHandler()接管qDebug(), qWarning()等调试信息,然后将信息流存储至本地日志文件,管理日志文件。
代码在原作者基础上做了部分调整:
1.更改日志存储名称格式,用QDateTime取代QDate,以避免当日记录多条日志时的覆盖问题;
2.增加日志文件个数的判断;
3.增加日志文件大小的检测;
4.屏蔽根据修改日期保存日志机制,以免在不同日期开启软件后冲掉以前的有用log,仅凭文件大小另存log文件,然后控制文件数量。
代码实现
LogHandler.cpp
#include "LogHandler.h"
#include <stdio.h>
#include <stdlib.h>
#include <QDebug>
#include <QDateTime>
#include <QMutexLocker>
#include <QtGlobal>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTimer>
#include <QTextStream>
#include <iostream>
#define LOGLIMIT_NUM 5 //日志文件存档个数
#define LOGLIMIT_SIZE 500 //单个日志文件存档大小限制,单位KB
/************************************************************************************************************
* *
* LogHandlerPrivate *
* *
***********************************************************************************************************/
struct LogHandlerPrivate {
LogHandlerPrivate();
~LogHandlerPrivate();
// 打开日志文件 protocal.log,如果日志文件不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 protocal.log
void openAndBackupLogFile();
// 消息处理函数
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
// 如果日志所在目录不存在,则创建
void makeSureLogDirectory() const;
// 检测当前日志文件大小
void checkLogFiles();
QDir logDir; // 日志文件夹
QTimer renameLogFileTimer; // 重命名日志文件使用的定时器
QTimer flushLogFileTimer; // 刷新输出到日志文件的定时器
QDateTime logFileCreatedDate; // 日志文件创建的时间
static QFile *logFile; // 日志文件
static QTextStream *logOut; // 输出日志的 QTextStream,使用静态对象就是为了减少函数调用的开销
static QMutex logMutex; // 同步使用的 mutex
};
// 初始化 static 变量
QMutex LogHandlerPrivate::logMutex;
QFile* LogHandlerPrivate::logFile = NULL;
QTextStream* LogHandlerPrivate::logOut = NULL;
LogHandlerPrivate::LogHandlerPrivate() {
logDir.setPath("Log"); // TODO: 日志文件夹的路径,为 exe 所在目录下的 log 文件夹,可从配置文件读取
QString logPath = logDir.absoluteFilePath("protocal.log");