package metrics import ( "context" "fmt" "net" "net/http" "time" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/prometheus" otelmetric "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" "github.com/kanootoko/codevault/internal" "github.com/kanootoko/codevault/internal/config" "github.com/kanootoko/codevault/internal/observability" "github.com/kanootoko/codevault/internal/utils/gshutdown" ) // Configure starts a Prometheus server which serves metrics and finishes on context finish func Configure(ctx context.Context, cfg config.MetricsConfig) error { if cfg.Host == "" { return nil } res, err := observability.GetOtelResource() if err != nil { return fmt.Errorf("cannot get OpenTelemetry resource: %w", err) } meterProvider, err := newMeterProvider(res) if err != nil { return fmt.Errorf("cannot instanciate OpenTelemetry meter provider: %w", err) } otel.SetMeterProvider(meterProvider) mux := http.NewServeMux() mux.Handle("/metrics", promhttp.Handler()) server := http.Server{ Addr: fmt.Sprintf(":%d", cfg.Port), Handler: mux, ReadTimeout: time.Second * 20, ReadHeaderTimeout: time.Second * 10, WriteTimeout: time.Second * 20, } listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)) if err != nil { return fmt.Errorf("error on creating listener for prometheus metrics: %w", err) } go func() { _ = server.Serve(listener) }() _ = gshutdown.SubtaskStarted(ctx, "prometheus") go func() { defer func() { _ = listener.Close() _ = gshutdown.SubtaskFinished(ctx, "prometheus") }() <-ctx.Done() _ = server.Shutdown(context.Background()) }() return nil } func GetMeter() otelmetric.Meter { mp := otel.GetMeterProvider() return mp.Meter(internal.PackageName, otelmetric.WithInstrumentationVersion(internal.Version)) } func newMeterProvider(res *resource.Resource) (*metric.MeterProvider, error) { metricExporter, err := prometheus.New() if err != nil { return nil, err } meterProvider := metric.NewMeterProvider( metric.WithResource(res), metric.WithReader(metricExporter), ) return meterProvider, nil }