コンテンツにスキップ

OTLPのspan nameが情報量少なすぎる件

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

OpenTelemetry の公式 instrumentation が付ける span name が粗すぎて、Grafana などの trace UI で見たときに実用上の情報量が足りない、という問題提起の記事。 HTTP クライアントの span name が GET だけだったり、mysqlclient の span name が SELECT だけだったりすると、同じ名前の span が大量に並び、どの処理かを毎回属性で開く必要がある。 取得できた本文が限定的だったため partial として保存するが、問題意識と読み替えを残す。

本文

記事の問題意識は、OpenTelemetry で trace を取っても、span name が粗いと trace UI が使いにくいというもの。 OpenTelemetry では span に attributes があり、詳細情報は http.methodhttp.routedb.statement などへ入る。 しかし、trace 一覧やウォーターフォール表示でまず目に入るのは span name。 span name が情報量の少ない文字列だと、調査時の認知負荷が高くなる。

HTTP client span が GET だけになる問題

例として、Requests の instrumentation で span name が GET のような HTTP メソッドだけになるケースが挙げられている。 HTTP メソッドだけでは、どの API へのアクセスか分からない。

trace 画面で GET が複数並ぶと、ユーザーはそれぞれの span を開き、attributes の URL や route を確認しなければならない。 障害調査では、どの外部 API、どの内部サービス、どの endpoint が遅いのかをすぐ見たい。 span name が GET だけでは、その入口で詰まる。

理想的には、クライアント呼び出しでも、少なくとも host や route 相当の情報が名前に入っている方が調査しやすい。 ただし、URL 全体を入れると ID やクエリパラメータで cardinality が爆発するため、正規化が必要になる。

DB span が SELECT だけになる問題

mysqlclient の instrumentation では、span name が SELECT のような SQL 操作種別だけになるケースが問題として挙げられている。 これも同じで、SELECT が大量に並んでも、どのテーブル、どのクエリ、どの処理か分からない。

詳細な SQL は db.statement attribute に入ることがある。 しかし、Grafana の trace view で毎回 span をクリックして db.statement を見るのは手間。 ウォーターフォール上で「この SELECT が遅い」と見えても、名前だけでは何の SELECT か判別できない。

一方で、SQL 文をそのまま span name に入れると別の問題が起きる。 値入り SQL は cardinality が高くなり、個人情報や機密値が入る危険もある。 したがって、span name には操作種別だけでなく、正規化されたテーブル名やクエリ種別のような、安全で安定した識別子を入れる設計が必要になる。

Flask の route span はまだ使いやすい

記事では、Flask の span name が GET /user/<id:int> のように route template を含む場合があり、これは比較的使いやすい例として読める。 実際の user ID を入れるのではなく、route pattern を入れることで、cardinality を抑えつつ、どの endpoint かが分かる。

HTTP server span では、OpenTelemetry semantic conventions でも route template を使う方向が一般的。 GET /users/{id} のような名前なら、trace UI で見た瞬間に対象の処理が分かる。

この考え方を、HTTP client や DB span にも応用したい。 生の URL や SQL ではなく、正規化された意味のある名前を span name に置く。

span name と attributes の役割

OpenTelemetry では、詳細情報は attributes に持たせる設計が多い。 これは検索や集計、仕様上の整理としては正しい。 しかし、UI 上の可読性では、span name が強い。

span name は、trace を開いた瞬間に見える見出し。 attributes は、必要に応じて掘る詳細。 見出しが GETSELECT だけでは、詳細を見る前に比較判断ができない。

したがって、span name には「人間が一覧で見て区別できる程度の情報量」が必要になる。 ただし、cardinality を増やしすぎない、機密情報を入れない、仕様と互換性を保つという制約もある。

読み替え

この記事は、OpenTelemetry の自動計装をそのまま使えば十分、とは限らないことを示している。 自動計装は導入を簡単にするが、運用で本当に使いやすい trace にするには、span name、attributes、resource、service name、route、DB 情報の設計が必要。

バックエンドで実務的に使うなら、次の観点を確認したい。

  • HTTP server span name は route template になっているか。
  • HTTP client span name は呼び出し先を区別できるか。
  • DB span name は操作種別だけでなく、どの処理かを推測できるか。
  • 生の ID、URL query、SQL literal、個人情報を span name に入れていないか。
  • Grafana や Tempo など実際に使う UI で、クリックなしに主要な遅延箇所が分かるか。

実装時の方向性

アプリケーション側で span name を補正できる場合は、低 cardinality で意味のある名前にする。

HTTP server なら次のような名前が望ましい。

GET /users/{id}
POST /orders

HTTP client なら、外部サービス名と正規化された endpoint を使う。

GET payment-api /v1/customers/{id}
POST search-api /v1/query

DB なら、SQL 全文ではなく、操作と対象、用途が分かる範囲に抑える。

SELECT users by id
UPDATE orders status

このような名前にするには、instrumentation hook、middleware、wrapper、DB access layer で span をリネームする必要がある。 公式 instrumentation のデフォルトが調査しにくい場合は、アプリケーションの観測設計として補う。

要点

  • span name が GETSELECT だけだと、trace UI でどの処理か分からない。
  • 詳細が attributes にあっても、一覧で見える span name の情報量が足りないと調査が遅い。
  • route template のような正規化された名前は、cardinality を抑えつつ人間に分かりやすい。
  • SQL 全文や URL 全体を span name に入れると cardinality や機密情報の問題がある。
  • 自動計装のデフォルトに任せず、実際の UI で使いやすい span naming を設計する必要がある。
  • 取得本文が限定的だったため、詳細な実装例や議論は未確認。

タグ

opentelemetry #tracing #span-name #observability #python #go