简单好用的C++日志库spdlog使用示例

2024-06-19 13:36

本文主要是介绍简单好用的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时需要事先初始化一个线程池。

五、注意事项

  1. 普通的logger就是阻塞主线程的,可以使用async_logger异步写日志。
  2. 如果想要打印出源码文件名和序号,必须是用宏才可以,spdlog自带的宏是SPDLOG
  3. 如果用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__)

封装代码解释

  1. 我们用一个单例类usespdlog封装,这么做是为了能在头文件中方便的定义一些参数变量。
  2. initSpdlog初始化全局日志器,我们主要记录debug,info,error三种日志,将trace、debug合并,warn、error、critial合并。可以选择控制台显示或者记录到文件。文件是按日期保存,保存15天的日志,之后会回滚,删除最早的一个。
  3. createAsyncRotatingFileLogger 创建一个可以同时显示到控制台或者记录到文件的日志创建器,注册到spdlog,就可以用名字全局使用了。
  4. 自己定义了一些宏,不再使用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使用示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075179

相关文章

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

解决Maven项目idea找不到本地仓库jar包问题以及使用mvn install:install-file

《解决Maven项目idea找不到本地仓库jar包问题以及使用mvninstall:install-file》:本文主要介绍解决Maven项目idea找不到本地仓库jar包问题以及使用mvnin... 目录Maven项目idea找不到本地仓库jar包以及使用mvn install:install-file基

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚