【Qt】【第三方库】spdlog日志模块的使用

版本

spdlog版本:1.5.0

采用1.5.0版本主要基于以下考虑:兼容Qt5.9.X版本和兼容C++11。

spdlog 1.5.0下载地址:https://github.com/gabime/spdlog/releases/tag/v1.5.0

摘要

在Qt应用程序开发中,良好的日志系统至关重要。本文将介绍如何使用spdlog 1.5.0创建满足以下要求的日志系统:

  1. 自定义文件名格式:yyyyMMdd_hhmmss_毫秒.log,不使用spdlog提供的日志轮转功能

    spdlog::sinks::rotating_file_sink_mt,采用自定义custom_rotating_file_sink;

  2. 保留最近10个日志文件,每个日志文件大小限制为1MB。

例子

logmanager.h文件

cpp 复制代码
#ifndef LOGMANAGER_H
#define LOGMANAGER_H

#include <QObject>
#include <memory>
#include <spdlog/spdlog.h>

class LogManager : public QObject
{
    Q_OBJECT
public:
    static LogManager& instance();

    void initialize(const QString& logDir = "logs",
                    const QString& appName = "app",
                    size_t maxFileSize = 1024 * 1024, // 1MB
                    size_t maxFiles = 10);
    void shutdown();

    template<typename... Args>
    static void log(spdlog::level::level_enum level, const QString& message, Args... args)
    {
        if (instance().m_logger)
        {
            instance().m_logger->log(level, message.toStdString().c_str(), args...);
        }
    }

    // 便捷方法
    static void trace(const QString& message)
    {
        log(spdlog::level::trace, message);
    }
    static void debug(const QString& message)
    {
        log(spdlog::level::debug, message);
    }
    static void info(const QString& message)
    {
        log(spdlog::level::info, message);
    }
    static void warn(const QString& message)
    {
        log(spdlog::level::warn, message);
    }
    static void error(const QString& message)
    {
        log(spdlog::level::err, message);
    }
    static void critical(const QString& message)
    {
        log(spdlog::level::critical, message);
    }


private:
    LogManager(QObject* parent = nullptr);
    ~LogManager();

    std::shared_ptr<spdlog::logger> createCustomLogger(const std::string& base_filename,
            size_t max_size,
            size_t max_files);

    std::shared_ptr<spdlog::logger> m_logger;
    std::atomic<bool> m_shuttingDown{false};

signals:
    void aboutToShutdown();

private slots:
    void onAboutToQuit();

};

// 日志宏定义
#define LOG_TRACE(...)    LogManager::log(spdlog::level::trace, __VA_ARGS__)
#define LOG_DEBUG(...)    LogManager::log(spdlog::level::debug, __VA_ARGS__)
#define LOG_INFO(...)     LogManager::log(spdlog::level::info, __VA_ARGS__)
#define LOG_WARN(...)     LogManager::log(spdlog::level::warn, __VA_ARGS__)
#define LOG_ERROR(...)    LogManager::log(spdlog::level::err, __VA_ARGS__)
#define LOG_CRITICAL(...) LogManager::log(spdlog::level::critical, __VA_ARGS__)

#endif // LOGMANAGER_H

logmanager.cpp文件

cpp 复制代码
#include "logmanager.h"
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/file_helper.h>
#include <mutex>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <vector>
#include <algorithm>
#include <QDir>
#include <QFileInfo>
#include <QDateTime>
#include <QCoreApplication>
#include <csignal>
#include <QDebug>

// 替换 std::filesystem 的 C++11 兼容实现
namespace spdlog
{
    class custom_rotating_file_sink : public spdlog::sinks::base_sink<std::mutex>
    {
    public:
        custom_rotating_file_sink(const std::string& base_filename,
                                  std::size_t max_size,
                                  std::size_t max_files)
            : base_filename_(base_filename),
              max_size_(max_size),
              max_files_(max_files)
        {
            file_helper_.open(gen_filename());
        }

    protected:
        void sink_it_(const spdlog::details::log_msg& msg) override
        {
            spdlog::memory_buf_t formatted;
            formatter_->format(msg, formatted);

            if (file_helper_.size() + formatted.size() > max_size_)
            {
                rotate_();
            }

            file_helper_.write(formatted);
        }

        void flush_() override
        {
            file_helper_.flush();
        }

    private:
        std::string gen_filename()
        {
            QDateTime now = QDateTime::currentDateTime();
            QString timeStr = now.toString("yyyyMMddhhmmss");

            // 添加毫秒部分(3位)
            int ms = now.time().msec();
            timeStr += QString("_%1").arg(ms, 3, 10, QLatin1Char('0'));

            return base_filename_ + "_" + timeStr.toStdString() + ".log";
        }

        void rotate_()
        {
            file_helper_.close();
            cleanup_old_files();
            file_helper_.open(gen_filename());
        }

        void cleanup_old_files()
        {
            if (max_files_ == 0) return;

            QFileInfo base_info(QString::fromStdString(base_filename_));
            QDir dir = base_info.absoluteDir();
            QString base_name = base_info.fileName();

            QFileInfoList files = dir.entryInfoList(QStringList() << (base_name + "_*.log"),
                                                    QDir::Files, QDir::Time);

            // 删除最旧的文件
            while (files.size() >= static_cast<int>(max_files_))
            {
                QFile::remove(files.last().absoluteFilePath());
                files.removeLast();
            }
        }

        std::string base_filename_;
        std::size_t max_size_;
        std::size_t max_files_;
        spdlog::details::file_helper file_helper_;
    };

} // namespace

LogManager::LogManager(QObject* parent) : QObject(parent)
{
    // 连接Qt退出信号
    connect(qApp, &QCoreApplication::aboutToQuit, this, &LogManager::onAboutToQuit);

    // 处理异常信号
    static auto handleSignal = [](int)
    {
        LogManager::instance().shutdown();
        std::_Exit(1);
    };

    std::signal(SIGTERM, handleSignal);
    std::signal(SIGSEGV, handleSignal);
    std::signal(SIGINT, handleSignal);
    std::signal(SIGABRT, handleSignal);
}

LogManager::~LogManager()
{
    shutdown();
}

LogManager& LogManager::instance()
{
    static LogManager instance;
    return instance;
}

void LogManager::initialize(const QString& logDir, const QString& appName, size_t maxFileSize, size_t maxFiles)
{
    if (m_logger)
    {
        return;
    }

    // 确保日志目录存在
    QDir().mkpath(logDir);

    std::string base_filename = QDir(logDir).absoluteFilePath(appName).toStdString();
    m_logger = createCustomLogger(base_filename, maxFileSize, maxFiles);

    // 设置默认日志格式
    m_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [thread %t] %v");
    m_logger->set_level(spdlog::level::trace);

    spdlog::register_logger(m_logger);
    spdlog::set_default_logger(m_logger);
}

void LogManager::shutdown()
{
    /*
    if (m_logger)
    {
        spdlog::drop(m_logger->name());
        m_logger.reset();
    }
    spdlog::shutdown();
    */
    if (m_shuttingDown) return;
    m_shuttingDown = true;

    emit aboutToShutdown();

    try
    {
        if (m_logger)
        {
            m_logger->flush();
            spdlog::drop(m_logger->name());
        }
        spdlog::shutdown();
        m_logger.reset();
    }
    catch (const spdlog::spdlog_ex& ex)
    {
        qCritical() << "Log shutdown error:" << ex.what();
    }
}

void LogManager::onAboutToQuit()
{
    shutdown();
}

std::shared_ptr<spdlog::logger> LogManager::createCustomLogger(const std::string& base_filename,
        size_t max_size,
        size_t max_files)
{
    auto sink = std::make_shared<spdlog::custom_rotating_file_sink>(base_filename, max_size, max_files);
    auto logger = std::make_shared<spdlog::logger>("qt_logger", sink);
    return logger;
}

main.cpp文件

cpp 复制代码
#include <QCoreApplication>
#include "logmanager.h"
#include <QTimer>
#include <QDebug>

int main(int argc, char* argv[])
{
    QCoreApplication a(argc, argv);

    // 初始化日志系统
    LogManager::instance().initialize("logs", "MyAppTest");

    // 连接关闭信号进行额外清理
    QObject::connect(&LogManager::instance(), &LogManager::aboutToShutdown, []()
    {
        LOG_INFO("Performing final cleanup before shutdown...");
    });

    for (int i = 0; i < 5000; ++i)
    {
        LOG_INFO("This is a test message to fill up the log file. Iteration: {}", i);
    }

    return a.exec();
}

下载地址:https://download.csdn.net/download/sunriver2000/90602858

相关推荐
用户805533698035 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner5 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz10 天前
QML Hello World 入门示例
qt
xcyxiner13 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner14 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner15 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能16 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G17 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt