@marcel91200 / caddy Mirror · read-only / commits / 388c7e89
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 12, 07:00 UTC

metrics: move `metrics` up, outside `servers` (#6606)

* metrics: move `metrics` up, outside `servers` This change moves the metrics configuration from per-server level to a single config knob within the `http` app. Enabling `metrics` in any of the configured servers inside `http` enables metrics for all servers. Fix #6604 Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com> * normalize domain name --------- Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

Mohammed Al Sahaf committed Oct 18, 2024 at 18:54 UTC 388c7e898c6cbcddd2c59e8a902238a0b4f06857
8 files changed +102 -13
caddyconfig/httpcaddyfile/httptype.go
+14
@@ -15,6 +15,7 @@
15 package httpcaddyfile
16
17 import (
18 + "cmp"
19 "encoding/json"
20 "fmt"
21 "net"
@@ -186,12 +187,25 @@ func (st ServerType) Setup(
187 return nil, warnings, err
188 }
189
190 + // hoist the metrics config from per-server to global
191 + metrics, _ := options["metrics"].(*caddyhttp.Metrics)
192 + for _, s := range servers {
193 + if s.Metrics != nil {
194 + metrics = cmp.Or[*caddyhttp.Metrics](metrics, &caddyhttp.Metrics{})
195 + metrics = &caddyhttp.Metrics{
196 + PerHost: metrics.PerHost || s.Metrics.PerHost,
197 + }
198 + s.Metrics = nil // we don't need it anymore
199 + }
200 + }
201 +
202 // now that each server is configured, make the HTTP app
203 httpApp := caddyhttp.App{
204 HTTPPort: tryInt(options["http_port"], &warnings),
205 HTTPSPort: tryInt(options["https_port"], &warnings),
206 GracePeriod: tryDuration(options["grace_period"], &warnings),
207 ShutdownDelay: tryDuration(options["shutdown_delay"], &warnings),
208 + Metrics: metrics,
209 Servers: servers,
210 }
211
caddyconfig/httpcaddyfile/options.go
+20
@@ -24,6 +24,7 @@ import (
24 "github.com/caddyserver/caddy/v2"
25 "github.com/caddyserver/caddy/v2/caddyconfig"
26 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
27 + "github.com/caddyserver/caddy/v2/modules/caddyhttp"
28 "github.com/caddyserver/caddy/v2/modules/caddytls"
29 )
30
@@ -53,6 +54,7 @@ func init() {
54 RegisterGlobalOption("local_certs", parseOptTrue)
55 RegisterGlobalOption("key_type", parseOptSingleString)
56 RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
57 + RegisterGlobalOption("metrics", parseMetricsOptions)
58 RegisterGlobalOption("servers", parseServerOptions)
59 RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
60 RegisterGlobalOption("cert_lifetime", parseOptDuration)
@@ -446,6 +448,24 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
448 return val, nil
449 }
450
451 +func unmarshalCaddyfileMetricsOptions(d *caddyfile.Dispenser) (any, error) {
452 + d.Next() // consume option name
453 + metrics := new(caddyhttp.Metrics)
454 + for d.NextBlock(0) {
455 + switch d.Val() {
456 + case "per_host":
457 + metrics.PerHost = true
458 + default:
459 + return nil, d.Errf("unrecognized servers option '%s'", d.Val())
460 + }
461 + }
462 + return metrics, nil
463 +}
464 +
465 +func parseMetricsOptions(d *caddyfile.Dispenser, _ any) (any, error) {
466 + return unmarshalCaddyfileMetricsOptions(d)
467 +}
468 +
469 func parseServerOptions(d *caddyfile.Dispenser, _ any) (any, error) {
470 return unmarshalCaddyfileServerOptions(d)
471 }
caddyconfig/httpcaddyfile/serveroptions.go
+1
@@ -240,6 +240,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
240 }
241
242 case "metrics":
243 + caddy.Log().Warn("The nested 'metrics' option inside `servers` is deprecated and will be removed in the next major version. Use the global 'metrics' option instead.")
244 serverOpts.Metrics = new(caddyhttp.Metrics)
245 for nesting := d.Nesting(); d.NextBlock(nesting); {
246 switch d.Val() {
caddytest/integration/caddyfile_adapt/metrics_merge_options.caddyfiletest new
+39
@@ -0,0 +1,39 @@
1 +{
2 + metrics
3 + servers :80 {
4 + metrics {
5 + per_host
6 + }
7 + }
8 +}
9 +:80 {
10 + respond "Hello"
11 +}
12 +
13 +----------
14 +{
15 + "apps": {
16 + "http": {
17 + "servers": {
18 + "srv0": {
19 + "listen": [
20 + ":80"
21 + ],
22 + "routes": [
23 + {
24 + "handle": [
25 + {
26 + "body": "Hello",
27 + "handler": "static_response"
28 + }
29 + ]
30 + }
31 + ]
32 + }
33 + },
34 + "metrics": {
35 + "per_host": true
36 + }
37 + }
38 + }
39 +}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/metrics_perhost.caddyfiletest
+4 -4
@@ -26,11 +26,11 @@
26 }
27 ]
28 }
29 - ],
30 - "metrics": {
31 - "per_host": true
32 - }
29 + ]
30 }
31 + },
32 + "metrics": {
33 + "per_host": true
34 }
35 }
36 }
modules/caddyhttp/app.go
+20 -7
@@ -15,6 +15,7 @@
15 package caddyhttp
16
17 import (
18 + "cmp"
19 "context"
20 "crypto/tls"
21 "fmt"
@@ -142,6 +143,10 @@ type App struct {
143 // affect functionality.
144 Servers map[string]*Server `json:"servers,omitempty"`
145
146 + // If set, metrics observations will be enabled.
147 + // This setting is EXPERIMENTAL and subject to change.
148 + Metrics *Metrics `json:"metrics,omitempty"`
149 +
150 ctx caddy.Context
151 logger *zap.Logger
152 tlsApp *caddytls.TLS
@@ -184,6 +189,10 @@ func (app *App) Provision(ctx caddy.Context) error {
189 return err
190 }
191
192 + if app.Metrics != nil {
193 + app.Metrics.init = sync.Once{}
194 + app.Metrics.httpMetrics = &httpMetrics{}
195 + }
196 // prepare each server
197 oldContext := ctx.Context
198 for srvName, srv := range app.Servers {
@@ -196,6 +205,15 @@ func (app *App) Provision(ctx caddy.Context) error {
205 srv.errorLogger = app.logger.Named("log.error")
206 srv.shutdownAtMu = new(sync.RWMutex)
207
208 + if srv.Metrics != nil {
209 + srv.logger.Warn("per-server 'metrics' is deprecated; use 'metrics' in the root 'http' app instead")
210 + app.Metrics = cmp.Or[*Metrics](app.Metrics, &Metrics{
211 + init: sync.Once{},
212 + httpMetrics: &httpMetrics{},
213 + })
214 + app.Metrics.PerHost = app.Metrics.PerHost || srv.Metrics.PerHost
215 + }
216 +
217 // only enable access logs if configured
218 if srv.Logs != nil {
219 srv.accessLogger = app.logger.Named("log.access")
@@ -342,16 +360,11 @@ func (app *App) Provision(ctx caddy.Context) error {
360 srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
361 }
362 }
345 -
363 // pre-compile the primary handler chain, and be sure to wrap it in our
364 // route handler so that important security checks are done, etc.
365 primaryRoute := emptyHandler
366 if srv.Routes != nil {
350 - if srv.Metrics != nil {
351 - srv.Metrics.init = sync.Once{}
352 - srv.Metrics.httpMetrics = &httpMetrics{}
353 - }
354 - err := srv.Routes.ProvisionHandlers(ctx, srv.Metrics)
367 + err := srv.Routes.ProvisionHandlers(ctx, app.Metrics)
368 if err != nil {
369 return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
370 }
@@ -370,7 +383,7 @@ func (app *App) Provision(ctx caddy.Context) error {
383
384 // provision the named routes (they get compiled at runtime)
385 for name, route := range srv.NamedRoutes {
373 - err := route.Provision(ctx, srv.Metrics)
386 + err := route.Provision(ctx, app.Metrics)
387 if err != nil {
388 return fmt.Errorf("server %s: setting up named route '%s' handlers: %v", name, srvName, err)
389 }
modules/caddyhttp/metrics.go
+3 -2
@@ -4,6 +4,7 @@ import (
4 "context"
5 "errors"
6 "net/http"
7 + "strings"
8 "sync"
9 "time"
10
@@ -133,8 +134,8 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
134 statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""}
135
136 if h.metrics.PerHost {
136 - labels["host"] = r.Host
137 - statusLabels["host"] = r.Host
137 + labels["host"] = strings.ToLower(r.Host)
138 + statusLabels["host"] = strings.ToLower(r.Host)
139 }
140
141 inFlight := h.metrics.httpMetrics.requestInFlight.With(labels)
modules/caddyhttp/server.go
+1
@@ -226,6 +226,7 @@ type Server struct {
226
227 // If set, metrics observations will be enabled.
228 // This setting is EXPERIMENTAL and subject to change.
229 + // DEPRECATED: Use the app-level `metrics` field.
230 Metrics *Metrics `json:"metrics,omitempty"`
231
232 name string