Como criar um plug-in de telemetria do Genkit

As bibliotecas do Firebase Genkit são instrumentadas com o OpenTelemetry. para dar suporte à coleta de traces, métricas e registros. Os usuários do Genkit podem exportar isto dados de telemetria para ferramentas de monitoramento e visualização, instalando um plug-in que configura o SDK do OpenTelemetry Go. exportar para um sistema compatível com OpenTelemetry.

O Genkit inclui um plug-in que configura o OpenTelemetry para exportar dados para Google Cloud Monitoring e Cloud Logging. Para o suporte outros sistemas de monitoramento, é possível ampliar o Genkit escrevendo um plug-in de telemetria, conforme descrito nesta página.

Antes de começar

Leia Como escrever plug-ins do Genkit (em inglês) para informações sobre como escrever qualquer tipo de plug-in Genkit, incluindo plug-ins de telemetria. Observe especificamente que cada plug-in precisa exportar uma função Init, que os usuários precisam chamar antes de usar o plug-in.

Exportadores e loggers

Como já mencionamos, a principal função de um plug-in de telemetria é configurar OpenTelemetry, que já foi instrumentado no Genkit, para exportar dados a um serviço específico. Para isso, você precisa do seguinte:

  • Uma implementação do SpanExporter do OpenTelemetry que exporta dados para o serviço de sua escolha.
  • Uma implementação do metric.Exporter do OpenTelemetry que exporta dados para o serviço de sua escolha.
  • Um slog.Logger ou uma implementação da interface slog.Handler que exporta registros para o serviço que você escolher.

Dependendo do serviço para o qual você quer exportar, pode ser um um esforço relativamente pequeno ou grande.

Como o OpenTelemetry é um padrão do setor, muitos serviços de monitoramento já têm bibliotecas que implementam essas interfaces. Por exemplo, o googlecloud plug-in para Genkit faz uso do opentelemetry-operations-go , mantida pela equipe do Google Cloud. Da mesma forma, muitos serviços de monitoramento oferecem bibliotecas que implementam a interfaces slog padrão.

Por outro lado, se não houver bibliotecas desse tipo disponíveis para seu serviço, implementar as interfaces necessárias pode ser um projeto substancial.

Verifique o registro do OpenTelemetry. ou os documentos do serviço de monitoramento para conferir se as integrações já estão disponíveis.

Se você precisar criar essas integrações por conta própria, dê uma olhada na fonte os exportadores oficiais do OpenTelemetry e a página Um guia para criar gerenciadores slog.

Como criar o plug-in

Dependências

Todo plug-in de telemetria precisa importar a biblioteca principal do Genkit e vários Bibliotecas do 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"
}

Se você estiver criando um plug-in baseado em um OpenTelemetry ou slog você também precisará importá-los.

Config

Um plug-in de telemetria precisa, no mínimo, ser compatível com a seguinte configuração: opções:

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
}

Os exemplos a seguir presumem que você está disponibilizando essas opções e que algumas orientações sobre como lidar com eles.

A maioria dos plug-ins inclui configurações para o serviço exportar para (chave de API, nome do projeto e assim por diante).

Init()

A função Init() de um plug-in de telemetria precisa realizar todas as ações a seguir:

  • Retorne antecipadamente se o Genkit estiver sendo executado em um ambiente de desenvolvimento (como quando em execução com genkit start) e a opção Config.ForceExport não estiver conjunto:

    shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev"
    if !shouldExport {
    	return nil
    }
    
  • Inicialize seu exportador de períodos de trace e registre-o com o Genkit:

    spanProcessor := trace.NewBatchSpanProcessor(YourCustomSpanExporter{})
    core.RegisterSpanProcessor(spanProcessor)
    
  • Inicializar e registrar o exportador de métricas no OpenTelemetry biblioteca:

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

    Use o intervalo de coleta configurado pelo usuário (Config.MetricInterval) quando inicializando o PeriodicReader.

  • Registre o gerenciador slog como o registrador padrão:

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

    Configure o gerenciador para respeitar o registro mínimo especificado pelo usuário nível (Config.LogLevel).

Encobrimento de PII

Como a maioria dos fluxos de IA generativa começa com algum tipo de entrada do usuário, é possibilidade provável de que alguns rastreamentos de fluxo contenham informações (PII). Para proteger seus é necessário encobrir as PII dos rastros antes de exportá-los.

Se você estiver criando seu próprio exportador de períodos, poderá criar essa funcionalidade nele.

Se você estiver criando um plug-in com base em uma integração atual do OpenTelemetry, pode unir o exportador de períodos fornecido com um exportador personalizado que realiza essa tarefa. Por exemplo, o plug-in googlecloud remove o genkit:input e Atributos genkit:output de cada período antes de exportá-los usando um wrapper semelhante a:

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
}

Solução de problemas

Se você estiver com problemas para fazer com que os dados apareçam no lugar esperado, o OpenTelemetry oferece uma ferramenta de diagnóstico útil. que ajuda a localizar a origem do problema.