编写 Genkit 遥测插件

Firebase Genkit 库使用 OpenTelemetry 进行了插桩 来支持收集跟踪记录、指标和日志。Genkit 用户可以导出 将遥测数据传输到监控和可视化工具 配置 OpenTelemetry Go SDK 导出到支持 OpenTelemetry 的特定系统。

Genkit 包含一个插件,用于将 OpenTelemetry 配置为将数据导出到 Google Cloud Monitoring 和 Cloud Logging。为了支持 其他监控系统,您可以通过编写遥测插件来扩展 Genkit, 如本页所述。

准备工作

如需了解如何编写 Genkit 插件,请参阅编写 Genkit 插件 任何类型的 Genkit 插件,包括遥测插件。需要特别注意的是 每个插件都必须导出一个 Init 函数,用户应调用该函数 然后再使用该插件

导出器和日志记录器

如前所述,遥测插件的主要任务是 OpenTelemetry(Genkit 已经插桩)用于导出数据 特定服务为此,您需要具备以下条件:

  • OpenTelemetry 的 SpanExporter 的实现 将数据导出到您选择的服务的界面。
  • OpenTelemetry 的 metric.Exporter 的实现 将数据导出到您选择的服务的界面。
  • slog.Logger 或者 slog.Handler 的实现 界面,用于将日志导出到您选择的服务。

根据要导出到的服务,这可能是 投入相对较小的工作量或很大的工作量。

由于 OpenTelemetry 是一种行业标准,因此许多监控服务 具有实现这些接口的库。例如,googlecloud Genkit 插件会利用 opentelemetry-operations-go 由 Google Cloud 团队维护。 同样,许多监控服务都提供了实现 标准的 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
    }
    
  • 初始化轨迹 span 导出器,并向 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)
    

    在以下情况下,使用用户配置的收集间隔 (Config.MetricInterval) 初始化 PeriodicReader

  • slog 处理程序注册为默认日志记录器:

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

    您应该将处理程序配置为遵循用户指定的最小日志 级别 (Config.LogLevel)。

个人身份信息隐去

大多数生成式 AI 流程都是从某种类型的用户输入开始, 某些用户流跟踪记录包含个人身份信息 个人身份信息 (PII) 的。为了保护您用户的则应隐去个人身份信息 然后再导出跟踪记录。

如果您要构建自己的 span 导出器, 进入其中。

如果您围绕现有 OpenTelemetry 集成构建插件, 可以使用执行此任务的自定义导出器来封装提供的 span 导出器 任务。例如,googlecloud 插件会移除 genkit:input 和 在使用封装容器导出每个 span 中的 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 一款实用的诊断工具 以便找出问题根源