- Create new metrics/ package for OpenTelemetry-native metrics with OTLP export - Refactor OTLP configuration to internal/tracerconfig/ to eliminate code duplication - Add consistent retry configuration across all HTTP OTLP exporters - Add configuration validation and improved error messages - Include test coverage for all new functionality - Make OpenTelemetry metrics dependencies explicit in go.mod Designed for new applications requiring structured metrics export to observability backends via OTLP protocol.
123 lines
3.9 KiB
Go
123 lines
3.9 KiB
Go
// Package metrics provides OpenTelemetry-native metrics with OTLP export support.
|
|
//
|
|
// This package implements a metrics system using the OpenTelemetry metrics data model
|
|
// with OTLP export capabilities. It's designed for new applications that want to use
|
|
// structured metrics export to observability backends.
|
|
//
|
|
// Key features:
|
|
// - OpenTelemetry native metric types (Counter, Histogram, Gauge, etc.)
|
|
// - OTLP export for sending metrics to observability backends
|
|
// - Resource detection and correlation with traces/logs
|
|
// - Graceful handling when OTLP configuration is not available
|
|
//
|
|
// Example usage:
|
|
//
|
|
// // Initialize metrics along with tracing
|
|
// shutdown, err := tracing.InitTracer(ctx, cfg)
|
|
// if err != nil {
|
|
// log.Fatal(err)
|
|
// }
|
|
// defer shutdown(ctx)
|
|
//
|
|
// // Get a meter and create instruments
|
|
// meter := metrics.GetMeter("my-service")
|
|
// counter, _ := meter.Int64Counter("requests_total")
|
|
// counter.Add(ctx, 1, metric.WithAttributes(attribute.String("method", "GET")))
|
|
package metrics
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.ntppool.org/common/internal/tracerconfig"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/metric"
|
|
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
|
)
|
|
|
|
var (
|
|
meterProvider metric.MeterProvider
|
|
setupOnce sync.Once
|
|
setupErr error
|
|
)
|
|
|
|
// Setup initializes the OpenTelemetry metrics provider with OTLP export.
|
|
// This function uses the configuration stored by the tracing package and
|
|
// creates a metrics provider that exports to the same OTLP endpoint.
|
|
//
|
|
// The function is safe to call multiple times - it will only initialize once.
|
|
// If tracing configuration is not available, it returns a no-op provider that
|
|
// doesn't export metrics.
|
|
//
|
|
// Returns an error only if there's a configuration problem. Missing tracing
|
|
// configuration is handled gracefully with a warning log.
|
|
func Setup(ctx context.Context) error {
|
|
setupOnce.Do(func() {
|
|
setupErr = initializeMetrics(ctx)
|
|
})
|
|
return setupErr
|
|
}
|
|
|
|
// GetMeter returns a named meter for creating metric instruments.
|
|
// The meter uses the configured metrics provider, or the global provider
|
|
// if metrics haven't been set up yet.
|
|
//
|
|
// This is the primary entry point for creating metric instruments in your application.
|
|
func GetMeter(name string, opts ...metric.MeterOption) metric.Meter {
|
|
if meterProvider == nil {
|
|
// Return the global provider as fallback (no-op if not configured)
|
|
return otel.GetMeterProvider().Meter(name, opts...)
|
|
}
|
|
return meterProvider.Meter(name, opts...)
|
|
}
|
|
|
|
// initializeMetrics sets up the OpenTelemetry metrics provider with OTLP export.
|
|
func initializeMetrics(ctx context.Context) error {
|
|
log := slog.Default()
|
|
|
|
// Check if tracing configuration is available
|
|
cfg, configCtx, factory := tracerconfig.GetMetricExporter()
|
|
if cfg == nil || configCtx == nil || factory == nil {
|
|
log.Warn("metrics setup: tracing configuration not available, using no-op provider")
|
|
// Set the global provider as fallback - metrics just won't be exported
|
|
meterProvider = otel.GetMeterProvider()
|
|
return nil
|
|
}
|
|
|
|
// Create OTLP metrics exporter
|
|
exporter, err := factory(ctx, cfg)
|
|
if err != nil {
|
|
log.Error("metrics setup: failed to create OTLP exporter", "error", err)
|
|
// Fall back to global provider
|
|
meterProvider = otel.GetMeterProvider()
|
|
return nil
|
|
}
|
|
|
|
// Create metrics provider with the exporter
|
|
provider := sdkmetric.NewMeterProvider(
|
|
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(
|
|
exporter,
|
|
sdkmetric.WithInterval(15*time.Second),
|
|
)),
|
|
)
|
|
|
|
// Set the global provider
|
|
otel.SetMeterProvider(provider)
|
|
meterProvider = provider
|
|
|
|
log.Info("metrics setup: OTLP metrics provider initialized")
|
|
return nil
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the metrics provider.
|
|
// This should be called during application shutdown to ensure all metrics
|
|
// are properly flushed and exported.
|
|
func Shutdown(ctx context.Context) error {
|
|
if provider, ok := meterProvider.(*sdkmetric.MeterProvider); ok {
|
|
return provider.Shutdown(ctx)
|
|
}
|
|
return nil
|
|
}
|