## OpenTelemetry - OpenTelemetry是 CNCF 的一个可观测性项目,旨在提供可观测性领域的标准化方案,解决观测数据的数据模型、采集、处理、导出等的标准化问题,提供与三方 vendor 无关的服务。 - OpenTelemetry 是什么? - OpenTelemetry 是一组标准和工具的集合,旨在管理观测类数据,如 trace、metrics、logs 等 (未来可能有新的观测类数据类型出现)。 - OpenTelemetry 提供与 vendor 无关的实现,根据用户的需要将观测类数据导出到不同的后端,如开源的 Prometheus、Jaeger 或云厂商的服务中。 - OpenTelemetry 不是什么 - 即 OpenTelemetry 不提供与可观测性相关的后端服务,这类后端服务通常提供的是存储、查询、可视化等服务。 - 通过下述抽象图可以简单理解 OpenTelemetry 的工作范围: - ![png](https://ucc.alicdn.com/pic/developer-ecology/2541b8c5a42e4bffb2fea28926f70715.png) ### trace - opentelemetry中的trace由多个span定义,可以将trace视为span的有向无环图, 其中span间的边定义为父/子关系。 ``` Causal relationships between Spans in a single Trace [Span A] ←←←(the root span) | +------+------+ | | [Span B] [Span C] ←←←(Span C is a `child` of Span A) | | [Span D] +---+-------+ | | [Span E] [Span F] ``` - 更直观的查看方式,通过时间轴查看trace ``` Temporal relationships between Spans in a single Trace ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time [Span A···················································] [Span B··········································] [Span D······································] [Span C····················································] [Span E·······] [Span F··] ``` - 一个Span代表一个工作或操作单元包含如下字段 - Name - Parent span ID (empty for root spans) - Start and End Timestamps - Span Context - Attributes - Span Events - Span Links - Span Status - SpanContext 表示标识跟踪中的 Span 的所有信息 - TraceId 跟踪ID,表示一个跟踪链,需要全局唯一 - SpanId 跨度标识,需要在跟踪链中唯一 - TraceFlags 跟踪标志,用于外部觉得是否需要应用跟踪 - [Tracestate](https://w3c.github.io/trace-context/#tracestate-field) 跟踪状态,分布式跟踪是一组事件,跨应用程序的各个组件进行合并 - Span Links - span可以链接到零个或多个其他因果相关的span ### Baggage 跨多端采集 - 实现标准 [w3c trace-context](https://www.w3.org/TR/trace-context/) + traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 - version: 2hex 版本 - trace-id: 32hex 应该跟踪ID,全局唯一 - parent-id: 16hex 父spanid - trace-flags: 2hex 抽样标志 00表示需要记录,01表示采样 + tracestate(可选) - 跨不同的分布式跟踪系统提供额外的供应商特定的跟踪标识信息,它还传达有关请求在多个分布式跟踪图中的位置的信息 - 格式:标头名称保持小写。标头名称是一个没有任何分隔符的单词,例如 vendorname1=opaqueValue1,vendorname2=opaqueValue2 ### rust 采集演示 ``` RUST fn work() { let root = tracing::info_span!("child work"); let _enter = root.enter(); } async fn work_async() { let root = tracing::info_span!("child work async"); let _enter = root.enter(); } // 自动创建span方法 #[instrument] #[inline] async fn expensive_work() { tracing::span!(tracing::Level::INFO, "expensive_step_1") .in_scope(|| thread::sleep(Duration::from_millis(25))); tracing::span!(tracing::Level::INFO, "expensive_step_2") .in_scope(|| thread::sleep(Duration::from_millis(25))); } #[tokio::test(flavor = "multi_thread")] async fn test() -> Result<(), Box> { let root = tracing::info_span!("app_start"); let _enter = root.enter(); // 同步调用 work(); // 异步调用 work_async() .instrument(tracing::info_span!("child work async root")) .await; // 自动创建span方法 expensive_work().await; } ``` ![](http://ser.yinengyun.com:10082/tech/img/uploads/b7890ae31d173aace3a870ea11dd9cad/20221107175920.png) - [instrument官方说明文档](https://tracing-rs.netlify.app/tracing/attr.instrument.html) ### ts 采集演示 ``` import { getTrace } from "pi_common/opentelemetry/trace_lib"; let tracer = getTrace(); // Baggage导出 // {"traceparent": "00-277656cdf4562c5892da9925ce98c697-0fa197db81558780-01", "tracestate": ""} // 还原父span const parentSpan = tracer.extract( "http_headers", { traceparent }); const span = tracer.startSpan('listener_pid', { childOf: parentSpan, }); // 添加属性 span.setTag('alpha', '200'); span.setTag('beta', '50'); // 添加事件 span.log({ state: 'waiting' }); span.finish(); ``` #### 阿里云demo演示