mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:19:44 +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)
 | |
| 	}
 | |
| }
 | 
