mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
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>
This commit is contained in:
parent
c6f2979986
commit
388c7e898c
8 changed files with 102 additions and 13 deletions
|
@ -15,6 +15,7 @@
|
||||||
package httpcaddyfile
|
package httpcaddyfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -186,12 +187,25 @@ func (st ServerType) Setup(
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hoist the metrics config from per-server to global
|
||||||
|
metrics, _ := options["metrics"].(*caddyhttp.Metrics)
|
||||||
|
for _, s := range servers {
|
||||||
|
if s.Metrics != nil {
|
||||||
|
metrics = cmp.Or[*caddyhttp.Metrics](metrics, &caddyhttp.Metrics{})
|
||||||
|
metrics = &caddyhttp.Metrics{
|
||||||
|
PerHost: metrics.PerHost || s.Metrics.PerHost,
|
||||||
|
}
|
||||||
|
s.Metrics = nil // we don't need it anymore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// now that each server is configured, make the HTTP app
|
// now that each server is configured, make the HTTP app
|
||||||
httpApp := caddyhttp.App{
|
httpApp := caddyhttp.App{
|
||||||
HTTPPort: tryInt(options["http_port"], &warnings),
|
HTTPPort: tryInt(options["http_port"], &warnings),
|
||||||
HTTPSPort: tryInt(options["https_port"], &warnings),
|
HTTPSPort: tryInt(options["https_port"], &warnings),
|
||||||
GracePeriod: tryDuration(options["grace_period"], &warnings),
|
GracePeriod: tryDuration(options["grace_period"], &warnings),
|
||||||
ShutdownDelay: tryDuration(options["shutdown_delay"], &warnings),
|
ShutdownDelay: tryDuration(options["shutdown_delay"], &warnings),
|
||||||
|
Metrics: metrics,
|
||||||
Servers: servers,
|
Servers: servers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ func init() {
|
||||||
RegisterGlobalOption("local_certs", parseOptTrue)
|
RegisterGlobalOption("local_certs", parseOptTrue)
|
||||||
RegisterGlobalOption("key_type", parseOptSingleString)
|
RegisterGlobalOption("key_type", parseOptSingleString)
|
||||||
RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
|
RegisterGlobalOption("auto_https", parseOptAutoHTTPS)
|
||||||
|
RegisterGlobalOption("metrics", parseMetricsOptions)
|
||||||
RegisterGlobalOption("servers", parseServerOptions)
|
RegisterGlobalOption("servers", parseServerOptions)
|
||||||
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
|
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
|
||||||
RegisterGlobalOption("cert_lifetime", parseOptDuration)
|
RegisterGlobalOption("cert_lifetime", parseOptDuration)
|
||||||
|
@ -446,6 +448,24 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarshalCaddyfileMetricsOptions(d *caddyfile.Dispenser) (any, error) {
|
||||||
|
d.Next() // consume option name
|
||||||
|
metrics := new(caddyhttp.Metrics)
|
||||||
|
for d.NextBlock(0) {
|
||||||
|
switch d.Val() {
|
||||||
|
case "per_host":
|
||||||
|
metrics.PerHost = true
|
||||||
|
default:
|
||||||
|
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMetricsOptions(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
|
return unmarshalCaddyfileMetricsOptions(d)
|
||||||
|
}
|
||||||
|
|
||||||
func parseServerOptions(d *caddyfile.Dispenser, _ any) (any, error) {
|
func parseServerOptions(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
return unmarshalCaddyfileServerOptions(d)
|
return unmarshalCaddyfileServerOptions(d)
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "metrics":
|
case "metrics":
|
||||||
|
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.")
|
||||||
serverOpts.Metrics = new(caddyhttp.Metrics)
|
serverOpts.Metrics = new(caddyhttp.Metrics)
|
||||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
switch d.Val() {
|
switch d.Val() {
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
metrics
|
||||||
|
servers :80 {
|
||||||
|
metrics {
|
||||||
|
per_host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:80 {
|
||||||
|
respond "Hello"
|
||||||
|
}
|
||||||
|
|
||||||
|
----------
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":80"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"body": "Hello",
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"per_host": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,11 +26,11 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"metrics": {
|
|
||||||
"per_host": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"per_host": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package caddyhttp
|
package caddyhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -142,6 +143,10 @@ type App struct {
|
||||||
// affect functionality.
|
// affect functionality.
|
||||||
Servers map[string]*Server `json:"servers,omitempty"`
|
Servers map[string]*Server `json:"servers,omitempty"`
|
||||||
|
|
||||||
|
// If set, metrics observations will be enabled.
|
||||||
|
// This setting is EXPERIMENTAL and subject to change.
|
||||||
|
Metrics *Metrics `json:"metrics,omitempty"`
|
||||||
|
|
||||||
ctx caddy.Context
|
ctx caddy.Context
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
tlsApp *caddytls.TLS
|
tlsApp *caddytls.TLS
|
||||||
|
@ -184,6 +189,10 @@ func (app *App) Provision(ctx caddy.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.Metrics != nil {
|
||||||
|
app.Metrics.init = sync.Once{}
|
||||||
|
app.Metrics.httpMetrics = &httpMetrics{}
|
||||||
|
}
|
||||||
// prepare each server
|
// prepare each server
|
||||||
oldContext := ctx.Context
|
oldContext := ctx.Context
|
||||||
for srvName, srv := range app.Servers {
|
for srvName, srv := range app.Servers {
|
||||||
|
@ -196,6 +205,15 @@ func (app *App) Provision(ctx caddy.Context) error {
|
||||||
srv.errorLogger = app.logger.Named("log.error")
|
srv.errorLogger = app.logger.Named("log.error")
|
||||||
srv.shutdownAtMu = new(sync.RWMutex)
|
srv.shutdownAtMu = new(sync.RWMutex)
|
||||||
|
|
||||||
|
if srv.Metrics != nil {
|
||||||
|
srv.logger.Warn("per-server 'metrics' is deprecated; use 'metrics' in the root 'http' app instead")
|
||||||
|
app.Metrics = cmp.Or[*Metrics](app.Metrics, &Metrics{
|
||||||
|
init: sync.Once{},
|
||||||
|
httpMetrics: &httpMetrics{},
|
||||||
|
})
|
||||||
|
app.Metrics.PerHost = app.Metrics.PerHost || srv.Metrics.PerHost
|
||||||
|
}
|
||||||
|
|
||||||
// only enable access logs if configured
|
// only enable access logs if configured
|
||||||
if srv.Logs != nil {
|
if srv.Logs != nil {
|
||||||
srv.accessLogger = app.logger.Named("log.access")
|
srv.accessLogger = app.logger.Named("log.access")
|
||||||
|
@ -342,16 +360,11 @@ func (app *App) Provision(ctx caddy.Context) error {
|
||||||
srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
|
srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-compile the primary handler chain, and be sure to wrap it in our
|
// pre-compile the primary handler chain, and be sure to wrap it in our
|
||||||
// route handler so that important security checks are done, etc.
|
// route handler so that important security checks are done, etc.
|
||||||
primaryRoute := emptyHandler
|
primaryRoute := emptyHandler
|
||||||
if srv.Routes != nil {
|
if srv.Routes != nil {
|
||||||
if srv.Metrics != nil {
|
err := srv.Routes.ProvisionHandlers(ctx, app.Metrics)
|
||||||
srv.Metrics.init = sync.Once{}
|
|
||||||
srv.Metrics.httpMetrics = &httpMetrics{}
|
|
||||||
}
|
|
||||||
err := srv.Routes.ProvisionHandlers(ctx, srv.Metrics)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
|
return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
|
||||||
}
|
}
|
||||||
|
@ -370,7 +383,7 @@ func (app *App) Provision(ctx caddy.Context) error {
|
||||||
|
|
||||||
// provision the named routes (they get compiled at runtime)
|
// provision the named routes (they get compiled at runtime)
|
||||||
for name, route := range srv.NamedRoutes {
|
for name, route := range srv.NamedRoutes {
|
||||||
err := route.Provision(ctx, srv.Metrics)
|
err := route.Provision(ctx, app.Metrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("server %s: setting up named route '%s' handlers: %v", name, srvName, err)
|
return fmt.Errorf("server %s: setting up named route '%s' handlers: %v", name, srvName, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -133,8 +134,8 @@ func (h *metricsInstrumentedHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
|
||||||
statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""}
|
statusLabels := prometheus.Labels{"server": server, "handler": h.handler, "method": method, "code": ""}
|
||||||
|
|
||||||
if h.metrics.PerHost {
|
if h.metrics.PerHost {
|
||||||
labels["host"] = r.Host
|
labels["host"] = strings.ToLower(r.Host)
|
||||||
statusLabels["host"] = r.Host
|
statusLabels["host"] = strings.ToLower(r.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
inFlight := h.metrics.httpMetrics.requestInFlight.With(labels)
|
inFlight := h.metrics.httpMetrics.requestInFlight.With(labels)
|
||||||
|
|
|
@ -226,6 +226,7 @@ type Server struct {
|
||||||
|
|
||||||
// If set, metrics observations will be enabled.
|
// If set, metrics observations will be enabled.
|
||||||
// This setting is EXPERIMENTAL and subject to change.
|
// This setting is EXPERIMENTAL and subject to change.
|
||||||
|
// DEPRECATED: Use the app-level `metrics` field.
|
||||||
Metrics *Metrics `json:"metrics,omitempty"`
|
Metrics *Metrics `json:"metrics,omitempty"`
|
||||||
|
|
||||||
name string
|
name string
|
||||||
|
|
Loading…
Reference in a new issue