【Rust.Crate之tracing 诊断系统】

server/2024/11/14 6:51:43/

Rust.Crate之tracing 诊断系统

  • 前言
  • diagnostics system
  • tracing
  • 标签 #[tracing::instrument]
  • 异步函数中span
  • Instrument中的?
  • tracing_subscriber
  • tracing 的原生Subscribers
  • 总结

前言

日志对于每一个开发者来说并不陌生,开发者通常希望在程序代码中的一些需要做记录的地方加上日志,亦或在调试期间加的打印,随着需求的展开和经验的不断丰富,单纯的日志往往无法让开发者第一时间定位到问题,一般的,日志记录都是文本格式,且针对特定的数据并没有一个舒适的格式便于人类阅读,即便有时间戳等的信息,没有上下文的记录,开发者依然需要还原现场才能够排查到具体的问题原因,这对于快速迭代的日常开发和服务器线上问题的解决并不友好。

diagnostics system

诊断系统是目前主流的信息记录手段,通常情况下,使用诊断信息可以做到log日志能够做的一切,并再此基础上,诊断系统可以记录程序运行期间的主要问题场景,从begining 到ending,整个时间跨度都可以记录下来,这对日常问题排查就很有友好了,加之诊断系统一般可轻松的将信息格式化的存储落盘,尤其是在服务器的请求响应模式下,每一条的请求都将格式化记录,这也适合第三方软件对日志关键内容的提取变得十分方便。

tracing

tracing 是Rust crate。

A scoped, structured logging and diagnostics system.
tracing是一个范围明确的结构化日志的诊断系统。

先来看一段代码:

rust">use std::io;
use tracing::*;
use tracing_subscriber;
fn test_trace(n: i32) {event!(Level::INFO, "something happened");let span = span!(Level::TRACE, "my_span");let _guard = span.enter();trace!(answer = n, "trace1: test_trace");event!(Level::INFO, "this is a test_trace ");inner_trace();
}fn inner_trace() {trace!("trace2: inner_trace");event!(Level::INFO, "this is a inner_trace ");
}fn main() {tracing_subscriber::fmt().with_max_level(Level::TRACE).with_writer(io::stdout).init();test_trace(33);
}

对上述代码进行分解:

rust">let span = span!(Level::TRACE, "my_span");let _guard = span.enter();

span指一个跨度,即在其enter的作用域内,在enter被析构前,所有的下游跨度都会被当作当前跨度的子跨度,编译器跟踪他的生命周期,在他离开作用域时候析构。
什么是他的下游跨度?
依上述代码中:

rust">fn test_trace(n: i32) {let span = span!(Level::TRACE, "my_span");let _guard = span.enter();trace!(answer = n, "trace1: test_trace");inner_trace();//<--- 这就是子跨度,在输出的信息中也可以证实
}

程序运行输出的信息:

rust">2024-11-08T07:00:41.891102Z TRACE my_span: log: trace1: test_trace answer=33
2024-11-08T07:00:41.891346Z TRACE my_span: log: trace2: inner_trace

标签 #[tracing::instrument]

此标签可以直接向函数添加跨度,在每次调用此函数时都会创建带有其名称的跨度,从而实现更好的追踪。

代码示例:

rust">use std::io;
use tracing::*;
use tracing_subscriber;
#[tracing::instrument]
fn test_span() {error!("error_my_instrument");let _ = std::time::Duration::from_secs(1);
}#[tracing::instrument]
fn test_span1() {trace!("trace_my_instrument");let _ = std::time::Duration::from_secs(1);
}fn main() {tracing_subscriber::fmt().with_max_level(Level::TRACE).with_writer(io::stdout).init();test_span();test_span1();
}

程序输出:

rust">2024-11-08T07:38:25.875136Z ERROR test_span: log: error_my_instrument
2024-11-08T07:38:25.875266Z TRACE test_span1: log: trace_my_instrument

异步函数中span

在异步程序中,异步函数都会返回一个Future,Future又会通过poll的不断轮询推进函数执行进度,具体的可参考
Rust异步编程
而在trcing crate中的Instrument(区别于macros),Instrument是一个用于扩展future的trait,它以span为参数,每当future被轮询的时候,都会enter当前跨度,每当当前future被挂起时,都会退出当前跨度,所以再此基础上可以一直追踪future。

代码示例:

rust">use std::io;
use tracing::*;
use tracing_subscriber;async fn test_span1() {trace!("this is a tracing");let _ = std::time::Duration::from_secs(1);
}
#[tokio::main]
async fn main() {tracing_subscriber::fmt().with_max_level(Level::TRACE).with_writer(io::stdout).init();let span = info_span!("trace_my_instrument");test_span1().instrument(span).await;
}

Instrument中的?

[?]就是使用Debug的简写,直接看代码:

rust">use std::io;
use tracing::*;
use tracing_subscriber;#[derive(Debug)]
struct MyStruct {field: &'static str,
}
async fn test_span1() {let my_struct = MyStruct {field: "Hello world!",};event!(Level::TRACE, greeting = ?my_struct);let _ = std::time::Duration::from_secs(1);
}
#[tokio::main]
async fn main() {tracing_subscriber::fmt().with_max_level(Level::TRACE).with_writer(io::stdout).init();let span = info_span!("trace_my_instrument");test_span1().instrument(span).await;
}

tracing_subscriber

代码中的tracing_subscriber用于格式化信息输出,包括数据格式,时间格式,输出到哪里,设置tracing最大级别等等。 由此名称也可以推断,每次生成的tracing信息都由其收集。


tracing 的原生Subscribers

当Spans和Events发生时,它们会被Subscriber trait的实现记录或聚合。当事件发生以及输入或退出Span时,Subscriber 会收到通知。这些通知由以下Subscriber trait方法表示:

rust">event, 当事件发生时触发
enter, 当进入跨度时触发
exit, 当推出跨度时触发

当我们需要高度定制化的Subscriber 时不如实现Subscriber trait来试试。

总结

至此,tracing 的基本使用方法和核心的几种方法就介绍完了,当然与之相连的还有很多crate,读者需要在需要时仔细阅读,多加练习。
开发者一般关注日志追踪的目的也时为了解决问题和提升系统,所以一般情况下错误处理和日志管理是一起讲的,thiserror , Anyhow
等都是很好的错误处理crate,错误处理在Rust中也犹如艺术一般。

“爬到山顶的最好方式就是不要去看远处的山顶,而是多注意脚下的路”


http://www.ppmy.cn/server/141428.html

相关文章

【已解决】Postman:Get请求传JSON数据

解决方案 将 JSON 对象转换为字符串&#xff0c;然后进行 URL 编码&#xff0c;最后作为查询参数附加到 URL 上。 例如&#xff0c;如果有一个 JSON 对象 {“key”: “value”}&#xff0c;你可以先将其转为字符串形式 “{“key”: “value”}”&#xff0c;然后 URL 编码得到…

Redis经典面试题-深度剖析

redis是单线程架构还是多线程架构 Redis 的核心操作是单线程架构&#xff0c;但在某些场景中也会使用多线程。 Redis 的大部分操作&#xff08;如键值存储、查询、更新等&#xff09;是通过单线程完成的&#xff0c;即所有客户端的请求在 Redis 中按顺序执行。这种设计主要出…

数据分析-44-时间序列预测之深度学习方法TCN

文章目录 1 TCN简介1.1 网络示意图1.2 TCN优点2 模拟应用2.1 模拟数据2.2 预处理创建滞后特征2.3 划分训练集和测试集2.4 创建TCN模型2.5 模型训练2.6 模型预测3 自定义my_TCN模型3.1 my_TCN()函数3.2 训练模型3.3 模型预测3.4 改进4 参考附录1 TCN简介 时间卷积网络(TCN)是…

前端权限控制代码

使用场景&#xff1a; 前端页面根据权限码&#xff0c;对页面模块、组件的显示控制。支持场景&#xff1a; 在js代码里&#xff0c;进行同步、异步权限判断、拦截登操作&#xff1b;在templete里通过v-if指令&#xff0c;控制组件、元素的显示&#xff1b; API说明&#xff1…

RHCE-第四章:ssh远程连接服务器

一、SSH介绍 SSH&#xff08;Secure Shell Protocol&#xff0c;安全的壳程序协议&#xff09;它可以通过数据包加密技术将等待传输的数据包加密后再传输到网络上。ssh协议本身提供两个服务器功能&#xff1a;一个是类似telnet的远程连接使用shell的服务器&#xff1b;另一个就…

[CKS] CIS基准测试,修复kubelet和etcd不安全项

目前的所有题目为2024年10月后更新的最新题库&#xff0c;考试的k8s版本为1.31.1 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS] K8S NetworkPolicy Set Up-C…

一些npm总结(持续更新...)

react复制npm react-copy-to-clipboard npm地址:https://www.npmjs.com/package/react-copy-to-clipboard 页面开发提效的神器 Code Inspector 点击页面上的 DOM 元素&#xff0c;它能自动打开 IDE 并将光标定位至 DOM 的源代码位置 官网&#xff1a;https://inspector.fe-dev.…