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

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

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

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,以下是一个基于spdlog的Qt项目中的日志系统,包含了日志打印信息颜色区分、日志文件轮换、日志压缩等功能。您可以参考这个代码来实现您自己的Qt项目日志系统。 首先,在您的Qt项目中添加spdlog的头文件和源文件。可以将spdlog的源文件直接放到您的项目文件夹中,然后在.pro文件中添加相应的路径。 ```pro INCLUDEPATH += $$PWD/spdlog/include SOURCES += $$PWD/spdlog/src/spdlog.cpp ``` 接下来,创建一个Logger类,封装spdlog的接口函数,用于记录日志信息。 logger.h ```c++ #ifndef LOGGER_H #define LOGGER_H #include <memory> #include "spdlog/spdlog.h" class Logger { public: static Logger& instance(); std::shared_ptr<spdlog::logger> get_console_logger(const std::string& name); std::shared_ptr<spdlog::logger> get_file_logger(const std::string& name, const std::string& filename); std::shared_ptr<spdlog::logger> get_rotating_file_logger(const std::string& name, const std::string& filename, size_t max_size, size_t max_files); std::shared_ptr<spdlog::logger> get_daily_file_logger(const std::string& name, const std::string& filename, int hour, int minute); std::shared_ptr<spdlog::logger> get_compressed_file_logger(const std::string& name, const std::string& filename, size_t max_size, size_t max_files); private: Logger(); }; #endif // LOGGER_H ``` logger.cpp ```c++ #include "logger.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/daily_file_sink.h" #include "spdlog/sinks/compressed_file_sink.h" Logger::Logger() { // 初始化spdlog spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] %v"); // 如果需要输出到控制台,请取消下面这行代码的注释 //auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); //spdlog::default_logger()->sinks().push_back(console_sink); } Logger& Logger::instance() { static Logger logger; return logger; } std::shared_ptr<spdlog::logger> Logger::get_console_logger(const std::string& name) { return spdlog::stdout_color_mt(name); } std::shared_ptr<spdlog::logger> Logger::get_file_logger(const std::string& name, const std::string& filename) { return spdlog::basic_logger_mt(name, filename); } std::shared_ptr<spdlog::logger> Logger::get_rotating_file_logger(const std::string& name, const std::string& filename, size_t max_size, size_t max_files) { return spdlog::rotating_logger_mt(name, filename, max_size, max_files); } std::shared_ptr<spdlog::logger> Logger::get_daily_file_logger(const std::string& name, const std::string& filename, int hour, int minute) { return spdlog::daily_logger_mt(name, filename, hour, minute); } std::shared_ptr<spdlog::logger> Logger::get_compressed_file_logger(const std::string& name, const std::string& filename, size_t max_size, size_t max_files) { return spdlog::rotating_logger_mt<spdlog::sinks::compressed_file_sink_st>(name, filename, max_size, max_files); } ``` 最后,在您的Qt项目中使用Logger类来记录日志信息。 ```c++ #include "mainwindow.h" #include "ui_mainwindow.h" #include "logger.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // 获取控制台日志器 auto console_logger = Logger::instance().get_console_logger("console"); // 获取文件日志器,使用基本文件记录器 auto file_logger = Logger::instance().get_file_logger("file", "example.log"); // 获取文件日志器,使用按大小轮转的文件记录器 auto rotating_file_logger = Logger::instance().get_rotating_file_logger("rotating_file", "example.rotating", 1024 * 1024 * 5, 3); // 获取文件日志器,使用按日期轮转的文件记录器 auto daily_file_logger = Logger::instance().get_daily_file_logger("daily_file", "example.%Y-%m-%d.log", 0, 0); // 获取文件日志器,使用压缩文件记录器 auto compressed_file_logger = Logger::instance().get_compressed_file_logger("compressed_file", "example.gz", 1024 * 1024 * 5, 3); // 输出带有颜色的信息 console_logger->info("\033[0;32mThis is a green message!\033[0m"); console_logger->warn("\033[0;33mThis is a yellow warning!\033[0m"); console_logger->error("\033[0;31mThis is a red error!\033[0m"); // 记录日志信息到文件 file_logger->info("This is a file message!"); rotating_file_logger->info("This is a rotating file message!"); daily_file_logger->info("This is a daily file message!"); compressed_file_logger->info("This is a compressed file message!"); } MainWindow::~MainWindow() { delete ui; } ``` 上述代码演示了如何在Qt项目中使用Logger类来记录日志信息,并使用不同的记录器实现文件日志的按大小轮转、按日期轮转、压缩等功能。在输出日志信息时,使用颜色标志符将不同级别的日志信息区分开来。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值