spdlog日志库嵌入式Linux C++使用指南

spdlog日志库嵌入式Linux C++使用指南

spdlog日志库嵌入式Linux C++使用指南

文档信息

项目内容
适配spdlog版本v1.12.0
适用场景ARM架构Linux系统、C++11及以上、嵌入式应用日志输出
前置依赖CMake 3.10+、C++11编译器、Linux glibc 2.28+

一、工具介绍

1.1 工具定位

spdlog是一款高性能、头文件优先的C++日志库,支持同步/异步日志输出,无第三方依赖,适配嵌入式Linux资源受限场景,可快速集成到C++项目中实现灵活的日志管理。

1.2 核心特性

  • 零外部依赖,纯头文件+源码集成,适配嵌入式交叉编译;
  • 支持TRACE/DEBUG/INFO/WARN/ERROR/CRITICAL 6级日志级别;
  • 多输出目标:控制台、文件、滚动文件(按大小/时间分割);
  • 线程安全设计,支持多线程环境下日志输出;
  • 自定义日志格式(时间、进程ID、线程ID、日志级别等);
  • 低内存占用,适配嵌入式闪存/内存受限场景。

1.3 对比优势(嵌入式场景)

对比项spdloglog4cpp
依赖依赖log4j、apr等库
编译体积小(仅需编译核心源码)大(依赖库编译后体积大)
内存占用低(支持按需加载)高(初始化加载全量组件)
嵌入式适配性优(头文件优先,支持交叉编译)差(依赖复杂,需手动移植)

二、快速集成

2.1 集成方式1:源码集成(嵌入式推荐)

步骤1:下载源码
git clone https://github.com/gabime/spdlog.git cd spdlog &&git checkout v1.12.0 # 固定版本,避免兼容性问题
步骤2:项目集成

将spdlog源码目录下的include/spdlog文件夹拷贝到项目的include路径下;
src/目录下的所有.cpp文件拷贝到项目的third_party/spdlog目录下(仅需编译一次)。

步骤3:CMakeLists.txt配置
# 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) 

2.2 集成方式2:包管理集成(桌面Linux)

# Ubuntu/Debian系统sudoapt update &&sudoaptinstall libspdlog-dev # CMakeLists.txt配置 find_package(spdlog REQUIRED) add_executable(your_project main.cpp) target_link_libraries(your_project spdlog::spdlog)

2.3 验证集成

测试代码(test_spdlog.cpp)
#include"spdlog/spdlog.h"intmain(){// 基础日志输出 spdlog::trace("这是TRACE级日志(最详细)"); spdlog::debug("这是DEBUG级日志(调试信息)"); spdlog::info("这是INFO级日志(普通信息)"); spdlog::warn("这是WARN级日志(警告信息)"); spdlog::error("这是ERROR级日志(错误信息)"); spdlog::critical("这是CRITICAL级日志(严重错误)");return0;}
编译运行
# 编译 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级日志(严重错误)

三、基础使用

3.1 日志级别控制

3.1.1 全局级别设置
#include"spdlog/spdlog.h"intmain(){// 设置全局日志级别为DEBUG(输出DEBUG及以上级别) spdlog::set_level(spdlog::level::debug); spdlog::debug("DEBUG日志将被输出"); spdlog::trace("TRACE日志被屏蔽");// 级别低于DEBUG,不输出return0;}
3.1.2 按logger实例设置级别
#include"spdlog/spdlog.h"intmain(){// 创建自定义loggerauto 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日志正常输出");return0;}

3.2 输出目标配置

3.2.1 控制台输出
// 普通控制台(无颜色)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日志");
3.2.2 文件输出
#include"spdlog/sinks/basic_file_sink.h"intmain(){// 基础文件输出(覆盖模式,每次运行清空文件)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("追加到文件末尾的日志");return0;}
3.2.3 滚动文件输出(嵌入式推荐)
#include"spdlog/sinks/rotating_file_sink.h"intmain(){// 按文件大小滚动:单个文件最大10MB,最多保留3个备份文件const size_t max_file_size =10*1024*1024;// 10MBconst 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分新建文件return0;}

3.3 日志格式自定义

3.3.1 全局格式设置
#include"spdlog/spdlog.h"intmain(){// 自定义格式:[时间] [进程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] 自定义格式的日志信息return0;}
3.3.2 格式占位符说明
占位符含义示例
%Y-%m-%d年-月-日2025-12-23
%H:%M:%S.%e时:分:秒.毫秒15:40:00.123
%P进程ID1234
%t线程ID5678
%l日志级别(小写)info、error
%L日志级别(大写)INFO、ERROR
%v日志内容自定义日志信息

四、高级特性

4.1 异步日志配置(嵌入式避坑)

4.1.1 基础异步配置
#include"spdlog/spdlog.h"#include"spdlog/async.h"#include"spdlog/sinks/basic_file_sink.h"intmain(){// 配置异步日志:队列大小8192,线程数1(嵌入式建议单线程) spdlog::init_thread_pool(8192,1);// 创建文件sinkauto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async.log",true);// 创建异步loggerauto 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();return0;}
4.1.2 嵌入式避坑点
  • 队列大小不宜过大(建议8192~16384),避免占用过多内存;
  • 异步溢出策略选择block(阻塞)而非overrun_oldest(覆盖旧日志),防止关键日志丢失;
  • 必须调用spdlog::shutdown()刷新队列,否则程序退出时未写入的日志会丢失。

4.2 多sink组合输出

#include"spdlog/spdlog.h"#include"spdlog/sinks/stdout_color_sinks.h"#include"spdlog/sinks/rotating_file_sink.h"intmain(){// 创建控制台sink和滚动文件sinkauto 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("同时输出到控制台和文件");return0;}

4.3 嵌入式串口输出(自定义sink)

#include"spdlog/spdlog.h"#include"spdlog/sinks/base_sink.h"#include<unistd.h>#include<fcntl.h>#include<termios.h>// 自定义串口sinkclassserial_sink:public spdlog::sinks::base_sink<std::mutex>{private:int serial_fd_;// 初始化串口(嵌入式需根据硬件调整参数)voidinit_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("串口打开失败");}structtermios options;tcgetattr(serial_fd_,&options);cfsetispeed(&options, B115200);// 波特率115200cfsetospeed(&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:voidsink_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());}voidflush_() override {tcdrain(serial_fd_);// 等待串口数据发送完成}public:explicitserial_sink(const std::string& port ="/dev/ttyS0"){init_serial(port);}~serial_sink() override {if(serial_fd_ >=0){close(serial_fd_);}}};// 使用自定义串口sinkintmain(){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("通过串口输出的日志信息");return0;}

五、避坑指南/常见问题

5.1 中文乱码问题

现象

日志中的中文显示为乱码(如????)。

解决方案
  • 编译时添加编码参数:-fexec-charset=UTF-8 -finput-charset=UTF-8
  • 确保嵌入式终端/串口工具的编码为UTF-8;
  • 自定义格式时避免使用非UTF-8字符集。
示例CMake配置
add_compile_options(-fexec-charset=UTF-8 -finput-charset=UTF-8) 

5.2 同步日志阻塞主线程

现象

高频率日志输出时,主线程卡顿(如嵌入式实时控制场景)。

解决方案
  • 切换为异步日志模式(参考4.1节);
  • 减少低级别日志输出(如关闭DEBUG/TRACE级);
  • 增大日志输出缓冲区(通过set_pattern添加缓冲配置)。

5.3 编译报错:nullptr未定义

错误信息
error: ‘nullptr’ was not declared in this scope 
解决方案
  • 编译时指定C++11及以上标准:-std=c++11
  • CMake中添加set(CMAKE_CXX_STANDARD 11)

5.4 日志文件占满闪存

现象

嵌入式设备闪存被日志文件占满,导致系统异常。

解决方案
  • 使用滚动文件日志(按大小/时间分割,参考3.2.3节);
  • 限制日志总大小(如最多保留3个10MB文件);
  • 定期清理过期日志(通过脚本或定时器)。

六、附录

6.1 常用API速查表

API功能描述
spdlog::set_level()设置全局日志级别
spdlog::set_pattern()设置全局日志格式
spdlog::get(logger_name)获取已注册的logger实例
spdlog::shutdown()刷新日志队列并释放资源
logger->set_level()设置单个logger的日志级别
logger->flush()手动刷新日志到输出目标

6.2 参考链接

  • spdlog官方文档:https://github.com/gabime/spdlog
  • spdlog嵌入式适配案例:https://github.com/gabime/spdlog/tree/v1.12.0/example
  • C++11线程安全指南:https://en.cppreference.com/w/cpp/thread

6.3 术语解释

  • Sink:日志输出目标(如控制台、文件、串口);
  • Logger:日志器实例,关联一个或多个Sink;
  • 异步日志:日志写入操作在独立线程中执行,不阻塞主线程;
  • 滚动日志:当日志文件达到指定大小/时间时,自动创建新文件并保留历史备份。

Read more

Python 数据科学秘籍(五)

原文:annas-archive.org/md5/a4f348a4e11e27ea41410c793e63daff 译者:飞龙 协议:CC BY-NC-SA 4.0 第九章:生长树 本章我们将涵盖以下食谱: * 从树到森林——随机森林 * 生长极度随机化的树 * 生长旋转森林 介绍 在本章中,我们将看到更多基于树的算法的袋装方法。由于它们对噪声的鲁棒性以及对各种问题的普适性,它们在数据科学社区中非常受欢迎。 大多数这些方法的名声在于它们相比其他方法能够在没有任何数据准备的情况下获得非常好的结果,而且它们可以作为黑盒工具交给软件工程师使用。 除了前文提到的过高的要求外,还有一些其他优点。 从设计上看,袋装法非常适合并行化。因此,这些方法可以轻松应用于集群环境中的大规模数据集。 决策树算法在树的每一层将输入数据划分为不同的区域。因此,它们执行了隐式的特征选择。特征选择是构建良好模型中的一个重要任务。通过提供隐式特征选择,决策树相较于其他技术处于有利位置。因此,带有决策树的袋装法具备这一优势。 决策树几乎不需要数据准备。例如,考虑属性的缩放。属性的缩放对决策

By Ne0inhk
Python 日志(logging)全解析

Python 日志(logging)全解析

文章目录 * 一、核心概念(四大组件) * 二、日志级别 * 三、基础使用 * 3.1 最简用法(默认配置) * 3.2 基本配置(logging.basicConfig) * 四、进阶配置(手动构建组件) * 4.1 手动配置流程 * 4.2 示例:多输出目标(控制台 + 文件) * 4.3 过滤器(Filter) * 五、日志轮转(解决日志文件过大问题) * 5.1 按大小轮转(RotatingFileHandler) * 5.2 按时间轮转(TimedRotatingFileHandler) * 六、异常日志记录 * 6.1 logging.exception

By Ne0inhk

重新创建python3.10环境,与先创建python3.8环境然后conda install python=3.10,二者不同

重新创建python3.10环境,与先创建python3.8环境然后conda install python=3.10,二者不同 今天安装mem0ai: pip install mem0ai 安装前没有查看要求的python版本,装了3.8,然后运行如下指令报错: from mem0 import MemoryClient 然后发现mem0ai要求python版本3.10以上,于是通过conda install python=3.10升级,出现了新的报错。 删除当前环境后重新创建python3.10环境,就可以正常运行了。 问了AI原来二者是有区别的: “在已有 Conda 环境中通过 conda install python=3.10 升级 Python 版本,会导致依赖包(尤其是含 C 扩展的包)与新 Python

By Ne0inhk
Python-flask的企业合同管理系统-Pycharm django

Python-flask的企业合同管理系统-Pycharm django

目录 * Python Flask 企业合同管理系统技术要点 * Django 企业合同管理系统技术对比 * Pycharm 开发优化技巧 * 技术选型建议 * 开发技术路线 * 源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式! Python Flask 企业合同管理系统技术要点 Flask 框架核心模块 * 使用 Flask-SQLAlchemy 进行数据库模型设计,定义合同、客户、用户等数据表。 * 通过 Flask-WTF 实现表单验证,确保合同录入数据的合法性。 * 采用 Flask-Login 管理用户认证和权限控制,区分管理员与普通用户角色。 关键功能实现 * 合同增删改查(CRUD)功能,结合分页插件(Flask-Paginate)优化数据展示。 * 文件上传模块,支持 PDF/Word 格式合同附件存储,使用 Flask-Uploads 扩展。 * 合同状态跟踪(如待签署、

By Ne0inhk