Files
metadata-agregator/internal/metrics/metrics.go
T
2026-05-07 16:50:40 +02:00

116 lines
3.4 KiB
Go

package metrics
import (
"context"
"fmt"
"net/http"
"time"
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
)
var (
CacheHits = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "metadata_cache_hits_total",
Help: "Number of cache hits by entity type",
}, []string{"entity"})
CacheMisses = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "metadata_cache_misses_total",
Help: "Number of cache misses by entity type",
}, []string{"entity"})
ProviderRequests = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "metadata_provider_requests_total",
Help: "Number of requests to external providers",
}, []string{"provider", "operation", "status"})
ProviderLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "metadata_provider_request_duration_seconds",
Help: "Latency of external provider requests",
Buckets: []float64{0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
}, []string{"provider", "operation"})
DBQueryLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "metadata_db_query_duration_seconds",
Help: "Latency of database queries",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
}, []string{"operation"})
)
type ServerMetrics struct {
grpcMetrics *grpcprom.ServerMetrics
registry *prometheus.Registry
}
func NewServerMetrics() *ServerMetrics {
reg := prometheus.NewRegistry()
reg.MustRegister(prometheus.NewGoCollector())
reg.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
srvMetrics := grpcprom.NewServerMetrics(
grpcprom.WithServerHandlingTimeHistogram(
grpcprom.WithHistogramBuckets([]float64{
0.001, 0.01, 0.05, 0.1, 0.3, 0.6, 1, 3, 6, 10, 30,
}),
),
)
reg.MustRegister(srvMetrics)
reg.MustRegister(CacheHits)
reg.MustRegister(CacheMisses)
reg.MustRegister(ProviderRequests)
reg.MustRegister(ProviderLatency)
reg.MustRegister(DBQueryLatency)
return &ServerMetrics{
grpcMetrics: srvMetrics,
registry: reg,
}
}
func (m *ServerMetrics) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return m.grpcMetrics.UnaryServerInterceptor()
}
func (m *ServerMetrics) StreamServerInterceptor() grpc.StreamServerInterceptor {
return m.grpcMetrics.StreamServerInterceptor()
}
func (m *ServerMetrics) InitializeMetrics(srv *grpc.Server) {
m.grpcMetrics.InitializeMetrics(srv)
}
func (m *ServerMetrics) StartHTTPServer(ctx context.Context, port int) {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{
EnableOpenMetrics: true,
}))
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("OK"))
})
srv := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
}()
log.Info().Int("port", port).Msg("metrics HTTP server starting")
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error().Err(err).Msg("metrics HTTP server failed")
}
}