spdlog一个非常好用的C++日志库(九): 扩展自定义sink

目录

1.引言

2.自定义sink的步骤

3.示例代码

4.总结


1.引言

        在spdlog中,自定义sink是一个高级功能,它允许用户根据自己的需求来定义日志的输出方式。

        sink是spdlog中的一个核心概念,它代表日志数据的输出目的地。一个日志库可以对应多个sink,即日志数据可以被发送到多个输出流。

        sink负责接收日志库生成的日志数据,并将其输出到指定的目的地,如文件、控制台、远程服务器等。

        在实际的项目开发中,spdlog系统自带的sink不能满足项目要求,就需要自定义sink来满足项目的要求。

2.自定义sink的步骤

        首先看一下基类base_sink定义:

namespace spdlog {
namespace sinks {
template<typename Mutex>
class SPDLOG_API base_sink : public sink
{
public:base_sink();explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);~base_sink() override = default;base_sink(const base_sink &) = delete;base_sink(base_sink &&) = delete;base_sink &operator=(const base_sink &) = delete;base_sink &operator=(base_sink &&) = delete;void log(const details::log_msg &msg) final;void flush() final;void set_pattern(const std::string &pattern) final;void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;protected:// sink formatterstd::unique_ptr<spdlog::formatter> formatter_;Mutex mutex_;virtual void sink_it_(const details::log_msg &msg) = 0;virtual void flush_() = 0;virtual void set_pattern_(const std::string &pattern);virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
};
} // namespace sinks
} // namespace spdlog

1.继承base_sink类

  • 自定义sink需要继承spdlog中的base_sink类,并实现其中的虚函数。
  • 可以参考spdlog中已有的sink实现,如daily_file_sink、rotating_file_sink等。

2.实现关键函数

  • sink_it_:这是最重要的一个接口,用于处理日志数据的输出。在该函数中,你需要将日志数据格式化为指定的格式,并输出到目标位置。
  • 其他可能需要的函数包括:start(用于初始化sink)、flush(用于刷新缓冲区)、stop(用于停止sink)等。

3.添加自定义逻辑

  • 根据需求,在sink_it_函数中添加自定义的逻辑,如根据时间或文件大小来滚动日志文件、将日志数据发送到远程服务器等。

4.编译和测试

  • 编写完自定义sink后,需要将其编译并链接到spdlog库中。
  • 使用自定义sink进行日志记录,并测试其输出是否符合预期。

3.示例代码

        以下是一个简单的自定义sink示例,它结合了daily_file_sink和rotating_file_sink的特点,根据时间和文件大小来滚动日志文件:

        spdlog 有 daily_file_sink 每日创建一个 和 rotating_file_sink 根据大小翻滚,但是每个 sink 是独立处理的,没法根据时间和文件大小来同时作为生成条件,只好继承 base_sink 自定义。

        首先是文件名的拼接,参照 daily_file_sink 源码:

static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
{filename_t basename, ext;std::tie(basename, ext) = details::file_helper::split_by_extension(filename);return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
}

        使用自定义的函数拆分了文件路径和后缀 ,然后将日期格式化拼接到一起。

        然后是文件的创建和日志的写入,这部分可以放到 sink_it_ 虚函数,参照 daily_file_sink 源码:

void sink_it_(const details::log_msg &msg) override
{auto time = msg.time;bool should_rotate = time >= rotation_tp_;if (should_rotate){auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));file_helper_.open(filename, truncate_);rotation_tp_ = next_rotation_tp_();}memory_buf_t formatted;base_sink<Mutex>::formatter_->format(msg, formatted);file_helper_.write(formatted);// Do the cleaning only at the end because it might throw on failure.if (should_rotate && max_files_ > 0){delete_old_();}
}

        源码中先计算了下一个时间点 rotation_tp_,当满足条件就新建一个文件并重新计算时间点。显然,文件大小限制也可以塞到这个函数里。 

        自定义 sink 完整代码:

#pragma once#include <spdlog/common.h>
#include <spdlog/details/file_helper.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/fmt/fmt.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/os.h>
#include <spdlog/details/circular_q.h>
#include <spdlog/details/synchronous_factory.h>#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
#include <tuple>namespace spdlog {
namespace sinks {//根据大小和时间生成日志文件
//龚建波 2021-1-18
//参照rotating_file_sink和daily_file_sink自定义
//(生成的时间也可以修改为任意间隔)
//(还有个需求没完成,就是清理旧文件,按照个数或者时间,即max_files参数目前相关逻辑是没用的)
template<typename Mutex>
class easy_file_sink final : public base_sink<Mutex>
{
public:easy_file_sink(filename_t base_filename, size_t max_size, size_t max_files = 0): base_filename_(std::move(base_filename)), max_size_(max_size), max_files_(max_files), filenames_q_(){auto now = log_clock::now();auto filename = daily_filename_(base_filename_, now_tm(now));file_helper_.open(filename, false);//记录大小current_size_ = file_helper_.size();//记录新建时间,24hrotation_tp_ = next_rotation_tp_();if (max_files_ > 0){init_filenames_q_();}}filename_t filename(){std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);return file_helper_.filename();}protected:void sink_it_(const details::log_msg &msg) override{memory_buf_t formatted;base_sink<Mutex>::formatter_->format(msg, formatted);current_size_ += formatted.size();auto time = msg.time;//超过大小原文件改后缀,超过日期新文件改日期名bool should_rotate = false;if(time >= rotation_tp_){should_rotate = true;file_helper_.close();auto filename = daily_filename_(base_filename_, now_tm(time));file_helper_.open(filename, false);current_size_ = file_helper_.size();rotation_tp_ = next_rotation_tp_();}else if(current_size_ >= max_size_){should_rotate = true;file_helper_.close();auto src_name = daily_filename_(base_filename_, now_tm(time));auto target_name = calc_filename_(base_filename_, now_tm(time));//如果ranme失败就用targetname作为当前文件名if (!rename_file_(src_name, target_name)){details::os::sleep_for_millis(200);if (!rename_file_(src_name, target_name)){src_name = target_name;}}file_helper_.open(src_name, false);current_size_ = file_helper_.size();rotation_tp_ = next_rotation_tp_();}file_helper_.write(formatted);// Do the cleaning only at the end because it might throw on failure.if (should_rotate && max_files_ > 0){delete_old_();}}void flush_() override{file_helper_.flush();}private:void init_filenames_q_(){using details::os::path_exists;filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));std::vector<filename_t> filenames;auto now = log_clock::now();while (filenames.size() < max_files_){auto filename = calc_filename_(base_filename_, now_tm(now));if (!path_exists(filename)){break;}filenames.emplace_back(filename);now -= std::chrono::hours(24);}for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter){filenames_q_.push_back(std::move(*iter));}}tm now_tm(log_clock::time_point tp){time_t tnow = log_clock::to_time_t(tp);return spdlog::details::os::localtime(tnow);}log_clock::time_point next_rotation_tp_(){auto now = log_clock::now();tm date = now_tm(now);date.tm_hour = 0;date.tm_min = 0;date.tm_sec = 0;auto rotation_time = log_clock::from_time_t(std::mktime(&date));if (rotation_time > now){return rotation_time;}return {rotation_time + std::chrono::hours(24)};}// Delete the file N rotations ago.// Throw spdlog_ex on failure to delete the old file.void delete_old_(){using details::os::filename_to_str;using details::os::remove_if_exists;filename_t current_file = file_helper_.filename();if (filenames_q_.full()){auto old_filename = std::move(filenames_q_.front());filenames_q_.pop_front();bool ok = remove_if_exists(old_filename) == 0;if (!ok){filenames_q_.push_back(std::move(current_file));throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);}}filenames_q_.push_back(std::move(current_file));}//每日名称static filename_t daily_filename_(const filename_t &filename, const tm &now_tm){filename_t basename, ext;//split_by_extension拆分文件后缀std::tie(basename, ext) = details::file_helper::split_by_extension(filename);return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}_{:02d}_{:02d}{}"),basename,now_tm.tm_year + 1900,now_tm.tm_mon + 1,now_tm.tm_mday,ext);}//超过大小后重命名,后面加上时分秒static filename_t calc_filename_(const filename_t &filename, const tm &now_tm){filename_t basename, ext;std::tie(basename, ext) = details::file_helper::split_by_extension(filename);return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}_{:02d}_{:02d}_{:02d}{:02d}{:02d}{}"),basename,now_tm.tm_year + 1900,now_tm.tm_mon + 1,now_tm.tm_mday,now_tm.tm_hour,now_tm.tm_min,now_tm.tm_sec,ext);}static bool rename_file_(const filename_t &src_filename, const filename_t &target_filename){(void)details::os::remove(target_filename);return details::os::rename(src_filename, target_filename) == 0;}filename_t base_filename_;log_clock::time_point rotation_tp_;details::file_helper file_helper_;std::size_t max_size_;std::size_t max_files_;std::size_t current_size_;details::circular_q<filename_t> filenames_q_;
};using easy_file_sink_mt = easy_file_sink<std::mutex>;
using easy_file_sink_st = easy_file_sink<details::null_mutex>;
}template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> easy_logger_mt(const std::string &logger_name, const filename_t &filename, size_t max_size, size_t max_files = 0)
{return Factory::template create<sinks::easy_file_sink_mt>(logger_name, filename, max_size, max_files);
}template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> easy_logger_st(const std::string &logger_name, const filename_t &filename, size_t max_size, size_t max_files = 0)
{return Factory::template create<sinks::easy_file_sink_st>(logger_name, filename, max_size, max_files);
}
}

注意:上述代码仅为示例,可能需要根据实际需求进行调整和完善。特别是滚动文件的逻辑部分,需要根据具体的存储要求和文件命名规则来实现。

4.总结

        自定义spdlog sink是一个灵活且强大的功能,它允许用户根据自己的需求来定义日志的输出方式。通过继承base_sink类并实现其中的虚函数,用户可以轻松地添加自定义的逻辑来满足特定的日志记录需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/466286.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML前端页面设计静态网站-仿百度

浅浅分享一下前端作业&#xff0c;大佬轻喷~ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>百度&#xff08;伪&#xff09;</title><style>body {margin: 0;padding: 0;}.top-bar {dis…

Linux多线程(个人笔记)

Linux多线程 1.Linux线程概念1.1线程的优点1.2线程的缺点 2.Linux线程VS进程3.Linux线程控制3.1创建线程3.2线程tid及进程地址空间布局3.3线程终止3.4线程等待 4.分离线程5.线程互斥5.1互斥锁mutex5.2互斥锁接口5.3互斥锁实现原理5.4可重入VS线程安全 6.线程同步6.1条件变量6.2…

【MacOS实操】如何基于SSH连接远程linux服务器

MacOS上远程连接linux服务器&#xff0c;可以使用ssh命令pem秘钥文件连接。 一、准备pem秘钥文件 如果已经有pem文件&#xff0c;则跳过这一步。如果手上有ppk文件&#xff0c;那么需要先转换为pem文件。 macOS 的默认 SSH 客户端不支持 PPK 格式&#xff0c;你需要将 PPK 文…

基于CNN-LSTM的时间序列数据预测,15个输入1个输出,可以更改数据集,MATLAB代码

1. 数据收集与预处理 数据清洗&#xff1a;处理缺失值、异常值等。特征工程&#xff1a;提取有助于预测的特征。数据标准化&#xff1a;将时间序列数据标准化&#xff0c;使其具有零均值和单位方差&#xff0c;有助于模型训练。滑动窗口划分&#xff1a;将时间序列数据划分为多…

win 查看显卡支持 CUDA版本

在cmd 中执行 nvidia-smi 二、nvcc -V

Java算法OJ(6)归并分治

目录 1.前言 2.正文 2.1归并分治的概念 2.2计算数组的小和 2.2.1题目 2.2.2示例 2.2.3代码 2.3翻转对 2.3.1题目 2.3.2示例 2.3.3代码 3.小结 1.前言 哈喽大家好吖&#xff0c;今天继续来给大家带来Java算法——归并分治的讲解&#xff0c;学习这篇的前提可以先把…

QML项目实战:自定义Combox

目录 一.添加模块 import QtQuick.Controls 2.4 import QtQuick.Templates 2.4 as T import QtGraphicalEffects 1.15 import QtQuick 2.15 as T2 二.自定义Combox 1.combox文字显示 2.设置下拉图标显示 3.下拉框中选中背景设置 4.下拉框中选中文字设置 5.下拉框设置…

招聘系统哪个最好用?

在当今竞争激烈的商业环境中&#xff0c;企业为了保持竞争优势&#xff0c;对人才的需求愈发迫切。然而&#xff0c;面对海量的简历和繁杂的招聘流程&#xff0c;如何高效、精准地找到合适的人才&#xff0c;成为许多企业面临的难题。招聘系统因此应运而生&#xff0c;为企业提…

基于C语言实现的图书管理系统

使用Visual Studio 2022编译工具进行编写代码的。 项目源码直接奉上: book1.h头文件: #ifndef __BOOK1_H //预处理用于条件编译 避免头文件反复包含 #define __BOOK1_H#include<stdio.h> #include <string.h> #include<stdlib.h> #include<stdbool.h&g…

带你用Go实现二维码小游戏(下)

本篇文章我们进入项目最后的部署和监控搭建阶段&#xff0c;这一节会有很少的编码量&#xff0c;但是却能够带来最实用的知识和技术&#xff0c;快来阅读吧~ 5 Docker镜像打包部署 接下来就到了我们项目的部署阶段&#xff0c;优雅的项目必须要搭配优雅的部署方式&#xff01…

Sigrity Power SI 3D-EM Inductance Extraction模式如何进行电感的提取操作指导(一)

Sigrity Power SI 3D-EM Inductance Extraction模式如何进行电感的提取操作指导(一) Sigrity Power SI使用3D-EM Inductance Extraction模式可以进行电感的提取,以下图为例 2D 视图 <

shodan6-7---清风

shodan6-7 1.shodan网页版 以cve-2019-0708漏洞指纹特征为例 "\x03\x00\x00\x0b\x06\xd0\x00\x00\x124\x00"在这里插入图片描述 搜索命令参考 https://www.shodan.io/search/filters这个网页中有搜索关键词 对指定网址进行监控&#xff0c;这里可以对ip进行扫描&…

CPU算法分析LiteAIServer视频智能分析平台视频智能分析:抖动、过亮与过暗检测技术

随着科技的飞速发展&#xff0c;视频监控系统在各个领域的应用日益广泛。然而&#xff0c;视频质量的好坏直接影响到监控系统的效能&#xff0c;尤其是在复杂多变的光照条件下和高速数据传输中&#xff0c;视频画面常常出现抖动、过亮或过暗等问题&#xff0c;导致监控视频难以…

win11电脑无法找到声音输出设备怎么办?查看解决方法

电脑无法找到声音输出设备是一个常见的问题&#xff0c;尤其是在使用Windows操作系统时。幸运的是&#xff0c;大部分问题都可以通过以下几种方法来解决。 一、检查物理连接 在深入诊断之前&#xff0c;首先要检查硬件连接是否正常。这包括&#xff1a; 确保耳机、扬声器或其…

JS数据结构之“栈”、“队列”、“链表”

一、栈 JS中没有栈这种数据类型&#xff0c;创建栈其实是创建数组。push&#xff08;内容&#xff09;入栈&#xff1b;pop&#xff08;&#xff09;出栈&#xff1b; const stack []; stack.push(1); stack.push(2); const item1 stack.pop(); const item2 stack.pop(); …

【51单片机】串口通信原理 + 使用

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 串口硬件电路UART串口相关寄存器 编码单片机通过串口发送数据电脑通过串口发送数据控制LED灯 串口 串口是一种应用十分广泛的通讯接…

嵌入式web开发:boa、lighttpd

嵌入式web开发&#xff1a;boa、lighttpd https://blog.csdn.net/m0_37105371/category_10937068.html BOA服务器的移植-CSDN博客 【第1部分&#xff1a;boa服务器部署到ubuntu里】 http://www.boa.org/boa-0.94.13.tar.gz tar xvzf boa-0.94.13.tar.gz cd boa-0.94.13/src/ a…

RC高通滤波器Bode图分析(传递函数零极点)

RC高通滤波器 我们使得R1K&#xff0c;C1uF&#xff1b;电容C的阻抗为Xc&#xff1b; 传递函数 H ( s ) u o u i R X C R R 1 s C R s R C 1 s R C &#xff08;其中 s j ω &#xff09; H(s)\frac{u_{o} }{u_{i} } \frac{R }{X_{C}R} \frac{R }{\frac{1}{sC}R} \fra…

Python决策树、随机森林、朴素贝叶斯、KNN(K-最近邻居)分类分析银行拉新活动挖掘潜在贷款客户附数据代码

Python决策树、随机森林、朴素贝叶斯、KNN&#xff08;K-最近邻居&#xff09;分类分析银行拉新活动挖掘潜在贷款客户|附数据代码 最近我们被客户要求撰写关于银行拉新活动的研究报告&#xff0c;包括一些图形和统计输出。 项目背景&#xff1a;银行的主要盈利业务靠的是贷款&…

撰写开发信利器,高效工具助你赢在起点

ZohoCampaigns是电子邮件营销平台&#xff0c;提供创建、发送和分析邮件方案。其优势包括易用性、丰富模板、精准筛选、自动化和详细报告。外贸人员可用其高效发送开发信&#xff0c;追踪效果并优化策略&#xff0c;促进业务增长。 一、为什么选择Zoho Campaigns&#xff1f; …