@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 178 lines 5.77 KB
Raw
1 package reverseproxy
2
3 import (
4 "errors"
5 "runtime/debug"
6 "sync"
7 "time"
8
9 "github.com/prometheus/client_golang/prometheus"
10 "go.uber.org/zap"
11 "go.uber.org/zap/zapcore"
12
13 "github.com/caddyserver/caddy/v2"
14 )
15
16 var reverseProxyMetrics = struct {
17 once sync.Once
18 upstreamsHealthy *prometheus.GaugeVec
19 streamsActive *prometheus.GaugeVec
20 streamsTotal *prometheus.CounterVec
21 streamDuration *prometheus.HistogramVec
22 streamBytes *prometheus.CounterVec
23 logger *zap.Logger
24 }{}
25
26 func initReverseProxyMetrics(handler *Handler, registry *prometheus.Registry) {
27 const ns, sub = "caddy", "reverse_proxy"
28
29 upstreamsLabels := []string{"upstream"}
30 streamResultLabels := []string{"upstream", "result"}
31 streamBytesLabels := []string{"upstream", "direction"}
32 reverseProxyMetrics.once.Do(func() {
33 reverseProxyMetrics.upstreamsHealthy = prometheus.NewGaugeVec(prometheus.GaugeOpts{
34 Namespace: ns,
35 Subsystem: sub,
36 Name: "upstreams_healthy",
37 Help: "Health status of reverse proxy upstreams.",
38 }, upstreamsLabels)
39 reverseProxyMetrics.streamsActive = prometheus.NewGaugeVec(prometheus.GaugeOpts{
40 Namespace: ns,
41 Subsystem: sub,
42 Name: "streams_active",
43 Help: "Number of currently active upgraded reverse proxy streams.",
44 }, upstreamsLabels)
45 reverseProxyMetrics.streamsTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
46 Namespace: ns,
47 Subsystem: sub,
48 Name: "streams_total",
49 Help: "Total number of upgraded reverse proxy streams by close result.",
50 }, streamResultLabels)
51 reverseProxyMetrics.streamDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
52 Namespace: ns,
53 Subsystem: sub,
54 Name: "stream_duration_seconds",
55 Help: "Duration of upgraded reverse proxy streams by close result.",
56 Buckets: prometheus.DefBuckets,
57 }, streamResultLabels)
58 reverseProxyMetrics.streamBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
59 Namespace: ns,
60 Subsystem: sub,
61 Name: "stream_bytes_total",
62 Help: "Total bytes proxied across upgraded reverse proxy streams.",
63 }, streamBytesLabels)
64 })
65
66 // duplicate registration could happen if multiple sites with reverse proxy are configured; so ignore the error because
67 // there's no good way to capture having multiple sites with reverse proxy. If this happens, the metrics will be
68 // registered twice, but the second registration will be ignored.
69 if err := registry.Register(reverseProxyMetrics.upstreamsHealthy); err != nil &&
70 !errors.Is(err, prometheus.AlreadyRegisteredError{
71 ExistingCollector: reverseProxyMetrics.upstreamsHealthy,
72 NewCollector: reverseProxyMetrics.upstreamsHealthy,
73 }) {
74 panic(err)
75 }
76 if err := registry.Register(reverseProxyMetrics.streamsActive); err != nil &&
77 !errors.Is(err, prometheus.AlreadyRegisteredError{
78 ExistingCollector: reverseProxyMetrics.streamsActive,
79 NewCollector: reverseProxyMetrics.streamsActive,
80 }) {
81 panic(err)
82 }
83 if err := registry.Register(reverseProxyMetrics.streamsTotal); err != nil &&
84 !errors.Is(err, prometheus.AlreadyRegisteredError{
85 ExistingCollector: reverseProxyMetrics.streamsTotal,
86 NewCollector: reverseProxyMetrics.streamsTotal,
87 }) {
88 panic(err)
89 }
90 if err := registry.Register(reverseProxyMetrics.streamDuration); err != nil &&
91 !errors.Is(err, prometheus.AlreadyRegisteredError{
92 ExistingCollector: reverseProxyMetrics.streamDuration,
93 NewCollector: reverseProxyMetrics.streamDuration,
94 }) {
95 panic(err)
96 }
97 if err := registry.Register(reverseProxyMetrics.streamBytes); err != nil &&
98 !errors.Is(err, prometheus.AlreadyRegisteredError{
99 ExistingCollector: reverseProxyMetrics.streamBytes,
100 NewCollector: reverseProxyMetrics.streamBytes,
101 }) {
102 panic(err)
103 }
104
105 reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics")
106 }
107
108 func trackActiveStream(upstream string) func(result string, duration time.Duration, toBackend, fromBackend int64) {
109 labels := prometheus.Labels{"upstream": upstream}
110 reverseProxyMetrics.streamsActive.With(labels).Inc()
111
112 var once sync.Once
113 return func(result string, duration time.Duration, toBackend, fromBackend int64) {
114 once.Do(func() {
115 reverseProxyMetrics.streamsActive.With(labels).Dec()
116 reverseProxyMetrics.streamsTotal.WithLabelValues(upstream, result).Inc()
117 reverseProxyMetrics.streamDuration.WithLabelValues(upstream, result).Observe(duration.Seconds())
118 if toBackend > 0 {
119 reverseProxyMetrics.streamBytes.WithLabelValues(upstream, "to_upstream").Add(float64(toBackend))
120 }
121 if fromBackend > 0 {
122 reverseProxyMetrics.streamBytes.WithLabelValues(upstream, "from_upstream").Add(float64(fromBackend))
123 }
124 })
125 }
126 }
127
128 type metricsUpstreamsHealthyUpdater struct {
129 handler *Handler
130 }
131
132 func newMetricsUpstreamsHealthyUpdater(handler *Handler, ctx caddy.Context) *metricsUpstreamsHealthyUpdater {
133 initReverseProxyMetrics(handler, ctx.GetMetricsRegistry())
134 reverseProxyMetrics.upstreamsHealthy.Reset()
135
136 return &metricsUpstreamsHealthyUpdater{handler}
137 }
138
139 func (m *metricsUpstreamsHealthyUpdater) init() {
140 go func() {
141 defer func() {
142 if err := recover(); err != nil {
143 if c := reverseProxyMetrics.logger.Check(zapcore.ErrorLevel, "upstreams healthy metrics updater panicked"); c != nil {
144 c.Write(
145 zap.Any("error", err),
146 zap.ByteString("stack", debug.Stack()),
147 )
148 }
149 }
150 }()
151
152 m.update()
153
154 ticker := time.NewTicker(10 * time.Second)
155 for {
156 select {
157 case <-ticker.C:
158 m.update()
159 case <-m.handler.ctx.Done():
160 ticker.Stop()
161 return
162 }
163 }
164 }()
165 }
166
167 func (m *metricsUpstreamsHealthyUpdater) update() {
168 for _, upstream := range m.handler.Upstreams {
169 labels := prometheus.Labels{"upstream": upstream.Dial}
170
171 gaugeValue := 0.0
172 if upstream.Healthy() {
173 gaugeValue = 1.0
174 }
175
176 reverseProxyMetrics.upstreamsHealthy.With(labels).Set(gaugeValue)
177 }
178 }