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

OpenTelemetry with Rust

Avatar for ymgyt ymgyt
April 07, 2024

OpenTelemetry with Rust

Avatar for ymgyt

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 を利用す ることのメリットもある一方で課題もわかっ た