spdlog的基本使用和二次封装

介绍

一句话总结:头文件即可用、线程安全、支持异步、格式化友好,性能爆炸。

项目主页:https://github.com/gabime/spdlog

特性 说明
零配置 仅含头文件(header-only),#include 即可用
高性能 每秒可写千万级日志;异步模式下锁竞争极小
线程安全 多线程并发写同一条 sink 无需额外加锁
丰富 Sink 控制台(带颜色)、文件、滚动文件、每日轮换、syslog、远程 tcp …
现代格式化 内置 fmt 库,支持 Python 风格占位符 { }
异步支持 通过 spdlog::async_logger + 线程池,彻底解耦业务线程与 IO
分级过滤 trace/debug/info/warn/error/critical/off 六级,运行时可调
自定义日志器 可同时维护多个 logger,各自独立级别、输出目标

日志级别管理 vs std::cout

特性 日志库(spdlog/log4cpp 等) std::cout
分级过滤 ✅ TRACE < DEBUG < INFO < WARN < ERROR < FATAL ❌ 无级别概念
运行时可调 ✅ 改一行代码或配置即可切换全局/单 logger 级别 ❌ 必须手动注释/删除输出语句
环境适配 开发调 DEBUG,生产只留 ERROR/WARN,日志量可控 永远全量输出,文件容易爆
结论 按“级别”开关,灵活、干净、性能可控 临时打印方便,无法胜任正式日志需求

安装与配置

命令安装

sudo apt-get install libspdlog-dev

源码安装

git clone https://github.com/gabime/spdlog.git
cd spdlog/
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make && sudo make install

标准日志输出

  1. 包含头文件\#include "spdlog/spdlog.h"
  2. 创建日志对象

std::shared_ptr<logger> stdout_color_mt( const std::string &logger_name, color_mode mode = color_mode::automatic);

  1. 设置日志输出等级:void set_level(level::level_enum log_level);
  2. 设置日志输出格式:void set_pattern(std::string pattern, pattern_time_type time_type);
  3. 输出日志
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
#include "spdlog/spdlog.h"

int main()
{
    auto logger = spdlog::stdout_color_mt("stdout_logger");
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S ][%-7l]:%v");//自定义打印格式:时间 | 左对齐 7 字符级别 | 正文
    logger->debug("{} 今年 {} 岁了", "小明", 18);
    logger->info("{} 今年 {} 岁了", "小红", 17);
    logger->warn("{} 今年 {} 岁了", "小刚", 19);
    logger->error("{} 今年 {} 岁了", "小华", 20);
    return 0;
}

基于文件输出

//指定⽂件
template<typename Factory = spdlog::synchronous_factory>
std::shared_ptr<logger> basic_logger_mt(
const std::string &logger_name,
const filename_t &filename,
bool truncate = false,
const file_event_handlers &event_handlers = {})
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include "spdlog/spdlog.h"

int main()
{
    auto logger = spdlog::basic_logger_mt("file_logger", "./file.dat");
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S ][%-7l]:%v");
    logger->debug("{} 今年 {} 岁了", "小明", 18);
    logger->info("{} 今年 {} 岁了", "小红", 17);
    logger->warn("{} 今年 {} 岁了", "小刚", 19);
    logger->error("{} 今年 {} 岁了", "小华", 20);
    return 0;
}

循环文件输出

//循环⽂件
template<typename Factory = spdlog::synchronous_factory>
std::shared_ptr<logger> rotating_logger_mt(
const std::string &logger_name,
const filename_t &filename,
size_t max_file_size,
size_t max_files,
bool rotate_on_open = false)
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include "spdlog/spdlog.h"

int main()
{
    auto logger = spdlog::rotating_logger_mt("file_logger", "./rotating.dat", 1024, 3);
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S ][%-7l]:%v");
    for(int i=0;i<100;++i)
    {
        logger->error("Hello Loh! --  {}", i);
    }
    return 0;
}

异步输出

using async_factory = async_factory_impl<async_overflow_policy::block>;
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include "spdlog/spdlog.h"

int main()
{
    auto logger = spdlog::rotating_logger_mt<spdlog::async_factory>("file_logger", "./rotating.dat", 1024, 3);
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S ][%-7l]:%v");
    for(int i=0;i<100;++i)
    {
        logger->error("Hello Log! --  {}", i);
    }
    return 0;
}

spdlong的二次封装

因为spdlog本⾝提供的⽇志输出操作⽆法输出错误位置的⽂件名和⾏号,对于我们排查错误的时候有所不利

spdlog 原生接口没有 __FILE__ / __LINE__ 能力,排查问题时只能看到日志内容却定位不到代码位置

因此通过二次封装,给 spdlog 加上“文件名 + 行号”——宏封装一步到位

思想:

  1. 定义一个全局日志器
  2. 定义一个日志器参数结构体:日志输出等级、输出格式、日志器类型、输出目标
  3. 定义全局日志器初始化接口
/*
    对日志操作封装:
        1.防止头文件重复包含
        2.包含头文件
        3.声明命名空间
        4.声明全局日志器
        5.声明日志配置结构体
        6.声明全局日志器初始化接口
        7.封装日志输出宏
*/

#pragma once   

#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include "spdlog/spdlog.h"

namespace mylog {
    //声明全局日志器
    extern std::shared_ptr<spdlog::logger> g_logger;
    //声明日志配置结构体
    struct log_setting
    {
        bool async;         //是否异步日志
        int level;          //日志级别
        std::string path;         //日志文件路径
        std::string format;       //日志格式
    };
    //声明全局日志器初始化接口
    extern void mylog_init(const log_setting &setting);
    //封装日志输出宏
    #define DBG(fmt, ...) g_logger->debug("[{}:{}]: " + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define INF(fmt, ...) g_logger->info("[{}:{}]: " + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define WRN(fmt, ...) g_logger->warn("[{}:{}]: " + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define ERR(fmt, ...) g_logger->error("[{}:{}]: " + fmt, __FILE__, __LINE__, ##__VA_ARGS__)      

}

#include "mylog.h"

namespace mylog{
    std::shared_ptr<spdlog::logger> g_logger = nullptr;

    void mylog_init(const log_setting &setting){
        //判断类型 同步 or 异步
        if(setting.async)
        {
            if(setting.path == "stdout")
            {
                g_logger = spdlog::stdout_color_mt<spdlog::async_factory>("stdout_logger");
            }
            else
            {
                g_logger = spdlog::basic_logger_mt<spdlog::async_factory>("file_logger", setting.path);
            }
        }
        else
        {
            if(setting.path == "stdout")
            {
                g_logger = spdlog::stdout_color_mt("stdout_logger");
            }
            else
            {
                g_logger = spdlog::basic_logger_mt("file_logger", setting.path);
            }
        
        }
    
        //设置日志等级
        g_logger->set_level(static_cast<spdlog::level::level_enum>(setting.level));
        //设置日志格式
        g_logger->set_pattern(setting.format);
    }
}
posted @ 2026-01-16 15:11  gwj124  阅读(117)  评论(0)    收藏  举报