caddyhttp: Make metrics opt-in (#5042)

* caddyhttp: Make metrics opt-in

Related to #4644

* Make configurable in Caddyfile
This commit is contained in:
Matt Holt 2022-09-16 13:32:49 -06:00 committed by GitHub
parent 258071d857
commit 74547f5bed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 30 additions and 8 deletions

View file

@ -43,6 +43,7 @@ type serverOptions struct {
Protocols []string Protocols []string
StrictSNIHost *bool StrictSNIHost *bool
ShouldLogCredentials bool ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
} }
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) { func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
@ -175,6 +176,15 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
} }
serverOpts.StrictSNIHost = &boolVal serverOpts.StrictSNIHost = &boolVal
case "metrics":
if d.NextArg() {
return nil, d.ArgErr()
}
if d.NextBlock(0) {
return nil, d.ArgErr()
}
serverOpts.Metrics = new(caddyhttp.Metrics)
// TODO: DEPRECATED. (August 2022) // TODO: DEPRECATED. (August 2022)
case "protocol": case "protocol":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon") caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")
@ -259,6 +269,7 @@ func applyServerOptions(
server.MaxHeaderBytes = opts.MaxHeaderBytes server.MaxHeaderBytes = opts.MaxHeaderBytes
server.Protocols = opts.Protocols server.Protocols = opts.Protocols
server.StrictSNIHost = opts.StrictSNIHost server.StrictSNIHost = opts.StrictSNIHost
server.Metrics = opts.Metrics
if opts.ShouldLogCredentials { if opts.ShouldLogCredentials {
if server.Logs == nil { if server.Logs == nil {
server.Logs = &caddyhttp.ServerLogConfig{} server.Logs = &caddyhttp.ServerLogConfig{}

View file

@ -263,7 +263,7 @@ func (app *App) Provision(ctx caddy.Context) error {
// 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 {
err := srv.Routes.ProvisionHandlers(ctx) 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)
} }

View file

@ -11,6 +11,10 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
) )
// Metrics configures metrics observations.
// EXPERIMENTAL and subject to change or removal.
type Metrics struct{}
var httpMetrics = struct { var httpMetrics = struct {
init sync.Once init sync.Once
requestInFlight *prometheus.GaugeVec requestInFlight *prometheus.GaugeVec

View file

@ -130,7 +130,7 @@ func (routes RouteList) Provision(ctx caddy.Context) error {
if err != nil { if err != nil {
return err return err
} }
return routes.ProvisionHandlers(ctx) return routes.ProvisionHandlers(ctx, nil)
} }
// ProvisionMatchers sets up all the matchers by loading the // ProvisionMatchers sets up all the matchers by loading the
@ -156,7 +156,7 @@ func (routes RouteList) ProvisionMatchers(ctx caddy.Context) error {
// handler modules. Only call this method directly if you need // handler modules. Only call this method directly if you need
// to set up matchers and handlers separately without having // to set up matchers and handlers separately without having
// to provision a second time; otherwise use Provision instead. // to provision a second time; otherwise use Provision instead.
func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error { func (routes RouteList) ProvisionHandlers(ctx caddy.Context, metrics *Metrics) error {
for i := range routes { for i := range routes {
handlersIface, err := ctx.LoadModule(&routes[i], "HandlersRaw") handlersIface, err := ctx.LoadModule(&routes[i], "HandlersRaw")
if err != nil { if err != nil {
@ -168,7 +168,7 @@ func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
// pre-compile the middleware handler chain // pre-compile the middleware handler chain
for _, midhandler := range routes[i].Handlers { for _, midhandler := range routes[i].Handlers {
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(ctx, midhandler)) routes[i].middleware = append(routes[i].middleware, wrapMiddleware(ctx, midhandler, metrics))
} }
} }
return nil return nil
@ -270,9 +270,12 @@ func wrapRoute(route Route) Middleware {
// we need to pull this particular MiddlewareHandler // we need to pull this particular MiddlewareHandler
// pointer into its own stack frame to preserve it so it // pointer into its own stack frame to preserve it so it
// won't be overwritten in future loop iterations. // won't be overwritten in future loop iterations.
func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware { func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler, metrics *Metrics) Middleware {
// wrap the middleware with metrics instrumentation handlerToUse := mh
metricsHandler := newMetricsInstrumentedHandler(caddy.GetModuleName(mh), mh) if metrics != nil {
// wrap the middleware with metrics instrumentation
handlerToUse = newMetricsInstrumentedHandler(caddy.GetModuleName(mh), mh)
}
return func(next Handler) Handler { return func(next Handler) Handler {
// copy the next handler (it's an interface, so it's // copy the next handler (it's an interface, so it's
@ -284,7 +287,7 @@ func wrapMiddleware(ctx caddy.Context, mh MiddlewareHandler) Middleware {
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
// TODO: This is where request tracing could be implemented // TODO: This is where request tracing could be implemented
// TODO: see what the std lib gives us in terms of stack tracing too // TODO: see what the std lib gives us in terms of stack tracing too
return metricsHandler.ServeHTTP(w, r, nextCopy) return handlerToUse.ServeHTTP(w, r, nextCopy)
}) })
} }
} }

View file

@ -152,6 +152,10 @@ type Server struct {
// Default: `[h1 h2 h3]` // Default: `[h1 h2 h3]`
Protocols []string `json:"protocols,omitempty"` Protocols []string `json:"protocols,omitempty"`
// If set, metrics observations will be enabled.
// This setting is EXPERIMENTAL and subject to change.
Metrics *Metrics `json:"metrics,omitempty"`
name string name string
primaryHandlerChain Handler primaryHandlerChain Handler