一个用于 Qt 项目的简单的日志库
在写 Qt 程序时,经常会用到 qDebug 输出一些调试信息。但是正式发布时这些信息就看不到了,这时就很需要有个日志系统,可以把程序输出的一些关键性的信息记录下来。
上网查找了一番,是有个开源项目叫 log4qt 的。不过这个项目许久都不更新了。看了看这个项目还挺复杂的,感觉用不到这么多功能。所以就自己山寨了个简单的日志库。
Qt 中有个 qInstallMessageHandler 函数,可以注册自己的处理函数,之后 qDebug、qInfo 一类的信息就可以按照自己的需要输出到文件或者其他地方了。
在 Qt 中,调试信息分为 5 级,分别如下:
QtDebugMsg -> qDebug()
QtInfoMsg -> qInfo()
QtWarningMsg -> qWarning()
QtCriticalMsg -> qCritical()
QtFatalMsg -> qFatal()
qt 帮助文件中给的例子如下:
#include <qapplication.h>
#include <stdio.h>
#include <stdlib.h>
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();
}
}
int main(int argc, char **argv)
{
qInstallMessageHandler(myMessageOutput);
QApplication app(argc, argv);
...
return app.exec();
}
qt 自带的这个例子很简单,但是也基本上说明清楚了如何用这个函数了。
我自己的实现上又做了些扩展,首先是日志应该可以输出到多个地方,比如同时输出到 console 和 文件中。
另外就是哪几个级别的日志是要输出的也应该可以控制。为此,设计了两个类:
- FileLogger 一个 FileLogger 代表一个具体的日志存储方式,比如输出到 console 或存储到一个特定的文件。当然也可以输出到其他的地方,只要写个类继承自 FileLogger ,重新实现个 writeLog 方法就行了。
- LoggerController 用来注册具体的 FileLogger,一个 LoggerController可以注册多个 FileLogger。
下面是代码:
#ifndef MESSAGELOGGER_H
#define MESSAGELOGGER_H
#include <QtCore/QString>
#include <QtCore/QFile>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QDebug>
#include <QtCore/QMessageLogContext>
class FileLogger
{
public:
enum LEVEL{E_DEBUG = 1, E_INFO = 2, E_WARNING = 4, E_CRITICAL = 8, E_FATAL = 16};
FileLogger(QString name, LEVEL level);
FileLogger(QString name = "stderr", bool debug = false, bool info = true, bool warning = true, bool critical= true, bool fatal = true);
virtual ~FileLogger();
/**
* @brief setFileName 设置日志存储的文件名
* @param name 日志存储的文件名,如果为 "stderr" 则输出到 stderr
* @return
*/
bool setFileName(QString name = "stderr");
/**
* @brief setLogLevel 设置哪些级别的信息要输出到文件
* @param level 可以为 E_DEBUG、E_INFO、E_WARNING、E_CRITICAL、E_FATAL 或者这些项的组合(bit or)
* 没有设置的 level 则不输出日志
*/
void setLogLevel(LEVEL level);
/**
* @brief setLogLevel 设置哪些级别的信息要输出到文件
* @param debug true 表示 qDebug 信息输出到日志
* @param info true 表示 qInfo 信息输出到日志
* @param warning true 表示 qWarning 信息输出到日志
* @param critical true 表示 qCritical 信息输出到日志
* @param fatal true 表示 qFatal 信息输出到日志
*/
void setLogLevel(bool debug = false, bool info = true, bool warning = true, bool critical= true, bool fatal = true);
virtual void writeLog(QtMsgType type, const QMessageLogContext &context, const QString &msg);
private:
QString messageType(QtMsgType type);
FileLogger(FileLogger &f) {Q_UNUSED(f);} // 不能被拷贝
FileLogger& operator=( FileLogger &f) {Q_UNUSED(f);} // 不能被拷贝
private:
QFile m_file;
QMap<QtMsgType, bool> m_level;
bool m_where;
};
class LoggerController
{
public:
LoggerController(){}
~LoggerController();
void startLogging();
void attach(FileLogger *m_currentLogger);
void detach(FileLogger *m_currentLogger);
private:
static void writeLog(QtMsgType type, const QMessageLogContext &context, const QString &msg);
static LoggerController * m_currentLogger;
QList<FileLogger *> m_list;
};
#endif // MESSAGELOGGER_H
#include "MessageLogger.h"
#include <QtCore/QDateTime>
#include <QtCore/QTextStream>
FileLogger::FileLogger(QString name, bool debug, bool info, bool warning, bool critical, bool fatal)
:m_where(true)
{
setFileName(name);
setLogLevel(debug, info, warning, critical, fatal);
}
FileLogger::~FileLogger()
{
m_file.close();
}
bool FileLogger::setFileName(QString name)
{
m_file.close();
if(name == "stderr")
{
return m_file.open(stderr, QIODevice::WriteOnly);
}
else
{
m_file.setFileName(name);
return m_file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
}
}
void FileLogger::setLogLevel(LEVEL level)
{
m_level[QtDebugMsg] = static_cast<bool> (level | E_DEBUG);
m_level[QtInfoMsg] = static_cast<bool> (level | E_INFO);
m_level[QtWarningMsg] = static_cast<bool> (level | E_WARNING);
m_level[QtCriticalMsg] = static_cast<bool> (level | E_CRITICAL);
m_level[QtFatalMsg] = static_cast<bool> (level | E_FATAL);
}
void FileLogger::setLogLevel(bool debug, bool info, bool warning, bool critical, bool fatal)
{
m_level[QtDebugMsg] = debug;
m_level[QtInfoMsg] = info;
m_level[QtWarningMsg] = warning;
m_level[QtCriticalMsg] = critical;
m_level[QtFatalMsg] = fatal;
}
void FileLogger::writeLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if(!m_level[type])
{
return;
}
QString strTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QTextStream logfile(&m_file);
logfile << strTime << ", ";
logfile << messageType(type) << ", ";
logfile << msg;
if(context.file && m_where)
{
logfile << ", (" << context.file << ":" << context.line << ", " << context.function << ")\n";
}
else
{
logfile << endl;
}
if(type == QtFatalMsg)
{
abort();
}
}
QString FileLogger::messageType(QtMsgType type)
{
QString str;
switch (type)
{
case QtDebugMsg:
str = "[Debug] ";
break;
case QtInfoMsg:
str = "[Info] ";
break;
case QtWarningMsg:
str = "[Warning] ";
break;
case QtCriticalMsg:
str = "[Critical]";
break;
case QtFatalMsg:
str = "[Fatal] ";
}
return str;
}
void LoggerController::attach(FileLogger *logger)
{
m_list.append(logger);
}
void LoggerController::detach(FileLogger *logger)
{
if(logger)
{
m_list.removeOne(logger);
delete logger;
}
}
void LoggerController::startLogging()
{
m_currentLogger = this;
qInstallMessageHandler(LoggerController::writeLog);
}
LoggerController::~LoggerController()
{
qDeleteAll(m_list);
}
LoggerController* LoggerController::m_currentLogger = nullptr;
void LoggerController::writeLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if( m_currentLogger )
{
QList<FileLogger *> &list = m_currentLogger->m_list;
QList<FileLogger *>::const_iterator i;
for (i = list.cbegin(); i != list.cend(); ++i)
{
(*i)->writeLog(type, context, msg);
}
}
}
#include <QCoreApplication>
#include "MessageLogger.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
LoggerController logger;
logger.attach(new FileLogger("stderr",
FileLogger::E_DEBUG |
FileLogger::E_INFO |
FileLogger::E_WARNING |
FileLogger::E_CRITICAL |
FileLogger::E_FATAL));
logger.attach(new FileLogger("d:/log2.txt"));
logger.startLogging();
qDebug() << "this is a debug message" ;
qInfo() << "this is a info message";
qWarning() << "this is a warning message";
qCritical() << "this is a critical message";
return a.exec();
}
项目文件我放到了 code.csdn.net 上了,网址如下:
https://code.csdn.net/liyuanbhu/qtlogger.git
这个日志库我还会继续维护,增加新的功能。希望最后能成为一个比较完善功能的日志库。