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
标准日志输出
- 包含头文件
\#include "spdlog/spdlog.h" - 创建日志对象
std::shared_ptr<logger> stdout_color_mt( const std::string &logger_name, color_mode mode = color_mode::automatic);
- 设置日志输出等级:
void set_level(level::level_enum log_level); - 设置日志输出格式:
void set_pattern(std::string pattern, pattern_time_type time_type); - 输出日志
#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.声明命名空间
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);
}
}

浙公网安备 33010602011771号