mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 10:12:29 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			100 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package reverseproxy
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"runtime/debug"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/prometheus/client_golang/prometheus"
 | 
						|
	"go.uber.org/zap"
 | 
						|
	"go.uber.org/zap/zapcore"
 | 
						|
 | 
						|
	"github.com/caddyserver/caddy/v2"
 | 
						|
)
 | 
						|
 | 
						|
var reverseProxyMetrics = struct {
 | 
						|
	once             sync.Once
 | 
						|
	upstreamsHealthy *prometheus.GaugeVec
 | 
						|
	logger           *zap.Logger
 | 
						|
}{}
 | 
						|
 | 
						|
func initReverseProxyMetrics(handler *Handler, registry *prometheus.Registry) {
 | 
						|
	const ns, sub = "caddy", "reverse_proxy"
 | 
						|
 | 
						|
	upstreamsLabels := []string{"upstream"}
 | 
						|
	reverseProxyMetrics.once.Do(func() {
 | 
						|
		reverseProxyMetrics.upstreamsHealthy = prometheus.NewGaugeVec(prometheus.GaugeOpts{
 | 
						|
			Namespace: ns,
 | 
						|
			Subsystem: sub,
 | 
						|
			Name:      "upstreams_healthy",
 | 
						|
			Help:      "Health status of reverse proxy upstreams.",
 | 
						|
		}, upstreamsLabels)
 | 
						|
	})
 | 
						|
 | 
						|
	// duplicate registration could happen if multiple sites with reverse proxy are configured; so ignore the error because
 | 
						|
	// there's no good way to capture having multiple sites with reverse proxy. If this happens, the metrics will be
 | 
						|
	// registered twice, but the second registration will be ignored.
 | 
						|
	if err := registry.Register(reverseProxyMetrics.upstreamsHealthy); err != nil &&
 | 
						|
		!errors.Is(err, prometheus.AlreadyRegisteredError{
 | 
						|
			ExistingCollector: reverseProxyMetrics.upstreamsHealthy,
 | 
						|
			NewCollector:      reverseProxyMetrics.upstreamsHealthy,
 | 
						|
		}) {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics")
 | 
						|
}
 | 
						|
 | 
						|
type metricsUpstreamsHealthyUpdater struct {
 | 
						|
	handler *Handler
 | 
						|
}
 | 
						|
 | 
						|
func newMetricsUpstreamsHealthyUpdater(handler *Handler, ctx caddy.Context) *metricsUpstreamsHealthyUpdater {
 | 
						|
	initReverseProxyMetrics(handler, ctx.GetMetricsRegistry())
 | 
						|
	reverseProxyMetrics.upstreamsHealthy.Reset()
 | 
						|
 | 
						|
	return &metricsUpstreamsHealthyUpdater{handler}
 | 
						|
}
 | 
						|
 | 
						|
func (m *metricsUpstreamsHealthyUpdater) init() {
 | 
						|
	go func() {
 | 
						|
		defer func() {
 | 
						|
			if err := recover(); err != nil {
 | 
						|
				if c := reverseProxyMetrics.logger.Check(zapcore.ErrorLevel, "upstreams healthy metrics updater panicked"); c != nil {
 | 
						|
					c.Write(
 | 
						|
						zap.Any("error", err),
 | 
						|
						zap.ByteString("stack", debug.Stack()),
 | 
						|
					)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}()
 | 
						|
 | 
						|
		m.update()
 | 
						|
 | 
						|
		ticker := time.NewTicker(10 * time.Second)
 | 
						|
		for {
 | 
						|
			select {
 | 
						|
			case <-ticker.C:
 | 
						|
				m.update()
 | 
						|
			case <-m.handler.ctx.Done():
 | 
						|
				ticker.Stop()
 | 
						|
				return
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}()
 | 
						|
}
 | 
						|
 | 
						|
func (m *metricsUpstreamsHealthyUpdater) update() {
 | 
						|
	for _, upstream := range m.handler.Upstreams {
 | 
						|
		labels := prometheus.Labels{"upstream": upstream.Dial}
 | 
						|
 | 
						|
		gaugeValue := 0.0
 | 
						|
		if upstream.Healthy() {
 | 
						|
			gaugeValue = 1.0
 | 
						|
		}
 | 
						|
 | 
						|
		reverseProxyMetrics.upstreamsHealthy.With(labels).Set(gaugeValue)
 | 
						|
	}
 | 
						|
}
 |