pomerium/config/metrics.go
Caleb Doxsey d2c14cd6d2
logging: remove ctx from global log methods (#5337)
* log: remove warn

* log: update debug

* log: update info

* remove level, log

* remove contextLogger function
2024-10-23 14:18:52 -06:00

135 lines
3.5 KiB
Go

package config
import (
"context"
"net/http"
"net/url"
"os"
"reflect"
"sync"
"time"
"github.com/rs/zerolog"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/middleware"
"github.com/pomerium/pomerium/internal/telemetry"
"github.com/pomerium/pomerium/internal/telemetry/metrics"
)
const (
// defaultMetricsTimeout sets max time to collect and send aggregate pomerium metrics
defaultMetricsTimeout = time.Second * 30
)
// A MetricsManager manages metrics for a given configuration.
type MetricsManager struct {
mu sync.RWMutex
installationID string
serviceName string
addr string
basicAuth string
handler http.Handler
endpoints []MetricsScrapeEndpoint
}
// NewMetricsManager creates a new MetricsManager.
func NewMetricsManager(ctx context.Context, src Source) *MetricsManager {
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
return c.Str("service", "metrics_manager")
})
mgr := &MetricsManager{}
metrics.RegisterInfoMetrics()
src.OnConfigChange(ctx, mgr.OnConfigChange)
mgr.OnConfigChange(ctx, src.GetConfig())
return mgr
}
// Close closes any underlying http server.
func (mgr *MetricsManager) Close() error {
return nil
}
// OnConfigChange updates the metrics manager when configuration is changed.
func (mgr *MetricsManager) OnConfigChange(ctx context.Context, cfg *Config) {
mgr.mu.Lock()
defer mgr.mu.Unlock()
mgr.updateInfo(ctx, cfg)
mgr.updateServer(ctx, cfg)
}
func (mgr *MetricsManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mgr.mu.RLock()
defer mgr.mu.RUnlock()
if mgr.handler == nil {
http.NotFound(w, r)
return
}
mgr.handler.ServeHTTP(w, r)
}
func (mgr *MetricsManager) updateInfo(ctx context.Context, cfg *Config) {
serviceName := telemetry.ServiceName(cfg.Options.Services)
if serviceName == mgr.serviceName {
return
}
hostname, err := os.Hostname()
if err != nil {
log.Ctx(ctx).Error().Err(err).Msg("telemetry/metrics: failed to get OS hostname")
hostname = "__unknown__"
}
metrics.SetBuildInfo(serviceName, hostname, cfg.EnvoyVersion)
mgr.serviceName = serviceName
}
func (mgr *MetricsManager) updateServer(ctx context.Context, cfg *Config) {
if mgr.configUnchanged(cfg) {
return
}
mgr.addr = cfg.Options.MetricsAddr
mgr.basicAuth = cfg.Options.MetricsBasicAuth
mgr.installationID = cfg.Options.InstallationID
mgr.handler = nil
if mgr.addr == "" {
log.Ctx(ctx).Info().Msg("metrics: http server disabled")
return
}
mgr.endpoints = append(cfg.MetricsScrapeEndpoints,
MetricsScrapeEndpoint{
Name: "envoy",
URL: url.URL{Scheme: "http", Host: cfg.Options.MetricsAddr, Path: "/metrics/envoy"},
})
handler, err := metrics.PrometheusHandler(toInternalEndpoints(mgr.endpoints), mgr.installationID, defaultMetricsTimeout)
if err != nil {
log.Ctx(ctx).Error().Err(err).Msg("metrics: failed to create prometheus handler")
return
}
if username, password, ok := cfg.Options.GetMetricsBasicAuth(); ok {
handler = middleware.RequireBasicAuth(username, password)(handler)
}
mgr.handler = handler
}
func (mgr *MetricsManager) configUnchanged(cfg *Config) bool {
return cfg.Options.MetricsAddr == mgr.addr &&
cfg.Options.MetricsBasicAuth == mgr.basicAuth &&
cfg.Options.InstallationID == mgr.installationID &&
reflect.DeepEqual(mgr.endpoints, cfg.MetricsScrapeEndpoints)
}
func toInternalEndpoints(src []MetricsScrapeEndpoint) []metrics.ScrapeEndpoint {
dst := make([]metrics.ScrapeEndpoint, 0, len(src))
for _, e := range src {
dst = append(dst, metrics.ScrapeEndpoint(e))
}
return dst
}