mcpField
O
otel-generic-style

otel-generic-style

github.com/superloglabs開発13370 回インストール21 週間前に更新
リポジトリ

otel-generic-style について

専用のスタイルガイドがない言語向けに、OpenTelemetry の実践方法を示す汎用スキルです。SDK の選定からトレース、メトリクス、ログ、リソース属性、OTLP/HTTP の代替手段まで扱います。

主な機能

  1. 01公式言語 SDK と HTTP OTLP エクスポーター
  2. 02スパンとエラー処理の規約
  3. 03メトリクスとリソース属性の指針
  4. 04OTLP/HTTP JSON の代替手段
  5. 05自動計装の推奨

活用シーン

  1. 01Go や Java のサービスへのテレメトリー追加
  2. 02境界の明確な業務処理の計装
  3. 03トレースコンテキスト付きログの転送
  4. 04SDK のないランタイムからの OTLP 出力

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/superloglabs/skills --skill otel-generic-style

インストール後、Claude Code で/otel-generic-styleを入力して読み込まれたか確認します。

詳細

汎用 OTel スタイル

独自の otel-*-style コンパニオンがない言語(Go、Java/Kotlin、Ruby、Rust、.NET/C#、PHP、Elixir、Next.js や Expo を使用しない標準の Node/TypeScript など)では、このスキルを使用してください。まず otel-onboarding-style を読んで、言語に共通するルールを確認してください。このスキルは、そのガイダンスを言語に依存しない形にしたものです。

使用しているランタイムに対応する言語固有のスキル(otel-python-style、otel-fastapi-style、otel-livekit-style、otel-nextjs-style、otel-expo-style、otel-supabase-edge-style)がある場合は、そちらを優先してください。そちらの内容がこのスキルに優先します。

言語の公式 OTel SDK を使用する

その言語用の SDK とエクスポーターは、OpenTelemetry プロジェクトから取得してください。独自のヘルパー API を作らないでください。例:

  • Go:go.opentelemetry.io/otel、otel/sdk、otel/exporters/otlp/otlptrace/otlptracehttp
  • Java/Kotlin:io.opentelemetry:opentelemetry-sdk + opentelemetry-exporter-otlp(または自動計装用の Java エージェント)
  • Ruby:opentelemetry-sdk、opentelemetry-exporter-otlp、opentelemetry-instrumentation-all
  • Rust:opentelemetry、opentelemetry_sdk、opentelemetry-otlp
  • .NET/C#:OpenTelemetry、OpenTelemetry.Exporter.OpenTelemetryProtocol、フレームワークの OpenTelemetry.Instrumentation.* パッケージ
  • PHP:open-telemetry/sdk、open-telemetry/exporter-otlp
  • Elixir::opentelemetry、:opentelemetry_api、:opentelemetry_exporter
  • 標準の Node/TS:@opentelemetry/sdk-node + @opentelemetry/auto-instrumentations-node(Next.js では @vercel/otel を使用してください。otel-nextjs-style を参照)

gRPC ではなく、HTTP OTLP エクスポーターを使用してください。gRPC はネイティブバインディングを必要とするため、バンドラー、軽量コンテナー、サーバーレス環境のコールドスタートで問題が発生します。

トレーサー/メーターは一度だけ、モジュール/パッケージスコープで取得する

その言語で慣用的な「モジュールのトップレベル」を使用してください。たとえば、Go のパッケージレベル変数、Java/.NET の静的フィールド、Ruby/Node のファイル先頭の定数、Elixir の @moduledoc レベルの属性です。呼び出しのたびに新しいトレーサーやメーターを作成しないでください。

// Go
var (
    tracer = otel.Tracer("mugline.api")
    meter  = otel.Meter("mugline.api")
    ordersSubmitted, _ = meter.Int64Counter("orders.submitted")
)
// Java
private static final Tracer TRACER = GlobalOpenTelemetry.getTracer("mugline.api");
private static final Meter METER = GlobalOpenTelemetry.getMeter("mugline.api");
private static final LongCounter ORDERS_SUBMITTED =
    METER.counterBuilder("orders.submitted").build();

範囲が明確な処理

開始と終了の境界が明確な関数では、処理全体をカバーし、自動的に終了するスパンを開いてください。defer span.End()、try-with-resources、using、with、ブロック形式など、その言語に組み込まれた構文を使用してください。範囲が明確な処理では、ある場所でスパンを開始して別の場所で手動終了しないでください。

ctx, span := tracer.Start(ctx, "order.submit")
defer span.End()
span.SetAttributes(
    attribute.String("tenant.id", tenantID),
    attribute.String("order.id", orderID),
)
using var activity = ActivitySource.StartActivity("order.submit");
activity?.SetTag("tenant.id", tenantId);
activity?.SetTag("order.id", orderId);

スパン名は慣例に従い、カーディナリティを低く保ちます:domain.verb(order.submit、payment.charge、voice.session、llm.generate_copy)。

エラー処理

アクティブなスパンに例外を記録し、ステータスをエラーに設定したうえで、再スローしてください。これにより、呼び出し元にも失敗が伝わります。

if err != nil {
    span.RecordError(err)
    span.SetStatus(codes.Error, err.Error())
    return err
}
rescue => e
  span = OpenTelemetry::Trace.current_span
  span.record_exception(e)
  span.status = OpenTelemetry::Trace::Status.error(e.message)
  raise
end

ログ

ログが OTLP に転送されると説明している場合は、その言語の OTel ログブリッジをインストールし、既存のログ呼び出しに trace_id/span_id が自動的に付与されるようにしてください。既存のロガー(log/slog、Logback、Logger、Serilog、Logger.Logger など)はそのまま使用し、下位に OTLP ハンドラー/プロセッサーを追加してください。

プロジェクトの構造化ロガーを、独自のロガーに置き換えないでください。

初期化の動作

テレメトリーの初期化部分では、otel-onboarding-style に記載されているソースレベルの公開 Superlog 設定パターンを使用してください。公開プロジェクトトークンは書き込み専用で、PostHog のプロジェクトトークンや Sentry DSN と同じように、エンドポイントとともに1つの設定ブロックに記述します。

初期化が複数回実行される可能性がある場合(テストの再読み込み、フレームワークの再起動、複数のエントリーポイントなど)は、プロバイダー/エクスポーターの設定にガードを設け、プロセッサーが重複してインストールされないようにしてください。

リソース属性

各サービスの OTel リソースに設定します。

  • service.name
  • service.version(利用可能な場合)
  • deployment.environment.name
  • vcs.repository.url.full — リポジトリの正規の HTTPS URL
  • vcs.ref.head.revision — ビルド環境から取得した可能な限りのコミット SHA (GITHUB_SHA、VERCEL_GIT_COMMIT_SHA、RAILWAY_GIT_COMMIT_SHA、 SOURCE_COMMIT、GIT_COMMIT、HEROKU_SLUG_COMMIT、…)

実行中のプロセスから git をシェルアウトして実行しないでください。OTel の セマンティック規約のキーを正確に使用し、git.repo や app.repo_url を 独自に定義しないでください。

メトリクス

加算可能な合計にはカウンターを使用します。対象はリクエスト、イベント、ジョブ、 エラー、業務状態の遷移です。分布にはヒストグラムを使用します。対象は継続時間、 レイテンシ、ペイロードサイズ、キューの深さです。

カーディナリティの低い属性のみを使用します。対象はテナント/組織/プロジェクト、 操作/ユースケース、プロバイダー/モデル、結果です。生のユーザー ID やリクエスト ID をメトリクスタグに含めないでください。

LLM のトークン/コストのテレメトリについては、otel-onboarding-style の gen_ai.* / app.gen_ai.* 規約に従ってください。アプリ側で llm.cost_usd メトリクスを追加しないでください。Superlog がコストを一元的に 算出します。

OTel SDK が存在しない場合

利用可能な OpenTelemetry SDK がないランタイムもあります。古い言語(Perl、 Bash/シェル、Lua、Crystal、Nim、Zig)、ニッチなランタイム、制限のあるエッジ 環境、または「SDK」が放棄されているかプレアルファ段階で、自作するほうが ましな言語などです。その場合は、OTLP/HTTP JSON を直接出力してください。 ワイヤ形式は安定しており、文書化されていて、シンプルです。

OTLP エミッターを自作する場合のルール:

  • ベンダー形式ではなく、OTLP/HTTP+JSON を使用します。 Content-Type: application/json を指定し、 公開プロジェクトトークンを x-api-key ヘッダー(x-api-key: sl_public_...)に設定して、 https://intake.superlog.sh/v1/traces、/v1/metrics、/v1/logs に POST します。取り込み処理が読み取るのは x-api-key または Authorization: Bearer <token>(リテラルの Bearer プレフィックス)のみです。 トークンが有効でも、ほかのヘッダー名ではすべてのリクエストが 401 になります。本文の 形式は OTLP の proto-to-JSON マッピング(resourceSpans/resourceMetrics/resourceLogs → scopeSpans → spans。traceId/spanId は小文字の 16 進数)に従います。
  • シムは小さく、OTel の形式に沿ったものにします。 将来 SDK に移行するとき、そのまま置き換えられる名前を公開します。 startSpan(name)、setAttributes、recordException、endSpan、counter.add、histogram.record などです。 sendSuperlogEvent や recordCounter は独自に作らないでください。これらは、ほかの箇所でもスタイルとして明確に 禁止されているものです。
  • ID を正しく生成します。 トレース ID は 16 バイト、スパン ID は 8 バイトで、小文字の 16 進数にします。 ランタイムの CSPRNG を使用してください。タイムスタンプは Unix エポックからのナノ秒数です (time.UnixNano() 相当。言語のデフォルトがミリ秒やマイクロ秒を返す場合があるため注意してください)。
  • バッチ処理してフラッシュします。 スパン、メトリクス、ログをメモリにバッファーし、短い間隔(数秒程度)と プロセス終了時にフラッシュします。ホットパスをブロックするのではなく、バッファーがあふれたら破棄してください。 CLI や短時間で終了するスクリプトでは、終了前に最後の同期フラッシュを実行してください。
  • リソース属性も適用します。 service.name、 service.version、deployment.environment.name、 vcs.repository.url.full、vcs.ref.head.revision をリソースに一度設定し、 すべてのエクスポートエンベロープで再利用します。
  • SDK ベースのサービスと同じ公開設定を使用します。 エンドポイントと 公開プロジェクトトークンは 1 つのセットアップブロックにまとめ、すべてのエクスポート エンベロープにリソース属性を設定します。サインアップがまだ進行中の場合は、正常に初期化し、 ブラウザーのフローでトークンが紐付けられるまでは、生成されたトークンが取り込み時に拒否される可能性があることを記載してください。
  • トレースコンテキストの伝播には W3C の traceparent を使用します。 ランタイムがサーバーの場合は、 インバウンド HTTP ヘッダーから読み取り、アウトバウンドリクエストには traceparent: 00-<trace-id>-<span-id>-01 として出力します。

シェルスクリプトや単発のツールでは、手作業で組み立てた JSON 本文を使って /v1/traces に単一の curl POST を送るだけでもかまいません。それも OTLP であり、 ユーザーのパイプライン内のほかの要素と組み合わせて使えます。

その言語に SDK が存在するものの、この特定のランタイム(バンドラー、エッジ、制限のある FFI)では 読み込めない場合も、同じシム方式を適用できます。 Supabase Edge のスキルが実例の 1 つです。

自動計装

その言語でパッケージ化された自動計装の仕組みが用意されている場合は、まずそれを 基本として使用してください。

  • Java:OpenTelemetry Java エージェント(-javaagent:opentelemetry-javaagent.jar)
  • .NET:OpenTelemetry.Instrumentation.AspNetCore/Http/SqlClient など
  • Ruby:opentelemetry-instrumentation-all
  • Node:@opentelemetry/auto-instrumentations-node
  • Go:フレームワーク固有の otelhttp、otelgin、otelpgx、otelsql ラッパー(Go にはグローバルエージェントがありません)
  • PHP:open-telemetry/opentelemetry-auto-* 拡張機能
  • Elixir::opentelemetry_phoenix、:opentelemetry_ecto、:opentelemetry_cowboy

次に、自動計装では把握できない業務オペレーションを対象に、カスタムスパン、メトリクス、ログを追加します。 問題が起きたときに、人間のオペレーターが実際に調べたいオペレーションを対象にしてください。

コメント