spdlog 日志库嵌入式 Linux C++使用指南
spdlog 高性能 C++ 日志库在嵌入式 Linux 环境下的集成与使用方法。内容包括源码与包管理两种集成方式,详细讲解了日志级别控制、多输出目标(控制台、文件、滚动文件)配置及自定义格式设置。针对嵌入式场景,重点阐述了异步日志配置、多 sink 组合输出及串口自定义 sink 的实现,并提供了中文乱码、主线程阻塞、编译报错及闪存占满等常见问题的解决方案。适合 ARM 架构、C++11 及以上版本的嵌入式开发参考。

spdlog 高性能 C++ 日志库在嵌入式 Linux 环境下的集成与使用方法。内容包括源码与包管理两种集成方式,详细讲解了日志级别控制、多输出目标(控制台、文件、滚动文件)配置及自定义格式设置。针对嵌入式场景,重点阐述了异步日志配置、多 sink 组合输出及串口自定义 sink 的实现,并提供了中文乱码、主线程阻塞、编译报错及闪存占满等常见问题的解决方案。适合 ARM 架构、C++11 及以上版本的嵌入式开发参考。

| 项目 | 内容 |
|---|---|
| 适配 spdlog 版本 | v1.12.0 |
| 适用场景 | ARM 架构 Linux 系统、C++11 及以上、嵌入式应用日志输出 |
| 前置依赖 | CMake 3.10+、C++11 编译器、Linux glibc 2.28+ |
spdlog 是一款高性能、头文件优先的 C++ 日志库,支持同步/异步日志输出,无第三方依赖,适配嵌入式 Linux 资源受限场景,可快速集成到 C++ 项目中实现灵活的日志管理。
| 对比项 | spdlog | log4cpp |
|---|---|---|
| 依赖 | 无 | 依赖 log4j、apr 等库 |
| 编译体积 | 小(仅需编译核心源码) | 大(依赖库编译后体积大) |
| 内存占用 | 低(支持按需加载) | 高(初始化加载全量组件) |
| 嵌入式适配性 | 优(头文件优先,支持交叉编译) | 差(依赖复杂,需手动移植) |
git clone https://github.com/gabime/spdlog.git
cd spdlog && git checkout v1.12.0 # 固定版本,避免兼容性问题
将 spdlog 源码目录下的 include/spdlog 文件夹拷贝到项目的 include 路径下;
将 src/ 目录下的所有 .cpp 文件拷贝到项目的 third_party/spdlog 目录下(仅需编译一次)。
# 1. 添加头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 2. 添加 spdlog 源码编译
file(GLOB SPDLOG_SRC ${PROJECT_SOURCE_DIR}/third_party/spdlog/*.cpp)
add_library(spdlog STATIC ${SPDLOG_SRC})
# 3. 链接 spdlog 库到项目
add_executable(your_project main.cpp)
target_link_libraries(your_project spdlog pthread) # 依赖 pthread(线程安全)
# 4. 指定 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Ubuntu/Debian 系统
sudo apt update && sudo apt install libspdlog-dev
# CMakeLists.txt 配置
find_package(spdlog REQUIRED)
add_executable(your_project main.cpp)
target_link_libraries(your_project spdlog::spdlog)
#include "spdlog/spdlog.h"
int main(){
// 基础日志输出
spdlog::trace("这是 TRACE 级日志(最详细)");
spdlog::debug("这是 DEBUG 级日志(调试信息)");
spdlog::info("这是 INFO 级日志(普通信息)");
spdlog::warn("这是 WARN 级日志(警告信息)");
spdlog::error("这是 ERROR 级日志(错误信息)");
spdlog::critical("这是 CRITICAL 级日志(严重错误)");
return 0;
}
# 编译
g++ test_spdlog.cpp -o test -std=c++11 -lspdlog -pthread
# 运行(默认输出 INFO 及以上级别日志)
./test
# 输出结果示例:
# [2025-12-23 15:30:00.123] [info] 这是 INFO 级日志(普通信息)
# [2025-12-23 15:30:00.123] [warn] 这是 WARN 级日志(警告信息)
# [2025-12-23 15:30:00.123] [error] 这是 ERROR 级日志(错误信息)
# [2025-12-23 15:30:00.123] [critical] 这是 CRITICAL 级日志(严重错误)
#include "spdlog/spdlog.h"
int main(){
// 设置全局日志级别为 DEBUG(输出 DEBUG 及以上级别)
spdlog::set_level(spdlog::level::debug);
spdlog::debug("DEBUG 日志将被输出");
spdlog::trace("TRACE 日志被屏蔽"); // 级别低于 DEBUG,不输出
return 0;
}
#include "spdlog/spdlog.h"
int main(){
// 创建自定义 logger
auto logger = spdlog::get("my_logger");
if(!logger){
logger = spdlog::stdout_color_mt("my_logger"); // 彩色控制台输出
}
// 单独设置该 logger 的级别为 WARN
logger->set_level(spdlog::level::warn);
logger->info("INFO 日志被屏蔽");
logger->warn("WARN 日志正常输出");
return 0;
}
// 普通控制台(无颜色)
auto console_logger = spdlog::stdout_logger_mt("console");
// 彩色控制台(推荐,不同级别日志显示不同颜色)
auto color_console = spdlog::stdout_color_mt("color_console");
color_console->info("彩色 INFO 日志");
color_console->error("彩色 ERROR 日志");
#include "spdlog/sinks/basic_file_sink.h"
int main(){
// 基础文件输出(覆盖模式,每次运行清空文件)
auto file_logger = spdlog::basic_logger_mt("file_logger", "logs/app.log");
file_logger->info("写入文件的日志信息");
// 追加模式(保留历史日志)
auto append_file = spdlog::basic_logger_mt("append_file", "logs/app.log", true);
append_file->info("追加到文件末尾的日志");
return 0;
}
#include "spdlog/sinks/rotating_file_sink.h"
int main(){
// 按文件大小滚动:单个文件最大 10MB,最多保留 3 个备份文件
const size_t max_file_size = 10*1024*1024; // 10MB
const size_t max_files = 3;
auto rotating_logger = spdlog::rotating_logger_mt("rotating_logger", "logs/rotating.log", max_file_size, max_files);
// 按时间滚动(每天生成一个新文件)
#include "spdlog/sinks/daily_file_sink.h"
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.log", 0, 0); // 每天 0 点 0 分新建文件
return 0;
}
#include "spdlog/spdlog.h"
int main(){
// 自定义格式:[时间] [进程 ID:线程 ID] [日志级别] 日志内容
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%P:%t] [%l] %v");
spdlog::info("自定义格式的日志信息");
// 输出示例:[2025-12-23 15:40:00.123] [1234:5678] [info] 自定义格式的日志信息
return 0;
}
| 占位符 | 含义 | 示例 |
|---|---|---|
| %Y-%m-%d | 年 - 月 - 日 | 2025-12-23 |
| %H:%M:%S.%e | 时:分:秒。毫秒 | 15:40:00.123 |
| %P | 进程 ID | 1234 |
| %t | 线程 ID | 5678 |
| %l | 日志级别(小写) | info、error |
| %L | 日志级别(大写) | INFO、ERROR |
| %v | 日志内容 | 自定义日志信息 |
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
int main(){
// 配置异步日志:队列大小 8192,线程数 1(嵌入式建议单线程)
spdlog::init_thread_pool(8192, 1);
// 创建文件 sink
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async.log", true);
// 创建异步 logger
auto async_logger = std::make_shared<spdlog::async_logger>("async_logger", file_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::block);
// 注册并设置为默认 logger
spdlog::register_logger(async_logger);
spdlog::set_default_logger(async_logger);
// 异步输出日志(不阻塞主线程)
for(int i = 0; i < 1000; ++i){
spdlog::info("异步日志输出:{}", i);
}
// 程序退出前刷新日志(避免丢失)
spdlog::shutdown();
return 0;
}
block(阻塞)而非 overrun_oldest(覆盖旧日志),防止关键日志丢失;spdlog::shutdown() 刷新队列,否则程序退出时未写入的日志会丢失。#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
int main(){
// 创建控制台 sink 和滚动文件 sink
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/multi_sink.log", 10*1024*1024, 3);
// 组合 sink 到同一个 logger
std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};
auto multi_sink_logger = std::make_shared<spdlog::logger>("multi_sink", sinks.begin(), sinks.end());
// 注册并使用
spdlog::register_logger(multi_sink_logger);
multi_sink_logger->info("同时输出到控制台和文件");
return 0;
}
#include "spdlog/spdlog.h"
#include "spdlog/sinks/base_sink.h"
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
// 自定义串口 sink
class serial_sink : public spdlog::sinks::base_sink<std::mutex>{
private:
int serial_fd_; // 初始化串口(嵌入式需根据硬件调整参数)
void init_serial(const std::string& port){
serial_fd_ = open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if(serial_fd_ < 0){
throw std::runtime_error("串口打开失败");
}
struct termios options;
tcgetattr(serial_fd_, &options);
cfsetispeed(&options, B115200); // 波特率 115200
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD); // 本地连接、启用接收
options.c_cflag &= ~PARENB; // 无校验位
options.c_cflag &= ~CSTOPB; // 1 位停止位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8 位数据位
tcsetattr(serial_fd_, TCSANOW, &options);
}
protected:
void sink_it_(const spdlog::details::log_msg& msg) override {
// 格式化日志内容
spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<std::mutex>::formatter_->format(msg, formatted);
// 写入串口
write(serial_fd_, formatted.data(), formatted.size());
}
void flush_() override {
tcdrain(serial_fd_); // 等待串口数据发送完成
}
public:
explicit serial_sink(const std::string& port = "/dev/ttyS0"){
init_serial(port);
}
~serial_sink() override {
if(serial_fd_ >= 0){
close(serial_fd_);
}
}
};
// 使用自定义串口 sink
int main(){
auto serial_sink_ptr = std::make_shared<serial_sink>();
auto serial_logger = std::make_shared<spdlog::logger>("serial_logger", serial_sink_ptr);
spdlog::register_logger(serial_logger);
serial_logger->info("通过串口输出的日志信息");
return 0;
}
日志中的中文显示为乱码(如 ????)。
-fexec-charset=UTF-8 -finput-charset=UTF-8;add_compile_options(-fexec-charset=UTF-8 -finput-charset=UTF-8)
高频率日志输出时,主线程卡顿(如嵌入式实时控制场景)。
set_pattern 添加缓冲配置)。error: 'nullptr' was not declared in this scope
-std=c++11;set(CMAKE_CXX_STANDARD 11)。嵌入式设备闪存被日志文件占满,导致系统异常。
| API | 功能描述 |
|---|---|
| spdlog::set_level() | 设置全局日志级别 |
| spdlog::set_pattern() | 设置全局日志格式 |
| spdlog::get(logger_name) | 获取已注册的 logger 实例 |
| spdlog::shutdown() | 刷新日志队列并释放资源 |
| logger->set_level() | 设置单个 logger 的日志级别 |
| logger->flush() | 手动刷新日志到输出目标 |

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online