open-telemetry/opentelemetry-go

⭐ 5,920 🍴 1,200 Go📄 Apache License 2.0 GitHubで見る →

主な特徴

  • **3つのシグナルのサポート**: トレース(Stable)、メトリクス(Stable)、ログ(Beta)
  • **標準化されたAPI**: 一貫性のあるインストルメンテーションAPI
  • **豊富なエクスポーター**: OTLP、Prometheus、Zipkin、stdout対応
  • **自動インストルメンテーション**: 人気のあるライブラリに対する公式サポート
  • **軽量で高性能**: Go言語の特性を活かした効率的な実装
  • **拡張可能**: カスタムエクスポーターやプロセッサーの実装が容易
  • **活発なコミュニティ**: 定期的な更新と改善

リポジトリ解析: open-telemetry/opentelemetry-go

基本情報

  • リポジトリ名: open-telemetry/opentelemetry-go
  • 主要言語: Go
  • スター数: 5,920
  • フォーク数: 1,200
  • 最終更新: 継続的に更新中(活発なプロジェクト)
  • ライセンス: Apache License 2.0
  • トピックス: オブザーバビリティ、分散トレーシング、メトリクス、ログ、テレメトリ、クラウドネイティブ

概要

一言で言うと

OpenTelemetry-Goは、アプリケーションのパフォーマンスと動作を直接測定し、そのデータをオブザーバビリティプラットフォームに送信するためのGo言語実装です。

詳細説明

OpenTelemetryは、クラウドネイティブソフトウェアのオブザーバビリティのための統一的な標準を提供するプロジェクトです。このGo実装は、分散トレース、メトリクス、ログの収集と送信を可能にし、開発者がアプリケーションの内部動作を理解し、パフォーマンスの問題を診断できるようにします。

CNCF(Cloud Native Computing Foundation)のプロジェクトとして、ベンダー中立的なアプローチを採用し、様々なバックエンドシステムと統合できる設計になっています。

主な特徴

  • 3つのシグナルのサポート: トレース(Stable)、メトリクス(Stable)、ログ(Beta)
  • 標準化されたAPI: 一貫性のあるインストルメンテーションAPI
  • 豊富なエクスポーター: OTLP、Prometheus、Zipkin、stdout対応
  • 自動インストルメンテーション: 人気のあるライブラリに対する公式サポート
  • 軽量で高性能: Go言語の特性を活かした効率的な実装
  • 拡張可能: カスタムエクスポーターやプロセッサーの実装が容易
  • 活発なコミュニティ: 定期的な更新と改善

使用方法

インストール

前提条件

  • Go 1.23以上(現在はGo 1.23と1.24をサポート)
  • 対応OS: Ubuntu、macOS、Windows
  • 対応アーキテクチャ: amd64、386、arm64

インストール手順

# 方法1: go getを使用(推奨)
go get go.opentelemetry.io/otel

# 必要なエクスポーターとSDKコンポーネントを追加
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go get go.opentelemetry.io/otel/sdk/trace
go get go.opentelemetry.io/otel/sdk/metric

# 方法2: go.modファイルで依存関係を管理
# go.modに以下を追加:
# require (
#     go.opentelemetry.io/otel v1.37.0
#     go.opentelemetry.io/otel/trace v1.37.0
#     go.opentelemetry.io/otel/metric v1.37.0
# )

基本的な使い方

Hello World相当の例

// トレースの基本的な使用例
package main

import (
    "context"
    "log"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
    "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
    // Stdoutエクスポーターの作成
    exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
    if err != nil {
        log.Fatal(err)
    }
    
    // TracerProviderの設定
    tp := trace.NewTracerProvider(
        trace.WithBatcher(exporter),
    )
    otel.SetTracerProvider(tp)
    
    // トレーサーの取得とスパンの作成
    tracer := otel.Tracer("hello-tracer")
    ctx, span := tracer.Start(context.Background(), "hello-operation")
    defer span.End()
    
    // ここにアプリケーションのロジック
    log.Println("Hello, OpenTelemetry!")
}

実践的な使用例

// HTTPサーバーのインストルメンテーション例
package main

import (
    "fmt"
    "net/http"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/trace"
)

var (
    tracer  = otel.Tracer("dice-server")
    meter   = otel.Meter("dice-server")
    rollCnt metric.Int64Counter
)

func init() {
    var err error
    rollCnt, err = meter.Int64Counter("dice.rolls",
        metric.WithDescription("The number of dice rolls"),
        metric.WithUnit("1"))
    if err != nil {
        panic(err)
    }
}

func rolldice(w http.ResponseWriter, r *http.Request) {
    ctx, span := tracer.Start(r.Context(), "roll")
    defer span.End()
    
    // サイコロを振る(1-6)
    roll := 1 + rand.Intn(6)
    
    // スパンに属性を追加
    span.SetAttributes(attribute.Int("roll.value", roll))
    
    // メトリクスを記録
    rollCnt.Add(ctx, 1,
        metric.WithAttributes(attribute.Int("roll.value", roll)))
    
    fmt.Fprintf(w, "rolled a %d\n", roll)
}

高度な使い方

// OTLPエクスポーターを使用した本番環境向け設定
package main

import (
    "context"
    "time"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)

func initTracer() (*sdktrace.TracerProvider, error) {
    ctx := context.Background()
    
    // リソース情報の設定
    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName("my-service"),
            semconv.ServiceVersion("v1.0.0"),
        ),
    )
    if err != nil {
        return nil, err
    }
    
    // OTLPエクスポーターの作成
    exporter, err := otlptrace.New(
        ctx,
        otlptracegrpc.NewClient(
            otlptracegrpc.WithEndpoint("localhost:4317"),
            otlptracegrpc.WithInsecure(),
        ),
    )
    if err != nil {
        return nil, err
    }
    
    // TracerProviderの作成
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.AlwaysSample()),
    )
    
    // グローバル設定
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(
        propagation.NewCompositeTextMapPropagator(
            propagation.TraceContext{},
            propagation.Baggage{},
        ),
    )
    
    return tp, nil
}

ドキュメント・リソース

公式ドキュメント

サンプル・デモ

  • opentelemetry-go-contrib/examples: 実践的な使用例の豊富なコレクション
  • Dice Rolling Service: HTTPサーバーのインストルメンテーション例
  • gRPC例: gRPCサービスのトレーシング実装
  • Database例: データベース操作のトレーシング

チュートリアル・ガイド

  • Getting Started Guide(公式サイト)
  • オブザーバビリティのベストプラクティス
  • インストルメンテーションライブラリの使用方法
  • カスタムエクスポーターの実装ガイド
  • パフォーマンスチューニングガイド

技術的詳細

アーキテクチャ

全体構造

OpenTelemetry-Goは、レイヤー化されたアーキテクチャを採用しています。API層は安定したインターフェースを提供し、SDK層が実装の詳細を処理します。この分離により、APIの後方互換性を保ちながら、実装を改善できます。コアコンポーネントは、トレース、メトリクス、ログの3つのシグナルタイプをサポートし、各シグナルは独立して使用できます。

ディレクトリ構成

opentelemetry-go/
├── attribute/        # 属性(キー・バリュー)の定義と操作
├── baggage/          # コンテキスト伝播用のBaggage API
├── codes/            # ステータスコードの定義
├── exporters/        # 各種エクスポーター実装
│   ├── otlp/         # OpenTelemetry Protocol エクスポーター
│   ├── prometheus/   # Prometheusエクスポーター
│   ├── stdout/       # 標準出力エクスポーター
│   └── zipkin/       # Zipkinエクスポーター
├── metric/           # メトリクスAPI(独立モジュール)
├── propagation/      # コンテキスト伝播の実装
├── sdk/              # SDK実装(独立モジュール)
├── semconv/          # セマンティック規約
├── trace/            # トレースAPI(独立モジュール)
└── log/              # ログAPI(独立モジュール)

主要コンポーネント

  • Tracer Provider: トレーサーのファクトリとグローバル設定管理

    • 場所: otel.TracerProviderインターフェース
    • 依存: Resource、Exporter、Sampler
    • インターフェース: Tracer() - トレーサーインスタンスの取得
  • Meter Provider: メーターのファクトリとメトリクス設定管理

    • 場所: otel.MeterProviderインターフェース
    • 依存: Resource、Reader、View
    • インターフェース: Meter() - メーターインスタンスの取得
  • Propagator: 分散コンテキストの伝播

    • 場所: propagationパッケージ
    • 依存: TextMapCarrier
    • インターフェース: Inject(), Extract() - コンテキストの注入と抽出

技術スタック

コア技術

  • 言語: Go 1.23以上(ジェネリクス、エラーハンドリングの改善を活用)
  • モジュールシステム: Go Modules(マルチモジュール構成)
  • 主要ライブラリ:
    • go-logr (v1.4.3): 構造化ロギングの抽象化
    • google/go-cmp (v0.7.0): テストでの値比較
    • stretchr/testify (v1.10.0): アサーションとモック
    • gRPC: OTLPトランスポート層

開発・運用ツール

  • ビルドツール: Make(precommit、test、generate等のタスク自動化)
  • テスト: 包括的な単体テストとインテグレーションテスト、高いカバレッジ維持
  • CI/CD: GitHub Actions(複数OS/アーキテクチャでの自動テスト)
  • 品質管理:
    • golangci-lint: 静的解析
    • gofmt/goimports: コードフォーマット
    • codecov: カバレッジ追跡
    • OSS-Fuzz: ファジングテスト

設計パターン・手法

  • インターフェース分離の原則: APIとSDKの明確な分離
  • ファクトリパターン: Provider経由でのTracer/Meter生成
  • デコレータパターン: スパンやメトリクスへの属性追加
  • コンテキストパターン: Go標準のcontext.Contextを活用した伝播
  • プラガブルアーキテクチャ: エクスポーター、プロセッサー、サンプラーの交換可能性
  • ゼロアロケーション設計: パフォーマンスクリティカルな部分での最適化

データフロー・処理フロー

  1. 計測データの生成: アプリケーションコードでスパン/メトリクスを作成
  2. 属性の付与: コンテキスト情報(サービス名、バージョン等)を追加
  3. サンプリング: 設定に基づいてデータをフィルタリング
  4. バッチング: 効率的な送信のためのデータ集約
  5. エクスポート: 設定されたエクスポーター経由でバックエンドへ送信
  6. エラーハンドリング: 失敗時のリトライとフォールバック処理

API・インターフェース

公開API

Trace API

  • 目的: 分散トレースの作成と管理
  • 使用例:
tracer := otel.Tracer("component-name")
ctx, span := tracer.Start(ctx, "operation-name",
    trace.WithAttributes(attribute.String("key", "value")),
    trace.WithSpanKind(trace.SpanKindServer))
defer span.End()

// エラーの記録
if err != nil {
    span.RecordError(err)
    span.SetStatus(codes.Error, err.Error())
}

Metric API

  • 目的: アプリケーションメトリクスの記録
  • 使用例:
meter := otel.Meter("component-name")
counter, _ := meter.Int64Counter("requests_total",
    metric.WithDescription("Total number of requests"))

counter.Add(ctx, 1,
    metric.WithAttributes(attribute.String("method", "GET")))

// ヒストグラム
histogram, _ := meter.Float64Histogram("request_duration",
    metric.WithUnit("ms"))
histogram.Record(ctx, latency)

設定・カスタマイズ

環境変数による設定

# リソース属性の設定
export OTEL_RESOURCE_ATTRIBUTES="service.name=my-service,service.version=1.0.0"

# エクスポーターエンドポイント
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"

# ヘッダーの設定
export OTEL_EXPORTER_OTLP_HEADERS="api-key=your-api-key"

# プロトコルの選択
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"

拡張・プラグイン開発

カスタムエクスポーターの実装例:

type CustomExporter struct{}

func (e *CustomExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
    for _, span := range spans {
        // カスタムロジックでスパンを処理
        fmt.Printf("Span: %s, Duration: %v\n", 
            span.Name(), span.EndTime().Sub(span.StartTime()))
    }
    return nil
}

func (e *CustomExporter) Shutdown(ctx context.Context) error {
    // クリーンアップ処理
    return nil
}

パフォーマンス・スケーラビリティ

パフォーマンス特性

  • 低オーバーヘッド: 本番環境での使用を前提とした軽量実装
  • バッチ処理: エクスポート時のネットワーク効率を最適化
  • メモリ効率: プール化とゼロアロケーション技術の活用
  • 並行処理: Goのゴルーチンを活用した非同期エクスポート

スケーラビリティ

  • サンプリング戦略: トラフィック量に応じた動的サンプリング
  • バックプレッシャー: エクスポーターの過負荷時の適切な処理
  • リソース制限: スパンやメトリクスの最大数設定可能
  • 分散環境対応: マイクロサービス環境での効率的なコンテキスト伝播

制限事項

  • 技術的な制限:
    • ログAPIは現在ベータ版
    • 一部の高度な機能は実験的ステータス
    • Go 1.22以前のバージョンはサポート終了予定
  • 運用上の制限:
    • エクスポーター先の可用性に依存
    • 大量のカーディナリティは性能に影響
    • ネットワーク遅延がエクスポートに影響する可能性

評価・所感

技術的評価

強み

  • 標準化: CNCFの標準仕様に準拠し、ベンダーロックインを回避
  • 成熟度: トレースとメトリクスAPIが安定版でプロダクション対応
  • パフォーマンス: Go言語の特性を活かした高効率な実装
  • エコシステム: 豊富なインストルメンテーションライブラリ
  • コミュニティ: 活発な開発とサポート、定期的なSIGミーティング
  • 互換性: 主要なオブザーバビリティツールとの統合

改善の余地

  • ログAPIの成熟度: まだベータ版で、一部機能が未実装
  • 学習曲線: 初学者には概念が複雑に感じられる可能性
  • 設定の複雑さ: 高度な設定には深い理解が必要
  • ドキュメントの分散: 情報が複数のリポジトリに分散

向いている用途

  • マイクロサービスアーキテクチャ: 分散トレーシングが特に有効
  • クラウドネイティブアプリケーション: Kubernetes環境との親和性
  • パフォーマンス監視: 詳細なメトリクスとトレースによる分析
  • SREプラクティス: SLI/SLOの測定と監視
  • マルチクラウド環境: ベンダー中立的な実装

向いていない用途

  • レガシーシステム: 古いGoバージョンでは使用不可
  • 超軽量アプリケーション: オーバーヘッドが相対的に大きい
  • リアルタイムシステム: 遅延に極めて敏感なシステム
  • オフライン環境: エクスポーター先への接続が前提

総評

OpenTelemetry-Goは、モダンなGoアプリケーションのオブザーバビリティを実現する標準的なソリューションとして確立されています。CNCFプロジェクトとしての信頼性と、活発なコミュニティによる継続的な改善により、プロダクション環境での採用が進んでいます。

特に分散システムやマイクロサービスアーキテクチャにおいて、その真価を発揮します。標準化されたAPIにより、ベンダーロックインを避けながら、様々なバックエンドシステムと統合できる柔軟性は大きな利点です。

一方で、シンプルなアプリケーションには過剰な場合もあり、適切な採用判断が必要です。しかし、将来的な拡張性を考慮すると、早期からOpenTelemetryを導入することで、システムの成長に合わせたオブザーバビリティの進化が可能になります。