当前位置: 首页 > 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…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...