Escribe un complemento de telemetría de Genkit

Las bibliotecas de Firebase Genkit están instrumentadas con OpenTelemetry para admitir la recopilación de seguimientos, métricas y registros. Los usuarios de Genkit pueden exportar esto datos de telemetría a las herramientas de supervisión y visualización mediante la instalación de un complemento que Configura el SDK de OpenTelemetry para Go. para exportarlos a un sistema que admita OpenTelemetry.

Genkit incluye un complemento que configura OpenTelemetry para exportar datos a Google Cloud Monitoring y Cloud Logging. Para brindar asistencia de otros sistemas de supervisión, puedes ampliar Genkit escribiendo un complemento de telemetría, como se describe en esta página.

Antes de comenzar

Lee Cómo escribir complementos de Genkit para obtener información sobre la escritura cualquier tipo de complemento Genkit, incluidos los complementos de telemetría. En particular, ten en cuenta que cada complemento debe exportar una función Init, a la que se espera que los usuarios llamen antes de usar el complemento.

Exportadores y registradores

Como se mencionó antes, el trabajo principal de un complemento de telemetría es configurar OpenTelemetry (con el que ya se instrumentó Genkit) para exportar datos a un servicio en particular. Para hacerlo, necesitas lo siguiente:

  • Una implementación de SpanExporter de OpenTelemetry que exporta datos al servicio que elijas.
  • Una implementación de metric.Exporter de OpenTelemetry que exporta datos al servicio que elijas.
  • Un slog.Logger o una implementación de la slog.Handler de servicio, que exporta registros al servicio que elijas.

Según el servicio al que estés interesado en realizar la exportación, esta podría ser un esfuerzo relativamente menor o uno grande.

Debido a que OpenTelemetry es un estándar de la industria, muchos servicios de supervisión tienen bibliotecas que implementan estas interfaces. Por ejemplo, googlecloud. para Genkit usa la interfaz opentelemetry-operations-go administrada por el equipo de Google Cloud. De forma similar, muchos servicios de supervisión proporcionan bibliotecas que implementan interfaces slog estándar.

Por otro lado, si esas bibliotecas no están disponibles para tu servicio implementar las interfaces necesarias puede ser un proyecto sustancial.

Consulta el registro de OpenTelemetry o los documentos del servicio de supervisión para ver si las integraciones ya están disponibles.

Si necesitas compilar estas integraciones por tu cuenta, echa un vistazo a la fuente de los exportadores oficiales de OpenTelemetry y la página A Guide to Writing slog Handlers.

Cómo compilar el complemento

Dependencias

Cada complemento de telemetría necesita importar la biblioteca principal de Genkit y varias Bibliotecas de 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"
}

Si compilas un complemento en torno a un objeto OpenTelemetry o slog existente de la infraestructura, también deberás importarlos.

Config

Un complemento de telemetría debería, como mínimo, admitir la siguiente configuración Opciones:

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
}

En los siguientes ejemplos, se supone que estas opciones están disponibles y te daremos información sobre cómo manejarlos.

La mayoría de los complementos también incluirán parámetros de configuración del servicio al que exportar (clave de API, nombre del proyecto, etc.).

Init()

La función Init() de un complemento de telemetría debe hacer todo lo siguiente:

  • Regresa antes si Genkit se ejecuta en un entorno de desarrollo (por ejemplo, cuando se ejecuta con genkit start) y la opción Config.ForceExport no está establecer:

    shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev"
    if !shouldExport {
    	return nil
    }
    
  • Inicializa el exportador de intervalos de seguimiento y regístralo en Genkit:

    spanProcessor := trace.NewBatchSpanProcessor(YourCustomSpanExporter{})
    core.RegisterSpanProcessor(spanProcessor)
    
  • Inicializa el exportador de métricas y regístralo con OpenTelemetry biblioteca:

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

    Usa el intervalo de recopilación configurado por el usuario (Config.MetricInterval) cuando inicializando PeriodicReader.

  • Registra tu controlador slog como el registrador predeterminado:

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

    Debes configurar tu controlador para respetar el registro mínimo especificado por el usuario. nivel superior (Config.LogLevel).

Ocultamiento de PII

Debido a que la mayoría de los flujos de IA generativa comienzan con algún tipo de entrada del usuario, es un probabilidad probable de que algunos seguimientos de flujo contengan datos (PII). Para proteger las contraseñas de tus usuarios debes ocultar la PII de los seguimientos antes de exportarlos.

Si estás compilando tu propio exportador de intervalos, puedes crear esta funcionalidad. en ella.

Si estás compilando tu complemento en función de una integración existente de OpenTelemetry, puedes unir el exportador de intervalos proporcionado con un exportador personalizado que se ocupa de tarea. Por ejemplo, el complemento googlecloud quita genkit:input y genkit:output atributos de cada intervalo antes de exportarlos con un wrapper similar a lo siguiente:

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
}

Soluciona problemas

Si tienes problemas para que los datos se muestren donde esperas, OpenTelemetry proporciona una herramienta de diagnóstico útil que ayuda a encontrar la fuente del problema.