简单好用的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

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499