Écrire un plug-in de télémétrie Genkit

Les bibliothèques Firebase Genkit sont instrumentées avec OpenTelemetry. pour permettre la collecte de traces, de métriques et de journaux. Les utilisateurs de Genkit peuvent exporter ces données de télémétrie vers des outils de surveillance et de visualisation en installant un plug-in qui configure le SDK OpenTelemetry Go pour l'exportation vers un système OpenTelemetry compatible.

Genkit inclut un plug-in qui configure OpenTelemetry pour exporter les données vers Google Cloud Monitoring et Cloud Logging : Pour soutenir d'autres systèmes de surveillance, vous pouvez étendre Genkit en écrivant un plug-in de télémétrie, comme décrit sur cette page.

Avant de commencer

Consultez la section Écrire des plug-ins Genkit pour en savoir plus sur l'écriture tous types de plug-ins Genkit, y compris les plug-ins de télémétrie. En particulier, notez que chaque plug-in doit exporter une fonction Init, que les utilisateurs doivent appeler avant d'utiliser le plug-in.

Exportateurs et enregistreurs

Comme indiqué précédemment, la tâche principale d'un plug-in de télémétrie est de configurer OpenTelemetry (avec lequel Genkit a déjà été instrumenté) pour exporter des données à un service particulier. Pour ce faire, vous avez besoin des éléments suivants:

  • Implémentation de la méthode SpanExporter d'OpenTelemetry qui exporte des données vers le service de votre choix.
  • Implémentation de l'interface metric.Exporter d'OpenTelemetry qui exporte les données vers le service de votre choix.
  • Un slog.Logger ou une implémentation de l'interface slog.Handler qui exporte les journaux vers le service de votre choix.

Selon le service vers lequel vous souhaitez exporter, cela peut être un effort relativement mineur ou important.

Étant donné qu'OpenTelemetry est une norme du secteur, de nombreux services de surveillance disposent déjà de bibliothèques implémentant ces interfaces. Par exemple, googlecloud pour Genkit utilise le opentelemetry-operations-go gérée par l'équipe Google Cloud. De même, de nombreux services de surveillance fournissent des bibliothèques qui implémentent le interfaces slog standards.

En revanche, si aucune bibliothèque de ce type n'est disponible pour votre service, l'implémentation des interfaces nécessaires peut représenter un projet important.

Consultez le registre OpenTelemetry ou la documentation du service de surveillance pour voir si des intégrations sont déjà disponibles.

Si vous devez créer ces intégrations vous-même, consultez la source les exportateurs OpenTelemetry officiels et la page Guide de rédaction de gestionnaires slog.

Créer le plug-in

Dépendances

Chaque plug-in de télémétrie doit importer la bibliothèque de base Genkit et plusieurs bibliothèques OpenTelementry :

import {
	// Import the Genkit core library.

	"github.com/firebase/genkit/go/genkit"

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

Si vous créez un plug-in basé sur un objet OpenTelemetry ou slog existant vous devez aussi les importer.

Config

Un plug-in de télémétrie doit, au minimum, être compatible avec la configuration suivante : options:

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
}

Les exemples suivants supposent que vous proposez ces options vous fournir des conseils sur la façon de les gérer.

La plupart des plug-ins incluent également des paramètres de configuration pour le service vers lequel ils exportent (clé API, nom du projet, etc.).

Init()

La fonction Init() d'un plug-in de télémétrie doit effectuer toutes les opérations suivantes:

  • Retournez rapidement si Genkit s'exécute dans un environnement de développement (par exemple, lors de l'exécution avec genkit start) et que l'option Config.ForceExport n'est pas définie :

    shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev"
    if !shouldExport {
    	return nil
    }
    
  • Initialisez votre exportateur de délais de trace et enregistrez-le auprès de Genkit:

    spanProcessor := trace.NewBatchSpanProcessor(YourCustomSpanExporter{})
    genkit.RegisterSpanProcessor(g, spanProcessor)
    
  • Initialiser votre exportateur de métriques et l'enregistrer auprès d'OpenTelemetry bibliothèque:

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

    Utilisez l'intervalle de collecte configuré par l'utilisateur (Config.MetricInterval) lorsque : initialiser PeriodicReader.

  • Enregistrez votre gestionnaire slog comme enregistreur par défaut:

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

    Vous devez configurer votre gestionnaire pour qu'il respecte le niveau de journalisation minimal spécifié par l'utilisateur (Config.LogLevel).

Masquage des informations permettant d'identifier personnellement l'utilisateur

Comme la plupart des flux d'IA générative commencent par une entrée utilisateur quelconque, il s'agit il est probable que certaines traces de flux contiennent des informations informations permettant d'identifier personnellement l'utilisateur. Pour protéger les informations de vos utilisateurs, vous devez masquer les informations d'identification personnelle des traces avant de les exporter.

Si vous créez votre propre exportateur de span, vous pouvez intégrer cette fonctionnalité.

Si vous créez votre plug-in autour d'une intégration OpenTelemetry existante, vous pouvez encapsuler l'exportateur de span fourni avec un exportateur personnalisé qui effectue cette tâche. Par exemple, le plug-in googlecloud supprime les attributs genkit:input et genkit:output de chaque span avant de les exporter à l'aide d'un wrapper semblable à celui-ci :

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
}

Dépannage

Si vous ne parvenez pas à faire apparaître vos données là où vous le souhaitez, OpenTelemetry constitue un outil de diagnostic utile qui permet de localiser la source du problème.