当前位置: 首页 > news >正文

rust - 一个日志缓存记录的通用实现

本文给出了一个通用的设计模式,通过建造者模式实例化记录对象,可自定义格式化器将实例化后的记录对象写入到指定的缓存对象中。

定义记录对象

use chrono::prelude::*;
use std::{cell::RefCell, ffi::OsStr, fmt, io, io::Write, path::Path, rc::Rc, str,time::SystemTime,
};const DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S";/// 将 SystemTime 格式的时间转换为指定格式的字符串
fn format_system_time(st: SystemTime) -> String {let local_datetime: DateTime<Local> = st.clone().into();local_datetime.format(DATETIME_FORMAT).to_string()
}/// 定义需要构造的协议
#[derive(Debug, Default, Clone)]
struct Record<'a> {event_time: Option<SystemTime>,var_a: Option<String>,var_b: Option<&'a Path>,var_c: Option<i32>,var_d: Option<&'a OsStr>,
}/// Record -> RecordBuilder
impl<'a> Record<'a> {/// Returns a new builder.#[inline]fn builder() -> RecordBuilder<'a> {RecordBuilder::new()}#[inline]fn event_time(&self) -> Option<SystemTime> {self.event_time}#[inline]fn var_a(&self) -> &Option<String> {&self.var_a}#[inline]fn var_b(&self) -> Option<&'a Path> {self.var_b}#[inline]fn var_c(&self) -> Option<i32> {self.var_c}#[inline]fn var_d(&self) -> Option<&'a OsStr> {self.var_d}
}

定义对象的建造者

用于根据需求创建不同的记录对象

/// 用于构造协议,通过 Record 和 RecordBuidler 将协议的读写分离
#[derive(Debug)]
struct RecordBuilder<'a> {record: Record<'a>,
}impl<'a> RecordBuilder<'a> {/// Construct new `RecordBuilder`.#[inline]fn new() -> RecordBuilder<'a> {RecordBuilder { record: Record::default() }}#[inline]fn event_time(&mut self,event_time: Option<SystemTime>,) -> &mut RecordBuilder<'a> {self.record.event_time = event_time;self}#[inline]fn var_a(&mut self, var_a: Option<String>) -> &mut RecordBuilder<'a> {self.record.var_a = var_a;self}#[inline]fn var_b(&mut self, var_b: Option<&'a Path>) -> &mut RecordBuilder<'a> {self.record.var_b = var_b;self}#[inline]fn var_c(&mut self, var_c: Option<i32>) -> &mut RecordBuilder<'a> {self.record.var_c = var_c;self}#[inline]fn var_d(&mut self, var_d: Option<&'a OsStr>) -> &mut RecordBuilder<'a> {self.record.var_d = var_d;self}/// Invoke the builder and return a `Record`#[inline]fn build(&mut self) -> Record<'a> {// todo 添加业务逻辑self.record.clone()}
}impl<'a> Default for RecordBuilder<'a> {fn default() -> Self {Self::new()}
}

定义写缓存对象

指定记录对象的写入缓存

/// 定义一个写缓存
#[derive(Debug)]
struct Buffer(Vec<u8>);impl Buffer {/// 初始化缓存fn new() -> Self {Self(vec![])}/// 清空缓存fn clear(&mut self) {self.0.clear();}/// 写缓存fn write(&mut self, buf: &[u8]) -> io::Result<usize> {self.0.extend(buf);Ok(buf.len())}/// 刷新缓存fn flush(&mut self) -> io::Result<()> {Ok(())}/// 获得缓存的内容fn bytes(&self) -> &[u8] {&self.0}
}impl Default for Buffer {fn default() -> Self {Self::new()}
}

定义用于格式化器的写缓存

不同的格式化器可以使用不同的缓存,这里使用上面定义的一个简单的数组缓存来实现格式化器需要的缓存。

/// 定义缓存内容的格式器
struct FormatterBuffer {buf: Rc<RefCell<Buffer>>, // RefCell可以修改buf,Rc可以避免使用作用域标识
}impl FormatterBuffer {fn new(buffer: Rc<RefCell<Buffer>>) -> Self {FormatterBuffer { buf: buffer }}fn clear(&mut self) {self.buf.borrow_mut().clear()}fn buf(&self) -> Rc<RefCell<Buffer>> {self.buf.clone()}
}
impl io::Write for FormatterBuffer {fn write(&mut self, buf: &[u8]) -> io::Result<usize> {self.buf.borrow_mut().write(buf)}fn flush(&mut self) -> io::Result<()> {self.buf.borrow_mut().flush()}
}impl fmt::Debug for FormatterBuffer {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {f.debug_struct("FormatterBuffer").finish()}
}

定义格式化器

不同的格式化器将记录转换为不同的格式,写入到缓存中。

#[derive(Debug)]
/// 格式化器
struct Format<'a> {buf: &'a mut FormatterBuffer, // 数据缓存sep: &'a str,                 // 分隔符
}impl<'a> Format<'a> {/// 写数据到缓存中fn write(mut self, record: &Record) -> io::Result<()> {let _ = self.write_event_time(record);let _ = self.write_var_a(record);let _ = self.write_var_b(record);let _ = self.write_var_c(record);let _ = self.write_var_d(record);Ok(())}fn write_event_time(&mut self, record: &Record) -> io::Result<()> {match record.event_time() {Some(event_time) => {let datetime_str = format_system_time(event_time);write!(self.buf, "{}{}", datetime_str, self.sep)}None => {write!(self.buf, "{}", self.sep)}}}fn write_var_a(&mut self, record: &Record) -> io::Result<()> {match record.var_a() {Some(var_a) => {write!(self.buf, "{}{}", var_a, self.sep)}None => write!(self.buf, "{}", self.sep),}}fn write_var_b(&mut self, record: &Record) -> io::Result<()> {match record.var_b() {Some(var_b) => {write!(self.buf,"{}{}",var_b.to_string_lossy(), // 操作系统对路径处理的差异性可能会丢失部分数据self.sep)}None => write!(self.buf, "{}", self.sep),}}fn write_var_c(&mut self, record: &Record) -> io::Result<()> {match record.var_c() {Some(var_c) => {write!(self.buf, "{}{}", var_c, self.sep)}None => write!(self.buf, "{}", self.sep),}}fn write_var_d(&mut self, record: &Record) -> io::Result<()> {match record.var_d() {Some(var_d) => {write!(self.buf,"{}{}",var_d.to_os_string().to_str().unwrap(), // 操作系统对路径处理的差异性可能会panicself.sep)}None => write!(self.buf, "{}", self.sep),}}
}

调用示例

fn main() {// 创建缓存let buffer = Rc::new(RefCell::new(Buffer::default()));let mut format_buffer = FormatterBuffer::new(buffer.clone());format_buffer.clear();// 创建一个格式化器let format = Format { buf: &mut format_buffer, sep: "|" };// 构造事件发生时间let no_timezone =NaiveDateTime::parse_from_str("2024-01-02 03:04:05", DATETIME_FORMAT).unwrap();let event_time = Local.from_local_datetime(&no_timezone).unwrap().into();// 构造路径let path = Path::new("./foo/bar.txt");let os_str = OsStr::new("1.png");// 构造记录let record = Record::builder().event_time(Some(event_time)).var_a(Some("hello world".to_string())).var_b(Some(path)).var_c(Some(999)).var_d(Some(os_str)).build();// 写记录到缓存let _ = format.write(&record);// 获得RefCell对象的内部值let ref_cell_inner_value = buffer.borrow();let actual = str::from_utf8(ref_cell_inner_value.bytes()).unwrap();let expect = "2024-01-02 03:04:05|hello world|./foo/bar.txt|999|1.png|";assert_eq!(actual, expect);
}

参考

https://github.com/rust-cli/env_logger

相关文章:

rust - 一个日志缓存记录的通用实现

本文给出了一个通用的设计模式&#xff0c;通过建造者模式实例化记录对象&#xff0c;可自定义格式化器将实例化后的记录对象写入到指定的缓存对象中。 定义记录对象 use chrono::prelude::*; use std::{cell::RefCell, ffi::OsStr, fmt, io, io::Write, path::Path, rc::Rc,…...

elasticsearch(RestHighLevelClient API操作)(黑马)

操作全是换汤不换药&#xff0c;创建一个request&#xff0c;然后使用client发送就可以了 一、增加索引库数据 Testvoid testAddDocument() throws IOException {//从数据库查出数据Writer writer writerService.getById(199);//将查出来的数据处理成json字符串String json …...

用尾插的思想实现移除链表中的元素

目录 一、介绍尾插 1.链表为空 2.链表不为空 二、题目介绍 三、思路 四、代码 五、代码解析 1. 2. 3. 4. 5. 6. 六、注意点 1. 2. 一、介绍尾插 整体思路为 1.链表为空 void SLPushBack(SLTNode** pphead, SLTDataType x) {SLTNode* newnode BuyLTNode(x); …...

【Kubernetes】k8s删除master节点后重新加入集群

目录 前言一、思路二、实战1.安装etcdctl指令2.重置旧节点的k8s3.旧节点的的 etcd 从 etcd 集群删除4.在 master03 上&#xff0c;创建存放证书目录5.把其他控制节点的证书拷贝到 master01 上6.把 master03 加入到集群7.验证 master03 是否加入到 k8s 集群&#xff0c;检查业务…...

HCIP—OSPF虚链路实验

OSPF虚链路—Vlink 作用&#xff1a;专门解决OSPF不规则区域所诞生的技术&#xff0c;是一种虚拟的&#xff0c;逻辑的链路。实现非骨干区域和骨干区域在逻辑上直接连接。注意虚链路条件&#xff1a;只能穿越一个区域&#xff0c;通常对虚链路进行认证功能的配置。虚链路认证也…...

RAxML-NG安装与使用-raxml-ng-v1.2.0(bioinfomatics tools-013)

01 背景 1.1 ML树 ML树&#xff0c;或最大似然树&#xff0c;是一种在进化生物学中用来推断物种之间进化关系的方法。最大似然&#xff08;Maximum Likelihood, ML&#xff09;是一种统计框架&#xff0c;用于估计模型参数&#xff0c;使得观察到的数据在该模型参数下的概率最…...

Tomcat内存马

Tomcat内存马 前言 描述Servlet3.0后允许动态注册组件 这一技术的实现有赖于官方对Servlet3.0的升级&#xff0c;Servlet在3.0版本之后能够支持动态注册组件。 而Tomcat直到7.x才支持Servlet3.0&#xff0c;因此通过动态添加恶意组件注入内存马的方式适合Tomcat7.x及以上。…...

pytorch之诗词生成3--utils

先上代码&#xff1a; import numpy as np import settingsdef generate_random_poetry(tokenizer, model, s):"""随机生成一首诗:param tokenizer: 分词器:param model: 用于生成古诗的模型:param s: 用于生成古诗的起始字符串&#xff0c;默认为空串:return: …...

OpenAI的ChatGPT企业版专注于安全性、可扩展性和定制化。

OpenAI的ChatGPT企业版&#xff1a;安全、可扩展性和定制化的重点 OpenAI的ChatGPT在商业世界引起了巨大反响&#xff0c;而最近推出的ChatGPT企业版更是证明了其在企业界的日益重要地位。企业版ChatGPT拥有企业级安全、无限GPT-4访问、更长的上下文窗口以及一系列定制选项等增…...

JS06-class对象

class对象 className 修改样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&quo…...

深度学习1650ti在win10安装pytorch复盘

深度学习1650ti在win10安装pytorch复盘 前言1. 安装anaconda2. 检查更新显卡驱动3. 根据pytorch选择CUDA版本4. 安装CUDA5. 安装cuDNN6. conda安装pytorch结语 前言 建议有条件的&#xff0c;可以在安装过程中&#xff0c;开启梯子。例如cuDNN安装时登录 or 注册&#xff0c;会…...

Node.js与webpack(三)

上一节&#xff1a;Node.js与Webpack笔记&#xff08;二&#xff09;-CSDN博客 从0来一遍&#xff08;webpack项目&#xff09; 将之前的webpack 的纯开发配置&#xff0c;重新创建空白项目&#xff0c;重新做一遍&#xff0c;捋一遍思路防止加入生产模式时候弄混 1.创建文件夹…...

测试覆盖率那些事

在测试过程中&#xff0c;会出现测试覆盖不全的情况&#xff0c;特别是工期紧张的情况下&#xff0c;测试的时间被项目的周期一压再压&#xff0c;测试覆盖概率不全就会伴随而来。 网上冲浪&#xff0c;了解一下覆盖率的文章&#xff0c;其中一篇感觉写的很不错&#xff0c;将…...

Etcd 介绍与使用(入门篇)

etcd 介绍 etcd 简介 etc &#xff08;基于 Go 语言实现&#xff09;在 Linux 系统中是配置文件目录名&#xff1b;etcd 就是配置服务&#xff1b; etcd 诞生于 CoreOS 公司&#xff0c;最初用于解决集群管理系统中 os 升级时的分布式并发控制、配置文件的存储与分发等问题。基…...

Docker 安装 LogStash

关于LogStash Logstash&#xff0c;作为Elastic Stack家族中的核心成员之一&#xff0c;是一个功能强大的开源数据收集引擎。它专长于从各种来源动态地获取、解析、转换和丰富数据&#xff0c;并将这些结构化或非结构化的数据高效地传输到诸如Elasticsearch等存储系统中进行集…...

Selenium笔记

Selenium笔记 Selenium笔记 Selenium笔记element not interactable页面刷新 element not interactable "element not interactable"是Selenium在执行与网页元素交互操作&#xff08;如点击、输入等&#xff09;时抛出的一个常见错误。这个错误意味着虽然找到了对应的…...

ChatGPT :确定性AI源自于确定性数据

ChatGPT 幻觉 大模型实际应用落地过程中&#xff0c;会遇到幻觉&#xff08;Hallucination&#xff09;问题。对于语言模型而言&#xff0c;当生成的文本语法正确流畅&#xff0c;但不遵循原文&#xff08;Faithfulness&#xff09;&#xff0c;或不符合事实&#xff08;Factua…...

linux驱动开发面试题

1.linux中内核空间及用户空间的区别&#xff1f; 记住“22”&#xff0c;两级分段两级权限。 例如是32位的机器&#xff0c;从内存空间看&#xff1a;顶层1G是内核的&#xff0c;底3G是应用的&#xff1b;从权限看&#xff1a;内核是0级特权&#xff0c;应用是3级特权。 2.用…...

【AI】Ubuntu系统深度学习框架的神经网络图绘制

一、Graphviz 在Ubuntu上安装Graphviz&#xff0c;可以使用命令行工具apt进行安装。 安装Graphviz的步骤相对简单。打开终端&#xff0c;输入以下命令更新软件包列表&#xff1a;sudo apt update。之后&#xff0c;使用命令sudo apt install graphviz来安装Graphviz软件包。为…...

AI推介-大语言模型LLMs论文速览(arXiv方向):2024.03.05-2024.03.10—(2)

论文目录~ 1.Debiasing Large Visual Language Models2.Harnessing Multi-Role Capabilities of Large Language Models for Open-Domain Question Answering3.Towards a Psychology of Machines: Large Language Models Predict Human Memory4.Can we obtain significant succ…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...