A comprehensive OpenTelemetry plugin for Firebase Genkit Go that provides out-of-the-box trace spans, metrics collectors, and logs with the flexibility to bring your own exporters.
- 🚀 Out-of-the-box setup with sensible defaults
- 📊 Multiple exporters - OTLP, Prometheus, Jaeger, Console, and more
- ⚙️ Flexible configuration - use presets or bring your own exporters
- 🔄 Environment-aware - different behavior for dev vs production
- 📈 Comprehensive telemetry - traces, metrics, and structured logs
go get github.com/xavidop/genkit-opentelemetry-go
The simplest way to get started is with the default OTLP configuration:
package main
import (
"context"
"log"
"github.com/firebase/genkit/go/genkit"
opentelemetry "github.com/xavidop/genkit-opentelemetry-go"
)
func main() {
ctx := context.Background()
// Initialize OpenTelemetry plugin with default settings
plugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-genkit-app",
ForceExport: true, // Export even in development
})
// Initialize Genkit
genkit.Init(ctx,
genkit.WithPlugins(plugin),
)
// Your Genkit flows will now automatically emit telemetry!
}
For development, use the console preset to see telemetry data in your terminal:
otelPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetConsole, opentelemetry.Config{
ServiceName: "my-app-dev",
LogLevel: slog.LevelDebug,
ForceExport: true, // Export even in dev environment
})
jaegerPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetJaeger, opentelemetry.Config{
ServiceName: "my-genkit-app",
ForceExport: true,
})
plugin := opentelemetry.NewWithPreset(opentelemetry.PresetPrometheus, opentelemetry.Config{
ServiceName: "my-genkit-app",
ForceExport: true,
MetricInterval: 15 * time.Second,
PrometheusPort: 8081, // Custom port for the /metrics endpoint
})
plugin := opentelemetry.NewWithPreset(opentelemetry.PresetOTLP, opentelemetry.Config{
ServiceName: "my-genkit-app",
ForceExport: true,
MetricInterval: 15 * time.Second,
})
otelPlugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-app",
OTLPEndpoint: "https://api.your-provider.com",
OTLPUseHTTP: true,
OTLPHeaders: map[string]string{
"authorization": "Bearer your-token",
},
})
otelPlugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-app",
OTLPEndpoint: "https://api.honeycomb.io",
OTLPUseHTTP: true,
OTLPHeaders: map[string]string{
"x-honeycomb-team": os.Getenv("HONEYCOMB_API_KEY"),
},
})
otelPlugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-app",
OTLPEndpoint: "https://trace.agent.datadoghq.com",
OTLPUseHTTP: true,
OTLPHeaders: map[string]string{
"dd-api-key": os.Getenv("DD_API_KEY"),
},
})
otelPlugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-app",
OTLPEndpoint: "https://otlp.nr-data.net",
OTLPUseHTTP: true,
OTLPHeaders: map[string]string{
"api-key": os.Getenv("NEW_RELIC_LICENSE_KEY"),
},
})
# Clone the repository
git clone https://github.com/xavidop/genkit-opentelemetry-go
cd genkit-opentelemetry-go
# Start Jaeger, Prometheus, and Grafana
docker-compose up -d
# Use the OTLP collector with gRPC (default)
OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run your-app.go
# Use the OTLP collector with HTTP
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 go run your-app.go
# Or run the provided examples
go run examples/basic/main.go
go run examples/jaeger/main.go
go run examples/prometheus/main.go
go run examples/otel/main.go
- Jaeger UI: http://localhost:16686 (traces)
- Prometheus: http://localhost:9090 (metrics)
- Grafana: http://localhost:3000 (dashboards, admin/admin)
The plugin respects standard OpenTelemetry environment variables:
# OTLP Endpoint - for gRPC (default), use host:port format
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
# OTLP Endpoint - for HTTP, use full URL format
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# Service name
export OTEL_SERVICE_NAME=my-genkit-app
# Headers
export OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer-token
# Protocol (grpc or http/protobuf)
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
# Special endpoints for stdout
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=stdout
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=stdout
type Config struct {
// Export even in the dev environment
ForceExport bool
// The interval for exporting metric data (default: 60s)
MetricInterval time.Duration
// The minimum level at which logs will be written (default: Info)
LogLevel slog.Leveler
// Custom trace span exporter (optional)
TraceExporter trace.SpanExporter
// Custom metric exporter (optional)
MetricExporter metric.Exporter
// Custom log handler (optional)
LogHandler slog.Handler
// OTLP endpoint (default: "localhost:4317")
OTLPEndpoint string
// Whether to use HTTP instead of gRPC for OTLP (default: false)
OTLPUseHTTP bool
// Headers to include in OTLP requests
OTLPHeaders map[string]string
// Service name for telemetry data (default: "genkit-service")
ServiceName string
// Service version for telemetry data
ServiceVersion string
// Additional resource attributes
ResourceAttributes map[string]string
}
The plugin comes with several presets for common scenarios:
otelPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetOTLP)
otelPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetJaeger, opentelemetry.Config{
OTLPEndpoint: "http://localhost:14268/api/traces",
})
otelPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetPrometheus)
otelPlugin := opentelemetry.NewWithPreset(opentelemetry.PresetConsole, opentelemetry.Config{
LogLevel: slog.LevelDebug,
})
When you use this plugin with Genkit, you automatically get:
- Traces for all Genkit flows and actions
- Metrics for flow execution times, success/failure rates
- Logs with proper correlation to traces
- Custom attributes for Genkit-specific metadata
You can add your own traces and metrics:
import "go.opentelemetry.io/otel"
// Get a tracer
tracer := otel.Tracer("my-component")
// Create a span
ctx, span := tracer.Start(ctx, "my-operation")
defer span.End()
// Add attributes
span.SetAttributes(
attribute.String("user.id", userID),
attribute.Int("batch.size", batchSize),
)
// Get a meter for custom metrics
meter := otel.Meter("my-component")
counter, _ := meter.Int64Counter("requests_total")
counter.Add(ctx, 1, metric.WithAttributes(
attribute.String("method", "POST"),
))
- Check if
ForceExport: true
is set in development - Verify the OTLP endpoint is correct
- Check network connectivity to your telemetry backend
- Look for error logs in your application
- Reduce the
MetricInterval
for more frequent exports - Consider using sampling for traces in high-traffic applications
- Check if your telemetry backend is accepting data
- Check if your telemetry backend has ingestion limits
- Verify the service name matches your expectations
- Check the examples directory
- Review the main documentation
- Look at the test files for usage patterns
- Open an issue if you find bugs or need features
You can bring your own exporters:
import (
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
)
// Create custom exporters
traceExporter, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
metricExporter, _ := stdoutmetric.New(stdoutmetric.WithPrettyPrint())
otelPlugin := opentelemetry.New(opentelemetry.Config{
ServiceName: "my-app",
TraceExporter: traceExporter,
MetricExporter: metricExporter,
ForceExport: true,
})
Check out the CONTRIBUTING.md file for details on how to contribute to this project.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
- Firebase Genkit - AI application framework
- OpenTelemetry Go - Observability framework
- Genkit Google Cloud Plugin - Official Google Cloud telemetry plugin