Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caddyhttp: Trace individual middleware handlers #6313

Merged
merged 2 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 13 additions & 33 deletions caddyconfig/httpcaddyfile/serveroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type serverOptions struct {
ClientIPHeaders []string
ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
Trace bool // TODO: EXPERIMENTAL
}

func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
Expand Down Expand Up @@ -246,39 +247,11 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.Metrics = new(caddyhttp.Metrics)

// TODO: DEPRECATED. (August 2022)
case "protocol":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol sub-option will be removed soon")

for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "allow_h2c":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: allow_h2c will be removed soon; use protocols option instead")

if d.NextArg() {
return nil, d.ArgErr()
}
if sliceContains(serverOpts.Protocols, "h2c") {
return nil, d.Errf("protocol h2c already specified")
}
serverOpts.Protocols = append(serverOpts.Protocols, "h2c")

case "strict_sni_host":
caddy.Log().Named("caddyfile").Warn("DEPRECATED: protocol > strict_sni_host in this position will be removed soon; move up to the servers block instead")

if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
}
boolVal := true
if d.Val() == "insecure_off" {
boolVal = false
}
serverOpts.StrictSNIHost = &boolVal

default:
return nil, d.Errf("unrecognized protocol option '%s'", d.Val())
}
case "trace":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.Trace = true

default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
Expand Down Expand Up @@ -351,10 +324,17 @@ func applyServerOptions(
server.Metrics = opts.Metrics
if opts.ShouldLogCredentials {
if server.Logs == nil {
server.Logs = &caddyhttp.ServerLogConfig{}
server.Logs = new(caddyhttp.ServerLogConfig)
}
server.Logs.ShouldLogCredentials = opts.ShouldLogCredentials
}
if opts.Trace {
// TODO: THIS IS EXPERIMENTAL (MAY 2024)
if server.Logs == nil {
server.Logs = new(caddyhttp.ServerLogConfig)
}
server.Logs.Trace = opts.Trace
}

if opts.Name != "" {
nameReplacements[key] = opts.Name
Expand Down
3 changes: 3 additions & 0 deletions modules/caddyhttp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ func (app *App) Provision(ctx caddy.Context) error {
// only enable access logs if configured
if srv.Logs != nil {
srv.accessLogger = app.logger.Named("log.access")
if srv.Logs.Trace {
srv.traceLogger = app.logger.Named("log.trace")
}
}

// the Go standard library does not let us serve only HTTP/2 using
Expand Down
11 changes: 11 additions & 0 deletions modules/caddyhttp/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ type ServerLogConfig struct {
// and this includes some request and response headers, i.e `Cookie`,
// `Set-Cookie`, `Authorization`, and `Proxy-Authorization`.
ShouldLogCredentials bool `json:"should_log_credentials,omitempty"`

// Log each individual handler that is invoked.
// Requires that the log emit at DEBUG level.
//
// NOTE: This may log the configuration of your
// HTTP handler modules; do not enable this in
// insecure contexts when there is sensitive
// data in the configuration.
//
// EXPERIMENTAL: Subject to change or removal.
Trace bool `json:"trace,omitempty"`
}

// wrapLogger wraps logger in one or more logger named
Expand Down
6 changes: 4 additions & 2 deletions modules/caddyhttp/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,10 @@ func wrapMiddleware(_ caddy.Context, mh MiddlewareHandler, metrics *Metrics) Mid
nextCopy := next

return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
// TODO: This is where request tracing could be implemented
// TODO: see what the std lib gives us in terms of stack tracing too
// EXPERIMENTAL: Trace each module that gets invoked
if server, ok := r.Context().Value(ServerCtxKey).(*Server); ok && server != nil {
server.logTrace(handlerToUse)
}
return handlerToUse.ServeHTTP(w, r, nextCopy)
})
}
Expand Down
10 changes: 10 additions & 0 deletions modules/caddyhttp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ type Server struct {
logger *zap.Logger
accessLogger *zap.Logger
errorLogger *zap.Logger
traceLogger *zap.Logger
ctx caddy.Context

server *http.Server
Expand Down Expand Up @@ -738,6 +739,15 @@ func (s *Server) shouldLogRequest(r *http.Request) bool {
return !s.Logs.SkipUnmappedHosts
}

// logTrace will log that this middleware handler is being invoked.
// It emits at DEBUG level.
func (s *Server) logTrace(mh MiddlewareHandler) {
if s.Logs == nil || !s.Logs.Trace {
return
}
s.traceLogger.Debug(caddy.GetModuleName(mh), zap.Any("module", mh))
}

// logRequest logs the request to access logs, unless skipped.
func (s *Server) logRequest(
accLog *zap.Logger, r *http.Request, wrec ResponseRecorder, duration *time.Duration,
Expand Down
Loading