Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OpenTelemetry with Rust

ymgyt
April 07, 2024

OpenTelemetry with Rust

ymgyt

April 07, 2024
Tweet

More Decks by ymgyt

Other Decks in Programming

Transcript

  1. 概要 • Rust に OpenTelemetry を導入して、Traces, Metrics, Logs を取得した •

    tracing という lib から OpenTelemetry API を 利用した
  2. OpenTelemetry API を直接利用 するか tracing 経由で OpenTelemetry を利用すること にした •

    既に tracing を計装済だった • 利用している lib の多くが tracing で計装され ていた • tracing‑opentelemetry が tokio から提供され ていた
  3. OpenTelemetry API を直接利用 するか Rust の OpenTelemetry project でも tracing

    との 関係が OpenTelemetry Tracing API vs Tokio‑ Tracing API for Distributed Tracing で議論されて いる
  4. Resource 1 use opentelemetry_sdk::{resource::EnvResourceDetector, Resource}; 2 use opentelemetry_semantic_conventions::{ 3 resource::{SERVICE_NAME,

    SERVICE_NAMESPACE, SERVICE_VERSION}, 4 SCHEMA_URL, 5 }; 6 7 pub fn resource( 8 service_name: impl Into<Cow<'static, str>>, 9 service_version: impl Into<Cow<'static, str>>, 10) -> Resource { 11 Resource::from_schema_url( 12 [ 13 (SERVICE_NAME, service_name.into()), 14 (SERVICE_VERSION, service_version.into()), 15 (SERVICE_NAMESPACE, "foo".into()), 16 ] 17 .into_iter() 18 .map(|(key, value)| KeyValue::new(key, value)), 19 SCHEMA_URL, // https://opentelemetry.io/schemas/1.21.0 20 ) 21 .merge(&Resource::from_detectors( 22 Duration::from_millis(200), 23 // Detect "OTEL_RESOURCE_ATTRIBUTES" environment variables 24 vec![Box::new(EnvResourceDetector::new())], 25 )) 26}
  5. Traces #[tracing::instrument(skip_all, fields(%url))] async fn fetch_feed(&self, url: Url) -> Result<Feed,Error>

    { /* ... */ } 関数に Span を設定するには #[tracing::instrument] annotation を 利用する fields()に値を指定すると Span の attribute になる
  6. Traces async fn run() { // ... info!( "enduser.id"= user_id,

    "operation" = operation, ); // ... 関数の中で行った logging(info や error)は Span の Events として記録される
  7. Traces の課題 Context propagation では OpenTelemetry の仕組 みを利用するので、OpenTelemetry の Context

    や Baggage を意識する必要がある。 (Application が OpenTelemetry をまったく意識 しなくていいことにならない) • inject 時は tracing::Span ‑> opentelemetry::Context ‑> http::Header • extract 時は http::Header ‑> opentelemetry::Context ‑> tracing::Span
  8. Traces の課題 OpenTelemetry の trace は 1 layer(plugin)という 位置づけなので、sampling しない場合でも

    tracing 側で respect されない あくまで tracing 側の機構で span の sampling を制御する必要がある RUST_LOG=app=info, lib_b[request{path="foo"}]=trace (一方で、アプリケーションは info、lib_b の request span の path が"foo"は trace といった制 御ができる)
  9. Metrics Prometheus Metrics Application からは prometheus 形式の metrics を export

    して OpenTelemetry collector で OpenTelemetry 形式に変換する
  10. Metrics Prometheus Metrics Application からは prometheus 形式の metrics を export

    して OpenTelemetry collector で OpenTelemetry 形式に変換する => Signal を関連づける重要性が謳われていたの で採用しなかった
  11. Metrics OpenTelemetry Metrics API fn main() { let meter =

    opentelemetry::global::meter("foo"); let counter = meter.u64_counter("counter").init(); counter.add( 10, &[opentelemetry::KeyValue::new("key","value")], ); }
  12. Metrics OpenTelemetry Metrics API fn main() { let meter =

    opentelemetry::global::meter("foo"); let counter = meter.u64_counter("counter").init(); counter.add( 10, &[opentelemetry::KeyValue::new("key","value")], ); } => traces との一貫性を重視して、metrics も tracing 経由で 操作することにした
  13. Metrics tracing‑opentelemetry MetricsLayer info!( monotonic_counter.http.server.request = 1, http.response.status.code = status

    ); tracing では、info!の logging は Event の dispatch になる layer 側で、monotonic_counter prefix がついた event を受け 取ったら、attribute(http.response.status.code)を付与して、 counter metrics を生成する
  14. Metrics の課題 例えば、OpenTelemetry の UpDownCounter は Prometheus の Gauge になる

    > If the aggregation temporality is cumulative and the sum is non‑monotonic, it MUST be converted to a Prometheus Gauge. https://opentelemetry.io/docs/specs/otel/ compatibility/prometheus_and_openmetrics/# sums
  15. Metrics の課題 Prometheus との互換性に関しては Prometheus の Our commitment to OpenTelemetry

    というブ ログで互換性向上の取り組みが紹介されていた • http.server.request.duration ‑> http_server_requrest_duration の変換をなく す • Native support for resource attributes(prometheus は metrics attributes と resource を flat な label にする) • OTLP の support
  16. Metrics の課題 Elastic stack(elasticsearch, kibana)を使っていた 際も OpneTelemetry の resource や

    attributes が 変換されてしまっていたが Announcing the Elastic Common Schema(ECS) and OpenTelemetry Semantic Convention Convergence で、両者の統合が発表されていた > The goal is to achieve convergence of ECS and OTel Semantic Conventions into a single open schema that is maintained by OpenTelemetry
  17. Metrics の課題 Event の field(key,value)だけで表現できない metrics も value には primitive

    型(i64,f64,bool,..)や fmt::Display 等の文字列表現しか利用できない => Histogram で boundaries を指定したい場合 に対応できない
  18. Metrics の課題 use opentelemetry_sdk::metrics::{Instrument, Stream,View}; fn view() -> impl View

    { |instrument: &Instrument| -> Option<Stream> { match instrument.name.as_ref() { "graphql.duration" => Some( Stream::new() .name(instrument.name.clone()) .aggregation( opentelemetry_sdk::metrics::Aggregation::ExplicitBucketHistogram { boundaries: vec![ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0, ], record_min_max: false, }, ) .unit(Unit::new("s")), ), _ => { None } } } }
  19. Metrics の課題 Metrics の View という仕組みで、SDK 初期化時 に変換処理を追加することで対処した • Metrics

    生成箇所と SDK 初期化処理で code が 別れてしまった • 名前(Instrument.name)で一致させているので 型/compile 時の保証がない
  20. Logs Logs の収集には 2 つの方法を利用した • Kubernetes 環境では stdout に出力したのち、

    node に配置した collector から filelog receiver で収集 • opentelemetry‑appender‑tracing
  21. Logs Logs の spec OpenTelemetry Logging では Traces や metrics

    のように新しい API を提供す るのではなく Logs Bridge API を用意して、言語ごとの既存の logging ecosystem に組み込んでもらう方針が説 明されている > Our approach with logs is somewhat different. For OpenTelemetry to be successful in logging space we need to support existing legacy of logs and logging libraries
  22. Logs の課題 #[tracing::instrument] async fn foo() { info!("message"); } とした場合

    • foo Span の event に"message"が記録される • Log の record としても"message"が記録される
  23. Logs の課題 tracing(tracing_subsriber::FmtLayer)では、 http{method=POST path=/graphql request_id=Fsg3zhkIS4}: service{query="foo"}: db{connection=1} "message" のように

    log に span の情報を埋め込んでくれる ので Log 側も trace の情報を一部保持すること になる
  24. Logs の課題 tracing(tracing_subsriber::FmtLayer)では、 http{method=POST path=/graphql request_id=Fsg3zhkIS4}: service{query="foo"}: db{connection=1} "message" のように

    log に span の情報を埋め込んでくれる ので Log 側も trace の情報を一部保持すること になる trace は sampling されたり log とは lifecycle が 違うので log にどの程度 context 情報を付与す るか悩ましい
  25. 実際のコード 趣味で作っているツールの backend api でも OpenTelemetry を利用しました 本スライドで話した内容の実際のコード、 collector の設定

    file、grafana dashboard を公開 しています。 https://github.com/ymgyt/syndicationd/blob/ 943b9c4d36b3e45a616deb9065f384faf5c193a0/ crates/synd_api/src/main.rs#L45
  26. まとめ • Rust で tracing を利用して OpenTelemetry の 各 signal

    を導入できた • tracing を通じて OpenTelemetry API を利用す ることのメリットもある一方で課題もわかっ た