一个用于 Qt 项目的简单的日志库

来源:互联网 发布:三千元手机推荐知乎 编辑:程序博客网 时间:2024/05/17 22:41

一个用于 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

这个日志库我还会继续维护,增加新的功能。希望最后能成为一个比较完善功能的日志库。

原创粉丝点击