在 Qt 项目中使用 spdlog 的全攻略

目录

1. 准备工作:安装 spdlog

方法一:使用 CMake 的 FetchContent(推荐)

方法二:手动下载并添加到项目中

2. 在 Qt 项目中集成 spdlog

a. 初始化 spdlog

b. 在 Qt 的各个部分使用 spdlog

3. 基本使用示例

4. 高级配置:多种输出目标和格式化

a. 多种 Sink

b. 自定义格式化

5. 异步日志记录

6. 在 Qt 中处理线程安全

7. 常见问题与解决方案

问题1:日志文件没有生成或写入

问题2:日志级别不正确

问题3:异步日志丢失日志消息


1. 准备工作:安装 spdlog

首先,我们需要将 spdlog 添加到你的 Qt 项目中。spdlog 是一个头文件库,你可以通过以下几种方式来获取它:

方法一:使用 CMake 的 FetchContent(推荐)

如果你的 Qt 项目使用 CMake 构建,这是最简单的方法。

# 在你的 CMakeLists.txt 文件中添加以下内容

cmake_minimum_required(VERSION 3.14)
project(MyQtApp)

set(CMAKE_CXX_STANDARD 17)

# 引入 Qt
find_package(Qt5 COMPONENTS Widgets REQUIRED)

# 使用 FetchContent 下载 spdlog
include(FetchContent)
FetchContent_Declare(
  spdlog
  GIT_REPOSITORY https://github.com/gabime/spdlog.git
  GIT_TAG        v1.11.0 # 选择一个稳定的版本
)
FetchContent_MakeAvailable(spdlog)

# 添加可执行文件
add_executable(MyQtApp main.cpp mainwindow.cpp mainwindow.h mainwindow.ui)

# 链接 Qt 和 spdlog
target_link_libraries(MyQtApp PRIVATE Qt5::Widgets spdlog::spdlog)
方法二:手动下载并添加到项目中
  1. 前往 spdlog 的 GitHub 仓库
  2. 下载最新的 release 版本(ZIP 文件)。
  3. spdlog/include 文件夹复制到你的项目目录中(例如,放在 external/spdlog)。
  4. 在你的 CMakeLists.txt 中添加如下内容:
# 添加 spdlog 的包含路径
include_directories(${CMAKE_SOURCE_DIR}/external/spdlog/include)

# 假设你不需要构建 spdlog 库,而是作为头文件库使用
add_executable(MyQtApp main.cpp mainwindow.cpp mainwindow.h mainwindow.ui)

# 链接 Qt
target_link_libraries(MyQtApp PRIVATE Qt5::Widgets)

2. 在 Qt 项目中集成 spdlog

假设你已经将 spdlog 添加到项目中,接下来我们来配置和使用它。

a. 初始化 spdlog

在你的 Qt 项目的入口处( main.cpp),初始化 spdlog

// main.cpp
#include <QApplication>
#include "mainwindow.h"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    // 初始化 spdlog
    try {
        // 创建一个同时输出到控制台和文件的 logger
        auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/myapp.log", true);
        
        std::vector<spdlog::sink_ptr> sinks {console_sink, file_sink};
        auto logger = std::make_shared<spdlog::logger>("multi_sink", sinks.begin(), sinks.end());
        
        // 设置全局日志器
        spdlog::set_default_logger(logger);
        spdlog::set_level(spdlog::level::info); // 设置日志级别
        spdlog::flush_on(spdlog::level::info);  // 设置自动刷新级别
    }
    catch (const spdlog::spdlog_ex &ex) {
        fprintf(stderr, "日志初始化失败: %s\n", ex.what());
        return 1;
    }

    return a.exec();
}
b. 在 Qt 的各个部分使用 spdlog

在你的 MainWindow 或其他类中使用 spdlog 来记录日志:

// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <spdlog/spdlog.h>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    spdlog::info("应用程序已启动");
}

MainWindow::~MainWindow()
{
    spdlog::info("应用程序正在关闭");
    delete ui;
}

void MainWindow::on_someButton_clicked()
{
    spdlog::info("按钮被点击了!");
    // 你的其他代码
}

void MainWindow::on_errorCondition()
{
    spdlog::error("发生了一个错误!");
    // 错误处理代码
}

3. 基本使用示例

让我们来看一个简单的示例,展示如何在 Qt 应用中记录不同级别的日志。

// 在你的某个函数中
void MainWindow::performTask()
{
    spdlog::debug("任务开始");
    spdlog::info("任务进行中");
    
    if (/* 某个条件 */) {
        spdlog::warn("遇到警告条件");
    }

    try {
        // 可能会抛出异常的代码
    }
    catch (const std::exception &e) {
        spdlog::error("异常捕获: {}", e.what());
    }

    spdlog::critical("任务完成,但有严重问题!");
}

4. 高级配置:多种输出目标和格式化

a. 多种 Sink

spdlog 允许你将日志输出到多个目标(sinks),比如文件、控制台、甚至网络。

// 仅输出到控制台
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();

// 仅输出到文件
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/myapp.log", true);

// 创建包含多个 sinks 的 logger
std::vector<spdlog::sink_ptr> sinks {console_sink, file_sink};
auto logger = std::make_shared<spdlog::logger>("multi_sink", sinks.begin(), sinks.end());

// 设置为默认 logger
spdlog::set_default_logger(logger);
b. 自定义格式化

你可以定义日志的输出格式,使其更符合你的需求。

// 设置日志格式
// 例如: [2024-04-27 12:34:56.789] [info] [main.cpp:42] 这是一个信息日志
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%s:%#] %v");

常用的格式化标识:

  • %Y-%m-%d %H:%M:%S:日期和时间
  • %l:日志级别(info, error, etc.)
  • %s:源文件名
  • %#:源代码行号
  • %v:日志消息

5. 异步日志记录

对于需要高性能的应用,异步日志记录是个不错的选择。spdlog 提供了简便的异步日志支持。

#include <spdlog/async.h> // 需要包含这个头文件
#include <spdlog/sinks/basic_file_sink.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    try {
        // 初始化线程池,队列大小为8192,线程数为1
        spdlog::init_thread_pool(8192, 1);

        // 创建一个异步文件日志器
        auto async_file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async_log.txt", true);
        auto async_logger = std::make_shared<spdlog::async_logger>("async_logger", async_file_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::block);
        
        // 将异步 logger 添加到默认 logger
        spdlog::register_logger(async_logger);
        spdlog::set_default_logger(async_logger);

        spdlog::set_level(spdlog::level::info);
        spdlog::flush_on(spdlog::level::info);
    }
    catch (const spdlog::spdlog_ex &ex) {
        fprintf(stderr, "日志初始化失败: %s\n", ex.what());
        return 1;
    }

    return a.exec();
}

6. 在 Qt 中处理线程安全

Qt 应用通常涉及多线程操作,spdlog 是线程安全的,这意味着你可以在多个线程中安全地记录日志而无需担心竞争条件。

// 在主线程中
void MainWindow::startBackgroundTask()
{
    std::thread([=]() {
        spdlog::info("后台任务开始");
        // 执行一些耗时操作
        spdlog::info("后台任务完成");
    }).detach();
}

7. 常见问题与解决方案

问题1:日志文件没有生成或写入

解决方案

  • 确保指定的日志目录存在。例如,如果你指定 logs/myapp.log,确保 logs 目录已创建。
  • 检查文件路径是否正确,尤其是在不同操作系统上的路径格式。
问题2:日志级别不正确

解决方案

  • 确保设置了正确的日志级别。例如,默认级别可能是 info,如果你使用 debug 级别的日志,可能不会输出。通过 spdlog::set_level(spdlog::level::debug); 来调整。
问题3:异步日志丢失日志消息

解决方案

  • 在应用程序退出前,调用 spdlog::shutdown(); 确保所有日志消息被写入。

    int main(int argc, char *argv[])
    {
        // 初始化代码...
        
        int result = a.exec();
        
        spdlog::shutdown(); // 确保所有日志被写入
        return result;
    }
    

参考:

日志的艺术:深入理解 spdlog-CSDN博客

好的,以下是一个基于spdlogQt项目的日志系统,包含了日志打印信息颜色区分、日志文件轮换、日志压缩等功能。您可以参考这个代码来实现您自己的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() { // 初始化spdlogspdlog::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类来记录日志信息,并使用不同的记录器实现文件日志的按大小轮转、按日期轮转、压缩等功能。在输出日志信息时,使用颜色标志符将不同级别的日志信息区分开来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值