文章目录
- 前言
 - spdlog 与 glog
 
- spdlog介绍
 - 内容
 - 日志等级
 - 输出格式
 
- 使用
 - 步骤
 - 同步日志输出
 - 异步日志输出
 - 封装一个日志输出类
 - 对日志输出类的测试
 
- 安装
 
前言
在编写项目时,通过日志追踪程序错误或其他情况出现的位置是十分重要的,对于不太复杂的场景,可以通过自行编写简单的日志记录文件,但对于大型项目,可以使用较为完善,稳定的日志记录工具;
spdlog 与 glog
一般有两种选择glog 与 spdlog,下面是两者的对比:
| 特性 | spdlog | glog | 
|---|---|---|
| 性能 | 高性能,支持异步日志 | 性能较好,但主要为同步日志 | 
| 易用性 | 简洁明了,易于上手 | API 较复杂,适合 Google 环境 | 
| 日志格式 | 支持格式化字符串 | 不支持格式化 | 
| 日志级别 | Trace、Debug、Info、Warning、Error、Critical | Info、Warning、Error、Fatal | 
| 输出目标 | 控制台、文件、自定义目标 | 默认文件输出 | 
| 颜色输出 | 支持 | 不支持 | 
| 依赖 | 几乎没有外部依赖 | 依赖于 gflags | 
| 自定义能力 | 高,支持自定义格式和目标 | 较低,主要集中在调试信息 | 
| 使用场景 | 游戏开发、实时系统等 | 大规模软件开发、调试工具 | 
根据上面的内容,这里我们学习spdlog的使用:
spdlog介绍
spdlog 是一个高性能、超快速、零配置的 C++ 日志库,它旨在提供简洁的 API 和丰富的功能,同时保持高性能的日志记录。它支持多种输出目标、格式化选项、线程安全以及异步日志记录。
- 高性能:
spdlog专为速度而设计,即使在高负载情况下也能保持出色的性能。 - 零配置:使用简单,无需复杂配置,只需包含头文件即可在项目中使用。
 - 异步日志:支持异步日志记录,显著减少对主线程的影响。
 - 格式化:支持自定义日志消息格式化,包括时间戳、线程 ID、日志级别等信息。
 - 多平台兼容:跨平台支持,兼容 Windows、Linux、macOS 等操作系统。
 - 丰富的 API:提供多种日志级别和操作符重载,方便记录不同类型的日志信息。
 
内容
如果要使用spdlog,首先包含头文件:
#include <spdlog/spdlog.h>
 
日志等级
通过查阅common.h可以找到spdlog的日志等级枚举:
namespace level {
enum level_enum
{
    trace = SPDLOG_LEVEL_TRACE,
    debug = SPDLOG_LEVEL_DEBUG,
    info = SPDLOG_LEVEL_INFO,
    warn = SPDLOG_LEVEL_WARN,
    err = SPDLOG_LEVEL_ERROR,
    critical = SPDLOG_LEVEL_CRITICAL,
    off = SPDLOG_LEVEL_OFF,
    n_levels
};
 
输出格式
我们可以自定义spdlog的日志输出格式
logger->set_pattern("%Y-%m-%d %H:%M:%S [%t] [%-7l] %v");
 
| 格式符 | 描述 | 
|---|---|
%t | 线程 ID | 
%n | 日志器名称 | 
%l | 日志级别名称 | 
%v | 日志内容 | 
%Y | 年 | 
%m | 月 | 
%d | 日 | 
%H | 小时(24-hour format) | 
%M | 分钟 | 
%S | 秒 | 
使用
步骤
一般在通过spdlog输出日志往往有以下几个步骤:
- 引入 
spdlog库 
确保在项目中包含 spdlog 库的头文件。
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h> // 如果需要文件输出
 
- 初始化日志记录器
 
创建一个日志记录器,可以选择控制台输出或文件输出。
// 控制台输出
auto console = spdlog::stdout_color_mt("console");
// 文件输出
auto file_logger = spdlog::basic_file_sink_mt("logs.txt", true);
 
- 设置日志级别
 
设置日志的输出级别,以控制哪些信息会被记录。
spdlog::set_level(spdlog::level::info); // 只输出 INFO 及以上级别的日志
 
- 输出日志信息
 
使用不同的日志级别输出信息。
spdlog::info("This is an info message");
spdlog::debug("This debug message will not be shown"); // 如果级别设置为 INFO,这条不会输出
spdlog::error("This is an error message");
 
- (可选) 自定义日志格式
 
如果需要,可以设置自定义的日志格式。
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%n] [%l] %v");
 
- 清理资源
 
在程序结束前,可以选择清理日志资源(虽然 spdlog 会自动处理大部分情况)。
spdlog::shutdown();
 
同步日志输出
根据上面的内容,下面编写一个代码用于进行 同步的日志输出:
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
int main()
{
    // 1. 设置全局刷新策略
        // 每秒刷新
    spdlog::flush_every(std::chrono::seconds(1));
        //  遇到debug以上等级日志: 立即刷新
    spdlog::flush_on(spdlog::level::level_enum::debug);
    // 2. 设置全局日志输出等级(可以根据每个日志器独立设置)
    spdlog::set_level(spdlog::level::level_enum::debug);
    // 3. 创建同步日治器(标准输出 / 文件) 默认是同步
    auto logger = spdlog::basic_logger_mt("file-logger", "./sync.log");
    // 4. 设置日志器刷新策略
    logger->flush_on(spdlog::level::level_enum::debug);
    logger->set_level(spdlog::level::level_enum::debug);
    // 5. 设置日志输出格式
    logger->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");
    // 6. 执行日志输出
    logger->trace("trace message");
    logger->debug("debug message");
    logger->info("info message");
    logger->warn("warn message");
    logger->error("error message");
    logger->critical("critical message");
    // 7. 删除日志器
    spdlog::drop("file-logger");
    std::cout << "spdlog测试 | 日志输出完毕" << std::endl;
    return 0;
}
 
当编译并执行可执行程序后,会生成文件sync.log,其内容为:

异步日志输出
代码实现上与同步日志输出一致,主要在创建日志器时不同;
#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/async.h>
int main()
{
    // 1. 设置全局刷新策略
        // 每秒刷新
    spdlog::flush_every(std::chrono::seconds(1));
    spdlog::flush_on(spdlog::level::level_enum::debug);
    spdlog::set_level(spdlog::level::level_enum::debug);
    // 2. 初始化异步日志输出 线程配置
    spdlog::init_thread_pool(1145, 1);
    // 3. 创建异步日志器(标准输出/文件) 
    auto logger = spdlog::basic_logger_mt("async_logger", "async.log");
    // 4. 设置日志输出格式
    logger->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");
    // 5. 进行日志输出
    logger->trace("trace log {}", "hello world");
    logger->debug("debug log {}", "hello world");
    logger->info("info log {}", "hello world");
    logger->warn("warn log {}", "hello world");
    logger->error("error log {}", "hello world");
    logger->critical("critical log {}", "hello world");
    
    return 0;
}
 

封装一个日志输出类
如果每次想进行日志输出都需要先经过一系列步骤,在编码上会比较复杂,所以可以先封装一个日志输出类,每次使用前进行初始化:
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/async.h>
#include <iostream>
std::shared_ptr<spdlog::logger> default_logger;
// mode: 运行模式
// mode = true: 发布模式 false: 调试模式
void init_logger(bool mode, const std::string& file_name, int level)
{
    if(mode) { // 发布模式: 将日志输出到文件
        default_logger = spdlog::basic_logger_mt("default_logger", file_name);
        default_logger->set_level((spdlog::level::level_enum)level);
        default_logger->flush_on((spdlog::level::level_enum)level);
    } else { // 调试模式: 直接将日志输出到标准输出
        default_logger = spdlog::stdout_color_mt("default-logger");
        default_logger->set_level(spdlog::level::level_enum::trace);
        default_logger->flush_on(spdlog::level::level_enum::trace);
    }
    default_logger->set_pattern("[%n][%H:%M:%S][%t][%-8l]%v");
}
#define LOG_TRACE(format, ...) default_logger->trace(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(format, ...) default_logger->debug(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(format, ...) default_logger->info(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(format, ...) default_logger->warn(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) default_logger->error(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_CRITICAL(format, ...) default_logger->critical(std::string("[{}:{}] ") + format, __FILE__, __LINE__, ##__VA_ARGS__)
 
对日志输出类的测试
我们编写如下的代码进行测试日志输出类:
#include "logger.hpp"
#include <iostream>
#include <gflags/gflags.h>
DEFINE_bool(run_mode, false, "调试模式下的日志输出");
// DEFINE_string(log_path, "./log", "日志文件路径");
DEFINE_string(log_name, "", "日志文件名称");
DEFINE_int32(log_level, 0, "日志级别");
int main(int argc, char* argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, false);
    init_logger(FLAGS_run_mode, FLAGS_log_name, FLAGS_log_level);
    LOG_TRACE("This is a trace log: {}", "TRACE");
    LOG_DEBUG("This is a debug log: {}", "DEBUG");
    LOG_INFO("This is a info log: {}", "INFO");
    LOG_WARN("This is a warn log: {}", "WARN");
    LOG_ERROR("This is a error log: {}", "ERROR");
    LOG_CRITICAL("This is a critical log: {}", "CRITICAL");
    return -1;
}
 
此时可以生成可执行程序,并根据以下命令进行运行:
./main --run_mode=true --log_name="my_log" --log_level=2
 
生成日志文件为:
wqy@wqy-virtual-machine:~/warehouse/gitClone/linux/Project/MIM-System/test/spdlog-test$ cat my_log
[default_logger][08:37:51][6590][info    ][main.cc:18] This is a info log: INFO
[default_logger][08:37:51][6590][warning ][main.cc:19] This is a warn log: WARN
[default_logger][08:37:51][6590][error   ][main.cc:20] This is a error log: ERROR
[default_logger][08:37:51][6590][critical][main.cc:21] This is a critical log: CRITICAL
 
安装
一般习惯直接通过包管理器进行安装:
-  
Ubuntu/Debian:
sudo apt update sudo apt install libspdlog-dev -  
Fedora:
sudo dnf install spdlog-devel 
当然也可以通过源码进行编译安装:
-  
安装依赖(确保你已经安装了 CMake 和构建工具):
sudo apt install cmake g++ # Ubuntu/Debian -  
克隆
spdlog的 GitHub 仓库:git clone https://github.com/gabime/spdlog.git cd spdlog -  
创建构建目录并进入:
mkdir build cd build -  
运行 CMake:
cmake .. -  
编译和安装:
make sudo make install 


















