Genkit テレメトリー プラグインの作成

Firebase Genkit ライブラリは、OpenTelemetry でインストルメント化され、トレース、指標、ログの収集をサポートします。Genkit ユーザーは、OpenTelemetry Go SDK を特定の OpenTelemetry 対応システムにエクスポートするように構成するプラグインをインストールすることで、このテレメトリー データをモニタリング ツールと可視化ツールにエクスポートできます。

Genkit には、Google Cloud Monitoring と Cloud Logging にデータをエクスポートするように OpenTelemetry を構成するプラグインが含まれています。他のモニタリング システムをサポートするには、このページで説明されているように、テレメトリー プラグインを記述して Genkit を拡張できます。

始める前に

テレメトリー プラグインなど、あらゆる種類の Genkit プラグインの作成については、Genkit プラグインの作成をご覧ください。特に、すべてのプラグインは Init 関数をエクスポートする必要があります。この関数は、ユーザーがプラグインを使用する前に呼び出すことが想定されています。

エクスポータとロガー

前述のように、テレメトリー プラグインの主な役割は、OpenTelemetry(Genkit がすでにインストルされている)を設定して、特定のサービスにデータをエクスポートすることです。そのためには、次のものが必要です。

  • 任意のサービスにデータをエクスポートする OpenTelemetry の SpanExporter インターフェースの実装。
  • 任意のサービスにデータをエクスポートする OpenTelemetry の metric.Exporter インターフェースの実装。
  • 任意のサービスにログをエクスポートする slog.Logger または slog.Handler インターフェースの実装。

エクスポート先のサービスによっては、比較的、小規模な作業になる場合もあれば、大規模な作業になる場合もあります。

OpenTelemetry は業界標準であるため、多くのモニタリング サービスには、これらのインターフェースを実装するライブラリがすでに用意されています。たとえば、Genkit の googlecloud プラグインは、Google Cloud チームが管理する opentelemetry-operations-go ライブラリを使用しています。同様に、多くのモニタリング サービスは、標準の slog インターフェースを実装するライブラリを提供しています。

一方、そのようなライブラリがサービスで使用できない場合、必要なインターフェースを実装するのは大規模なプロジェクトになる可能性があります。

OpenTelemetry レジストリまたはモニタリング サービスのドキュメントで、統合がすでに利用可能かどうかを確認します。

これらの統合を自分で構築する必要がある場合は、公式の OpenTelemetry エクスポータのソースと slog ハンドラの作成ガイドをご覧ください。

プラグインのビルド

依存関係

すべてのテレメトリー プラグインは、Genkit コア ライブラリといくつかの OpenTelemetry ライブラリをインポートする必要があります。

import {
	// Import the Genkit core library.
	"github.com/firebase/genkit/go/core"

	// Import the OpenTelemetry libraries.
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/trace"
}

既存の OpenTelemetry または slog 統合を基にプラグインを構築する場合は、それらをインポートする必要もあります。

Config

テレメトリー プラグインは、少なくとも次の構成オプションをサポートする必要があります。

type Config struct {
	// Export even in the dev environment.
	ForceExport bool

	// The interval for exporting metric data.
	// The default is 60 seconds.
	MetricInterval time.Duration

	// The minimum level at which logs will be written.
	// Defaults to [slog.LevelInfo].
	LogLevel slog.Leveler
}

以下の例では、これらのオプションを利用できることを前提としています。また、それらのオプションを処理する方法についても説明します。

ほとんどのプラグインには、エクスポート先のサービス(API キー、プロジェクト名など)の構成設定も含まれています。

Init()

テレメトリー プラグインの Init() 関数は、次のすべてを行う必要があります。

  • Genkit が開発環境で実行されている場合(genkit start で実行している場合など)で、Config.ForceExport オプションが設定されていない場合は、早期リターンします。

    shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev"
    if !shouldExport {
    	return nil
    }
    
  • トレース スパンのエクスポータを初期化し、Genkit に登録します。

    spanProcessor := trace.NewBatchSpanProcessor(YourCustomSpanExporter{})
    core.RegisterSpanProcessor(spanProcessor)
    
  • 指標エクスポータを初期化し、OpenTelemetry ライブラリに登録します。

    r := metric.NewPeriodicReader(
    	YourCustomMetricExporter{},
    	metric.WithInterval(cfg.MetricInterval),
    )
    mp := metric.NewMeterProvider(metric.WithReader(r))
    otel.SetMeterProvider(mp)
    

    PeriodicReader を初期化するときに、ユーザーが設定した収集間隔(Config.MetricInterval)を使用します。

  • slog ハンドラをデフォルトのロガーとして登録します。

    logger := slog.New(YourCustomHandler{
    	Options: &slog.HandlerOptions{Level: cfg.LogLevel},
    })
    slog.SetDefault(logger)
    

    ユーザー指定の最小ログレベル(Config.LogLevel)に従うようにハンドラを構成する必要があります。

個人情報(PII)の削除

ほとんどの生成 AI フローは、なんらかのユーザー入力から始まるため、一部のフロー トレースには個人を特定できる情報(PII)が含まれている可能性が高くなります。ユーザーの情報を保護するには、トレースから PII を削除してからエクスポートする必要があります。

独自のスパン エクスポータを構築する場合は、この機能を組み込むことができます。

既存の OpenTelemetry 統合を基にプラグインを構築する場合は、このタスクを実行するカスタム エクスポータで提供されたスパン エクスポータをラップできます。たとえば、googlecloud プラグインは、次のようなラッパーを使用してエクスポートする前に、すべてのスパンから genkit:input 属性と genkit:output 属性を削除します。

type redactingSpanExporter struct {
	trace.SpanExporter
}

func (e *redactingSpanExporter) ExportSpans(ctx context.Context, spanData []trace.ReadOnlySpan) error {
	var redacted []trace.ReadOnlySpan
	for _, s := range spanData {
		redacted = append(redacted, redactedSpan{s})
	}
	return e.SpanExporter.ExportSpans(ctx, redacted)
}

func (e *redactingSpanExporter) Shutdown(ctx context.Context) error {
	return e.SpanExporter.Shutdown(ctx)
}

type redactedSpan struct {
	trace.ReadOnlySpan
}

func (s redactedSpan) Attributes() []attribute.KeyValue {
	// Omit input and output, which may contain PII.
	var ts []attribute.KeyValue
	for _, a := range s.ReadOnlySpan.Attributes() {
		if a.Key == "genkit:input" || a.Key == "genkit:output" {
			continue
		}
		ts = append(ts, a)
	}
	return ts
}

トラブルシューティング

想定どおりの場所にデータが表示されない場合は、OpenTelemetry が提供する便利な診断ツールが問題の原因特定に役立ちます。