简单好用的C++日志库spdlog使用示例
文章目录
- 前言
- 一、spdlog的日志风格
- fmt风格
- printf风格
- 二、日志格式pattern
- 三、sink,多端写入
- 四、异步写入
- 五、注意事项
- 六、自己封装了的代码
- usespdlog.h
- 封装代码解释
- 使用示例
前言
C++日志库有很多,glog,log4cpp,easylogging++, easylogger, plog,spdlog等等。每个都大致了解过,看过github代码,看下来还是觉得spdlog用起来最方便最简单,head-only。别看easylogging++只有2个文件,但是可读性有点差, plog虽然也比较简单,但是没有异步写日志。
spdlog地址:https://github.com/gabime/spdlog
一、spdlog的日志风格
fmt风格
与printf风格不同,spdlog采用的是fmt格式,也就是类似于:
fmt::print("我是{} 我有{}万",小明,1000)
fmt风格比较安全,多一个参数少一个参数都会抛异常,printf是可能会直接内存错误的。
printf风格
虽然spdlog本身只支持fmt风格,但是我们可以自己按printf风格解析然后按无参的形式写入spdlog。
二、日志格式pattern
通过pattern我们可以控制日志的格式,例如以下是一个常用格式:
%^[%Y-%m-%d %H:%M:%S.%e] [%-8l] [%s:%#] %v%$
[2024-06-18 16:27:08.778] [warning ] [Config.cpp:50] 这是一条日志消息
pattern支持日期时间、日志等级显示、代码行数、控制台颜色等常用格式,详细的可以看它github介绍,挺清晰的。上面这个pattern就是时间精确到毫秒,日志等级字段占8个字符左对齐,显示源文件和代码行数,在控制台中按照日志等级显示颜色。
三、sink,多端写入
sink在其它库里面可能叫appender,就是日志怎么写入终端,写到哪个终端。比如一条日志消息可以同时写入控制台、文件、tcp等等。一般常用的就是写到控制台和文件,文件按日期保存或者按log1.txt,log2.txt切割。
四、异步写入
spdlog中有普通的logger和async_logger,使用异步logger时需要事先初始化一个线程池。
五、注意事项
- 普通的logger就是阻塞主线程的,可以使用async_logger异步写日志。
- 如果想要打印出源码文件名和序号,必须是用宏才可以,spdlog自带的宏是SPDLOG
- 如果用spdlog自带的宏打印,注意它有一个全局日志等级宏SPDLOG_ACTIVE_LEVEL,默认应该是info等级,可以在cmake中加一句话开启所有等级:
add_compile_definitions(SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE)
六、自己封装了的代码
usespdlog.h
#pragma once
#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h"
#include "spdlog/fmt/bundled/printf.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/async.h"
//是否使用fmt格式
//#define USE_FMT_LOG
class useSpdlog {
private:useSpdlog() = default;useSpdlog(const useSpdlog&) = delete;useSpdlog& operator=(const useSpdlog&) = delete;
public:static useSpdlog* config() {static useSpdlog a;return &a;}std::string global_debug_logger_name = "global_debug_logger";std::string global_info_logger_name = "global_info_logger";std::string global_error_logger_name = "global_error_logger";std::string global_debug_logger_path = "cvlogs/vm_global_debug.log";std::string global_info_logger_path = "cvlogs/vm_global_info.log";std::string global_error_logger_path = "cvlogs/vm_global_error.log";std::string default_pattern = "%^[%Y-%m-%d %H:%M:%S.%e] [%-8l] [%s:%#] %v%$";static std::shared_ptr<spdlog::logger> createAsyncRotatingFileLogger(spdlog::level::level_enum level, const std::string& logName = "spdlog", const std::string& logPath = "logs/spdlog.log", bool enableConsole = true, bool enableFile = true, const std::string& pattern = useSpdlog::config()->default_pattern){assert(enableConsole || enableFile);auto p_logger = std::make_shared<spdlog::async_logger>(spdlog::async_logger(logName, {}, spdlog::thread_pool(), spdlog::async_overflow_policy::block));//auto p_logger = std::make_shared<spdlog::logger>(spdlog::logger(logName, {}));if (enableConsole){auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();p_logger->sinks().push_back(console_sink);}if (enableFile){auto file_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>(logPath, 0, 0, false, 15);p_logger->sinks().push_back(file_sink);}p_logger->set_level(level);p_logger->set_pattern(pattern);return p_logger;}static void initSpdlog(spdlog::level::level_enum level, bool enableconsole = true, bool enableFile = true){//初始化线程池spdlog::init_thread_pool(100, 4);//专门记录trace和debugauto g_debug_logger = createAsyncRotatingFileLogger(spdlog::level::trace, useSpdlog::config()->global_debug_logger_name, useSpdlog::config()->global_debug_logger_path, enableconsole, enableFile);//专门记录infoauto g_info_logger = createAsyncRotatingFileLogger(spdlog::level::info, useSpdlog::config()->global_info_logger_name, useSpdlog::config()->global_info_logger_path, enableconsole, enableFile, "%^[%Y-%m-%d %H:%M:%S.%e] [%-8l] %v%$");//专门记录 warn error cirtialauto g_error_logger = createAsyncRotatingFileLogger(spdlog::level::warn, useSpdlog::config()->global_error_logger_name, useSpdlog::config()->global_error_logger_path, enableconsole, enableFile);spdlog::register_logger(g_debug_logger);spdlog::register_logger(g_info_logger);spdlog::register_logger(g_error_logger);//每三秒刷新一下文件spdlog::flush_every(std::chrono::seconds(3));//设置全局日志等级spdlog::set_level(level);}static void register_AsyncRotatingFileLogger(spdlog::level::level_enum level, const std::string& logName, const std::string& logPath, bool enableConsole = true, bool enableFile = true, const std::string& pattern = useSpdlog::config()->default_pattern){auto p_logger = createAsyncRotatingFileLogger(level, logName, logPath, enableConsole, enableFile, pattern);spdlog::register_logger(p_logger);}static void spd_printf_log(std::shared_ptr<spdlog::logger> logger, spdlog::level::level_enum level, char* file, int line, char* func, char* format, ...){va_list args;va_start(args, format);char buff[1024];vsnprintf(buff, sizeof(buff), format, args);va_end(args);std::string msg = buff;logger->log(spdlog::source_loc{file, line, func}, level, msg);}static void set_level(spdlog::level::level_enum level){spdlog::set_level(level);}
};
//printf格式
#define MODULE_LOG_TRACE_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::trace, __FILE__,__LINE__,__FUNCTION__, __VA_ARGS__)
#define MODULE_LOG_DEBUG_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::debug, __FILE__,__LINE__,__FUNCTION__, __VA_ARGS__)
#define MODULE_LOG_INFO_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::info, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)
#define MODULE_LOG_WARN_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::warn, __FILE__, __LINE__,__FUNCTION__, __VA_ARGS__)
#define MODULE_LOG_ERROR_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::err, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)
#define MODULE_LOG_CRITICAL_PRINTF(name,...) useSpdlog::spd_printf_log(spdlog::get(name),spdlog::level::level_enum::critical, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)//fmt格式
#define MODULE_LOG_TRACE_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::trace, __VA_ARGS__)
#define MODULE_LOG_DEBUG_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::debug, __VA_ARGS__)
#define MODULE_LOG_INFO_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::info, __VA_ARGS__)
#define MODULE_LOG_WARN_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::warn, __VA_ARGS__)
#define MODULE_LOG_ERROR_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::err, __VA_ARGS__)
#define MODULE_LOG_CRITICAL_FMT(name,...) (spdlog::get(name))->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, spdlog::level::level_enum::critical, __VA_ARGS__)//fmt和printf格式选择
#ifndef USE_FMT_LOG
#define MODULE_LOG_TRACE(name,...) MODULE_LOG_TRACE_PRINTF(name, __VA_ARGS__)
#define MODULE_LOG_DEBUG(name,...) MODULE_LOG_DEBUG_PRINTF(name, __VA_ARGS__)
#define MODULE_LOG_INFO(name,...) MODULE_LOG_INFO_PRINTF(name, __VA_ARGS__)
#define MODULE_LOG_WARN(name,...) MODULE_LOG_WARN_PRINTF(name, __VA_ARGS__)
#define MODULE_LOG_ERROR(name,...) MODULE_LOG_ERROR_PRINTF(name, __VA_ARGS__)
#define MODULE_LOG_CRITICAL(name,...) MODULE_LOG_CRITICAL_PRINTF(name, __VA_ARGS__)
#else
#define MODULE_LOG_TRACE(name,...) MODULE_LOG_TRACE_FMT(name, __VA_ARGS__)
#define MODULE_LOG_DEBUG(name,...) MODULE_LOG_DEBUG_FMT(name, __VA_ARGS__)
#define MODULE_LOG_INFO(name,...) MODULE_LOG_INFO_FMT(name, __VA_ARGS__)
#define MODULE_LOG_WARN(name,...) MODULE_LOG_WARN_FMT(name, __VA_ARGS__)
#define MODULE_LOG_ERROR(name,...) MODULE_LOG_ERROR_FMT(name, __VA_ARGS__)
#define MODULE_LOG_CRITICAL(name,...) MODULE_LOG_CRITICAL_FMT(name, __VA_ARGS__)
#endif#define LOG_TRACE(...) MODULE_LOG_TRACE(useSpdlog::config()->global_debug_logger_name, __VA_ARGS__)
#define LOG_DEBUG(...) MODULE_LOG_DEBUG(useSpdlog::config()->global_debug_logger_name, __VA_ARGS__)
#define LOG_INFO(...) MODULE_LOG_INFO(useSpdlog::config()->global_info_logger_name, __VA_ARGS__)
#define LOG_WARN(...) MODULE_LOG_WARN(useSpdlog::config()->global_error_logger_name, __VA_ARGS__)
#define LOG_ERROR(...) MODULE_LOG_ERROR(useSpdlog::config()->global_error_logger_name, __VA_ARGS__)
#define LOG_CRITICAL(...) MODULE_LOG_CRITICAL(useSpdlog::config()->global_error_logger_name, __VA_ARGS__)
封装代码解释
- 我们用一个单例类usespdlog封装,这么做是为了能在头文件中方便的定义一些参数变量。
- initSpdlog初始化全局日志器,我们主要记录debug,info,error三种日志,将trace、debug合并,warn、error、critial合并。可以选择控制台显示或者记录到文件。文件是按日期保存,保存15天的日志,之后会回滚,删除最早的一个。
- createAsyncRotatingFileLogger 创建一个可以同时显示到控制台或者记录到文件的日志创建器,注册到spdlog,就可以用名字全局使用了。
- 自己定义了一些宏,不再使用spdlog自带的宏,通过USE_FMT_LOG开关可以选择使用FMT或printf风格写日志。不带MODUILE前缀的LOG_INFO等使用定义的全局日志记录器写日志,带MODULE前缀的MODULE_LOG_INFO等通过指定日至器写入日志。
使用示例
//初始化全局日志器
useSpdlog::initSpdlog(spdlog::level::level_enum::debug);
//创建一个只在控制台显示的日志器
auto console_debug = useSpdlog::createAsyncRotatingFileLogger(spdlog::level::level_enum::debug, "console_debug", "", true, false, "[console debug] %v");spdlog::register_logger(console_debug);
LOG_INFO("123 %.2f",3.0);
MODULE_LOG_INFO("console_debug", "123 %s","33333");
相关文章:

简单好用的C++日志库spdlog使用示例
文章目录 前言一、spdlog的日志风格fmt风格printf风格 二、日志格式pattern三、sink,多端写入四、异步写入五、注意事项六、自己封装了的代码usespdlog.h封装代码解释使用示例 前言 C日志库有很多,glog,log4cpp,easylogging, eas…...
python 方法运行计时装饰模式实现
在代码开发过程中,需要记录方法的执行时间,每个方法都硬代码也可以实现,但是不是最好的方式,考虑到设计模式和模版代码,通过装饰模式实现方法运行计时 在Python中,装饰器可以接受参数,这样可以…...

【权威出版/投稿优惠】2024年水利水电与能源环境科学国际会议(WRHEES 2024)
2024 International Conference on Water Resources, Hydropower, Energy and Environmental Science 2024年水利水电与能源环境科学国际会议 【会议信息】 会议简称:WRHEES 2024 大会时间:点击查看 截稿时间:点击查看 大会地点:…...

阿赵UE引擎C++编程学习笔记——场景加载和切换
大家好,我是阿赵。 继续学习UE引擎,这次来学习一下切换和加载场景的各种做法。 一、 蓝图实现 1、 切换关卡 所谓切换关卡,就是从当前关卡进入到一个新的关卡, 旧关卡的数据将会被放弃。进入新的关卡后,将会执行…...

【LLM之RAG】RAFT论文阅读笔记
研究背景 论文针对的主要问题是如何将预训练的大型语言模型(LLMs)适应特定领域的检索增强生成(RAG)。这些模型通常在广泛的文本数据上进行预训练,已经表现出在广义知识推理任务上的优越性能。然而,在特定领…...

【Android】使用Binder(AIDL)实现利用自定义Bean进行的进程间通信(二)
项目前置 这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章,感兴趣的可以看一下: Binder(一)Binder的介绍和AIDL使用Binder的实例 项目目标 在两个APP之间进行数据传递,使用Android推荐的Binder通讯&#…...
HTTP中get与post的区别?在传输数据类型上有什么区别?【面试】
HTTP中的GET和POST是两种最常见的请求方法,它们在数据传输和使用场景上有一些关键的区别: GET请求: 数据传输方式:GET请求将数据附加在URL之后,形成查询字符串(namevalue的形式),数…...

「51媒体-年中大促」天津有哪些媒体资源-媒体宣传服务公司
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 天津的媒体资源相当丰富,涵盖了报纸、电视、广播、新闻门户网站、央媒驻天津机构、视频媒体以及全国媒体资源等多个方面。以下是详细的媒体资源分类和具体信息: 一…...

Thinkphp校园新闻发布系统源码 毕业设计项目实例
Thinkphp校园新闻发布系统源码 毕业设计项目实例 校园新闻发布系统模块: 用户模块:注册,登陆,查看个人信息,修改个人信息,站内搜索,新闻浏览等功能, 后台管理员模块:会员…...

前端老古董execCommand——操作 选中文本 样式
文章目录 ⭐前言⭐exe command api用法💖 example示例💖 测试效果 ⭐execommand和getSelection 的联系⭐总结⭐结束 ⭐前言 大家好,我是yma16,本文分享关于 前端老古董execCommand——操作选中文本。 execommand 当一个 HTML 文…...
elementui写一个自定义的rangeInput的组件
组件定义 使用el-row确保元素都在一行上对外暴露的prop是minValue和maxValue,但是不建议直接使用,使用计算属性minValueComputed和maxValueComputed更改计算属性的值的不要直接更改计算属性,也不要直接更改原本的prop,通知外层的父…...

护眼灯哪些牌子好?一文刨析护眼灯怎么选择!
护眼灯哪些牌子好?护眼台灯作为对抗视力挑战的一种方法,逐渐赢得了众多家长的青睐。这些台灯利用尖端光学技术,发出柔和且无刺激的照明,有助于保护眼睛不受伤害。它们不但可以调节亮度和色温,打造一个舒适且自然的阅读…...

抖音短剧看剧系统是怎么做的?怎么样搭建上线运营?
前言: 当前热门短剧已深入大家的日常,针对一些好的短剧更是吸金无数。今天给大家介绍一下短剧这个项目整个运作模式。 一、一部短剧是怎么样呈现到观众眼前的? 首先影视作品公司拍摄剪辑好短剧 ,弄好一切审核后,放到…...
2024.06.06校招 实习 内推 面经
绿*泡*泡VX: neituijunsir 交流*裙 ,内推/实习/校招汇总表格 1、校招 | 追觅科技2025届校园招聘/正式启动! 校招 | 追觅科技2025届校园招聘正式启动! 2、校招&实习&社招 | 博世海外招聘—德国/专场正式启动࿰…...

神经网络模型---ResNet
一、ResNet 1.导入包 import tensorflow as tf from tensorflow.keras import layers, models, datasets, optimizersoptimizers是用于更新模型参数以最小化损失函数的算法 2.加载数据集、归一化、转为独热编码的内容一致 3.增加颜色通道 train_images train_images[...,…...

Linux之网络编程
Linux之网络编程 TCP协议 TCP(Transmission ControlProtocol) : 传输控制协议,是一个 面向连接的、可靠的、基于字节流的传输层的协议。TCP 协议建立的是一种点到点的,一对一的可靠连接协议 特点: 数据无丢失数据无失序数据无错误数据无重…...
opencascade AIS_InteractiveContext源码学习1
AIS_InteractiveContext 前言 交互上下文(Interactive Context)允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是,对于已经被交互上下文识别的交互对象,必须使用上下文方法进行…...

TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法
TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法 这个错误信息是由于缺少设备镜像无法下载到操作面板而导致的。 当使用 TIA V15.1 Update 4 和 Update 5 组态 TP1000F Mobile 时,请遵守特别注意事项。 问题 在编译一个…...

【MySQL】聊聊数据库是如何保证数据不丢的
对于一个存储系统来说,其中比较关键的核心组件包含,网络、存储模型、持久化、数据结构等。而数据如何保证不丢失,对于不同的存储系统来说,比如Redis采用AOF和RDB的方式进行混合使用,而MySQL采用日志进行保证。也就是re…...

GitLab教程(四):分支(branch)和合并(merge)
文章目录 1.分支(branch)(1)分支的概念(2)branch命令 2.合并(merge)(1)三个命令pullfetchmergegit fetchgit mergegit pull (2)合并冲…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...