@marcel91200 / caddy Mirror · read-only / commits / f4bf4e00
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, 08:15 UTC

perf: use zap's Check() to prevent useless allocs (#6560)

* perf: use zap's Check() to prevent useless allocs * fix * fix * fix * fix * restore previous replacer behavior * fix linter

Kévin Dunglas committed Sep 13, 2024 at 19:16 UTC f4bf4e0097853438eb69c573bbaa0581e9b9c02d
30 files changed +599 -282
modules/caddyhttp/caddyauth/caddyauth.go
+4 -3
@@ -19,6 +19,7 @@ import (
19 "net/http"
20
21 "go.uber.org/zap"
22 + "go.uber.org/zap/zapcore"
23
24 "github.com/caddyserver/caddy/v2"
25 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -76,9 +77,9 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
77 for provName, prov := range a.Providers {
78 user, authed, err = prov.Authenticate(w, r)
79 if err != nil {
79 - a.logger.Error("auth provider returned error",
80 - zap.String("provider", provName),
81 - zap.Error(err))
80 + if c := a.logger.Check(zapcore.ErrorLevel, "auth provider returned error"); c != nil {
81 + c.Write(zap.String("provider", provName), zap.Error(err))
82 + }
83 continue
84 }
85 if authed {
modules/caddyhttp/fileserver/browse.go
+7 -4
@@ -33,6 +33,7 @@ import (
33 "time"
34
35 "go.uber.org/zap"
36 + "go.uber.org/zap/zapcore"
37
38 "github.com/caddyserver/caddy/v2"
39 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -68,9 +69,9 @@ type Browse struct {
69 }
70
71 func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
71 - fsrv.logger.Debug("browse enabled; listing directory contents",
72 - zap.String("path", dirPath),
73 - zap.String("root", root))
72 + if c := fsrv.logger.Check(zapcore.DebugLevel, "browse enabled; listing directory contents"); c != nil {
73 + c.Write(zap.String("path", dirPath), zap.String("root", root))
74 + }
75
76 // Navigation on the client-side gets messed up if the
77 // URL doesn't end in a trailing slash because hrefs to
@@ -92,7 +93,9 @@ func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w ht
93 origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
94 if r.URL.Path == "" || path.Base(origReq.URL.Path) == path.Base(r.URL.Path) {
95 if !strings.HasSuffix(origReq.URL.Path, "/") {
95 - fsrv.logger.Debug("redirecting to trailing slash to preserve hrefs", zap.String("request_path", r.URL.Path))
96 + if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to trailing slash to preserve hrefs"); c != nil {
97 + c.Write(zap.String("request_path", r.URL.Path))
98 + }
99 return redirect(w, r, origReq.URL.Path+"/")
100 }
101 }
modules/caddyhttp/fileserver/browsetplcontext.go
+4 -3
@@ -28,6 +28,7 @@ import (
28
29 "github.com/dustin/go-humanize"
30 "go.uber.org/zap"
31 + "go.uber.org/zap/zapcore"
32
33 "github.com/caddyserver/caddy/v2"
34 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -57,9 +58,9 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
58
59 info, err := entry.Info()
60 if err != nil {
60 - fsrv.logger.Error("could not get info about directory entry",
61 - zap.String("name", entry.Name()),
62 - zap.String("root", root))
61 + if c := fsrv.logger.Check(zapcore.ErrorLevel, "could not get info about directory entry"); c != nil {
62 + c.Write(zap.String("name", entry.Name()), zap.String("root", root))
63 + }
64 continue
65 }
66
modules/caddyhttp/fileserver/matcher.go
+11 -3
@@ -33,6 +33,7 @@ import (
33 "github.com/google/cel-go/common/types/ref"
34 "github.com/google/cel-go/parser"
35 "go.uber.org/zap"
36 + "go.uber.org/zap/zapcore"
37
38 "github.com/caddyserver/caddy/v2"
39 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -326,7 +327,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
327
328 fileSystem, ok := m.fsmap.Get(fsName)
329 if !ok {
329 - m.logger.Error("use of unregistered filesystem", zap.String("fs", fsName))
330 + if c := m.logger.Check(zapcore.ErrorLevel, "use of unregistered filesystem"); c != nil {
331 + c.Write(zap.String("fs", fsName))
332 + }
333 return false
334 }
335 type matchCandidate struct {
@@ -356,7 +359,10 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
359 return val, nil
360 })
361 if err != nil {
359 - m.logger.Error("evaluating placeholders", zap.Error(err))
362 + if c := m.logger.Check(zapcore.ErrorLevel, "evaluating placeholders"); c != nil {
363 + c.Write(zap.Error(err))
364 + }
365 +
366 expandedFile = file // "oh well," I guess?
367 }
368
@@ -379,7 +385,9 @@ func (m MatchFile) selectFile(r *http.Request) (matched bool) {
385 } else {
386 globResults, err = fs.Glob(fileSystem, fullPattern)
387 if err != nil {
382 - m.logger.Error("expanding glob", zap.Error(err))
388 + if c := m.logger.Check(zapcore.ErrorLevel, "expanding glob"); c != nil {
389 + c.Write(zap.Error(err))
390 + }
391 }
392 }
393
modules/caddyhttp/fileserver/staticfiles.go
+63 -28
@@ -31,6 +31,7 @@ import (
31 "strings"
32
33 "go.uber.org/zap"
34 + "go.uber.org/zap/zapcore"
35
36 "github.com/caddyserver/caddy/v2"
37 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -286,11 +287,14 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
287 // remove any trailing `/` as it breaks fs.ValidPath() in the stdlib
288 filename := strings.TrimSuffix(caddyhttp.SanitizedPathJoin(root, r.URL.Path), "/")
289
289 - fsrv.logger.Debug("sanitized path join",
290 - zap.String("site_root", root),
291 - zap.String("fs", fsName),
292 - zap.String("request_path", r.URL.Path),
293 - zap.String("result", filename))
290 + if c := fsrv.logger.Check(zapcore.DebugLevel, "sanitized path join"); c != nil {
291 + c.Write(
292 + zap.String("site_root", root),
293 + zap.String("fs", fsName),
294 + zap.String("request_path", r.URL.Path),
295 + zap.String("result", filename),
296 + )
297 + }
298
299 // get information about the file
300 info, err := fs.Stat(fileSystem, filename)
@@ -313,9 +317,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
317 indexPath := caddyhttp.SanitizedPathJoin(filename, indexPage)
318 if fileHidden(indexPath, filesToHide) {
319 // pretend this file doesn't exist
316 - fsrv.logger.Debug("hiding index file",
317 - zap.String("filename", indexPath),
318 - zap.Strings("files_to_hide", filesToHide))
320 + if c := fsrv.logger.Check(zapcore.DebugLevel, "hiding index file"); c != nil {
321 + c.Write(
322 + zap.String("filename", indexPath),
323 + zap.Strings("files_to_hide", filesToHide),
324 + )
325 + }
326 continue
327 }
328
@@ -335,7 +342,9 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
342 info = indexInfo
343 filename = indexPath
344 implicitIndexFile = true
338 - fsrv.logger.Debug("located index file", zap.String("filename", filename))
345 + if c := fsrv.logger.Check(zapcore.DebugLevel, "located index file"); c != nil {
346 + c.Write(zap.String("filename", filename))
347 + }
348 break
349 }
350 }
@@ -343,9 +352,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
352 // if still referencing a directory, delegate
353 // to browse or return an error
354 if info.IsDir() {
346 - fsrv.logger.Debug("no index file in directory",
347 - zap.String("path", filename),
348 - zap.Strings("index_filenames", fsrv.IndexNames))
355 + if c := fsrv.logger.Check(zapcore.DebugLevel, "no index file in directory"); c != nil {
356 + c.Write(
357 + zap.String("path", filename),
358 + zap.Strings("index_filenames", fsrv.IndexNames),
359 + )
360 + }
361 if fsrv.Browse != nil && !fileHidden(filename, filesToHide) {
362 return fsrv.serveBrowse(fileSystem, root, filename, w, r, next)
363 }
@@ -355,9 +367,12 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
367 // one last check to ensure the file isn't hidden (we might
368 // have changed the filename from when we last checked)
369 if fileHidden(filename, filesToHide) {
358 - fsrv.logger.Debug("hiding file",
359 - zap.String("filename", filename),
360 - zap.Strings("files_to_hide", filesToHide))
370 + if c := fsrv.logger.Check(zapcore.DebugLevel, "hiding file"); c != nil {
371 + c.Write(
372 + zap.String("filename", filename),
373 + zap.Strings("files_to_hide", filesToHide),
374 + )
375 + }
376 return fsrv.notFound(w, r, next)
377 }
378
@@ -375,15 +390,21 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
390 if path.Base(origReq.URL.Path) == path.Base(r.URL.Path) {
391 if implicitIndexFile && !strings.HasSuffix(origReq.URL.Path, "/") {
392 to := origReq.URL.Path + "/"
378 - fsrv.logger.Debug("redirecting to canonical URI (adding trailing slash for directory)",
379 - zap.String("from_path", origReq.URL.Path),
380 - zap.String("to_path", to))
393 + if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to canonical URI (adding trailing slash for directory"); c != nil {
394 + c.Write(
395 + zap.String("from_path", origReq.URL.Path),
396 + zap.String("to_path", to),
397 + )
398 + }
399 return redirect(w, r, to)
400 } else if !implicitIndexFile && strings.HasSuffix(origReq.URL.Path, "/") {
401 to := origReq.URL.Path[:len(origReq.URL.Path)-1]
384 - fsrv.logger.Debug("redirecting to canonical URI (removing trailing slash for file)",
385 - zap.String("from_path", origReq.URL.Path),
386 - zap.String("to_path", to))
402 + if c := fsrv.logger.Check(zapcore.DebugLevel, "redirecting to canonical URI (removing trailing slash for file"); c != nil {
403 + c.Write(
404 + zap.String("from_path", origReq.URL.Path),
405 + zap.String("to_path", to),
406 + )
407 + }
408 return redirect(w, r, to)
409 }
410 }
@@ -411,13 +432,19 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
432 compressedFilename := filename + precompress.Suffix()
433 compressedInfo, err := fs.Stat(fileSystem, compressedFilename)
434 if err != nil || compressedInfo.IsDir() {
414 - fsrv.logger.Debug("precompressed file not accessible", zap.String("filename", compressedFilename), zap.Error(err))
435 + if c := fsrv.logger.Check(zapcore.DebugLevel, "precompressed file not accessible"); c != nil {
436 + c.Write(zap.String("filename", compressedFilename), zap.Error(err))
437 + }
438 continue
439 }
417 - fsrv.logger.Debug("opening compressed sidecar file", zap.String("filename", compressedFilename), zap.Error(err))
440 + if c := fsrv.logger.Check(zapcore.DebugLevel, "opening compressed sidecar file"); c != nil {
441 + c.Write(zap.String("filename", compressedFilename), zap.Error(err))
442 + }
443 file, err = fsrv.openFile(fileSystem, compressedFilename, w)
444 if err != nil {
420 - fsrv.logger.Warn("opening precompressed file failed", zap.String("filename", compressedFilename), zap.Error(err))
445 + if c := fsrv.logger.Check(zapcore.WarnLevel, "opening precompressed file failed"); c != nil {
446 + c.Write(zap.String("filename", compressedFilename), zap.Error(err))
447 + }
448 if caddyErr, ok := err.(caddyhttp.HandlerError); ok && caddyErr.StatusCode == http.StatusServiceUnavailable {
449 return err
450 }
@@ -448,7 +475,9 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
475
476 // no precompressed file found, use the actual file
477 if file == nil {
451 - fsrv.logger.Debug("opening file", zap.String("filename", filename))
478 + if c := fsrv.logger.Check(zapcore.DebugLevel, "opening file"); c != nil {
479 + c.Write(zap.String("filename", filename))
480 + }
481
482 // open the file
483 file, err = fsrv.openFile(fileSystem, filename, w)
@@ -548,10 +577,14 @@ func (fsrv *FileServer) openFile(fileSystem fs.FS, filename string, w http.Respo
577 if err != nil {
578 err = fsrv.mapDirOpenError(fileSystem, err, filename)
579 if errors.Is(err, fs.ErrNotExist) {
551 - fsrv.logger.Debug("file not found", zap.String("filename", filename), zap.Error(err))
580 + if c := fsrv.logger.Check(zapcore.DebugLevel, "file not found"); c != nil {
581 + c.Write(zap.String("filename", filename), zap.Error(err))
582 + }
583 return nil, caddyhttp.Error(http.StatusNotFound, err)
584 } else if errors.Is(err, fs.ErrPermission) {
554 - fsrv.logger.Debug("permission denied", zap.String("filename", filename), zap.Error(err))
585 + if c := fsrv.logger.Check(zapcore.DebugLevel, "permission denied"); c != nil {
586 + c.Write(zap.String("filename", filename), zap.Error(err))
587 + }
588 return nil, caddyhttp.Error(http.StatusForbidden, err)
589 }
590 // maybe the server is under load and ran out of file descriptors?
@@ -559,7 +592,9 @@ func (fsrv *FileServer) openFile(fileSystem fs.FS, filename string, w http.Respo
592 //nolint:gosec
593 backoff := weakrand.Intn(maxBackoff-minBackoff) + minBackoff
594 w.Header().Set("Retry-After", strconv.Itoa(backoff))
562 - fsrv.logger.Debug("retry after backoff", zap.String("filename", filename), zap.Int("backoff", backoff), zap.Error(err))
595 + if c := fsrv.logger.Check(zapcore.DebugLevel, "retry after backoff"); c != nil {
596 + c.Write(zap.String("filename", filename), zap.Int("backoff", backoff), zap.Error(err))
597 + }
598 return nil, caddyhttp.Error(http.StatusServiceUnavailable, err)
599 }
600 return file, nil
modules/caddyhttp/intercept/intercept.go
+4 -1
@@ -23,6 +23,7 @@ import (
23 "sync"
24
25 "go.uber.org/zap"
26 + "go.uber.org/zap/zapcore"
27
28 "github.com/caddyserver/caddy/v2"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -165,7 +166,9 @@ func (ir Intercept) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy
166 }
167 repl.Set("http.intercept.status_code", rec.Status())
168
168 - ir.logger.Debug("handling response", zap.Int("handler", rec.handlerIndex))
169 + if c := ir.logger.Check(zapcore.DebugLevel, "handling response"); c != nil {
170 + c.Write(zap.Int("handler", rec.handlerIndex))
171 + }
172
173 // pass the request through the response handler routes
174 return rec.handler.Routes.Compile(next).ServeHTTP(w, r)
modules/caddyhttp/ip_matchers.go
+8 -2
@@ -26,6 +26,7 @@ import (
26 "github.com/google/cel-go/cel"
27 "github.com/google/cel-go/common/types/ref"
28 "go.uber.org/zap"
29 + "go.uber.org/zap/zapcore"
30
31 "github.com/caddyserver/caddy/v2"
32 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -150,12 +151,17 @@ func (m MatchRemoteIP) Match(r *http.Request) bool {
151 address := r.RemoteAddr
152 clientIP, zoneID, err := parseIPZoneFromString(address)
153 if err != nil {
153 - m.logger.Error("getting remote IP", zap.Error(err))
154 + if c := m.logger.Check(zapcore.ErrorLevel, "getting remote "); c != nil {
155 + c.Write(zap.Error(err))
156 + }
157 +
158 return false
159 }
160 matches, zoneFilter := matchIPByCidrZones(clientIP, zoneID, m.cidrs, m.zones)
161 if !matches && !zoneFilter {
158 - m.logger.Debug("zone ID from remote IP did not match", zap.String("zone", zoneID))
162 + if c := m.logger.Check(zapcore.DebugLevel, "zone ID from remote IP did not match"); c != nil {
163 + c.Write(zap.String("zone", zoneID))
164 + }
165 }
166 return matches
167 }
modules/caddyhttp/logging.go
+7 -5
@@ -193,7 +193,7 @@ func (sa *StringArray) UnmarshalJSON(b []byte) error {
193 // to use, the error log message, and any extra fields.
194 // If err is a HandlerError, the returned values will
195 // have richer information.
196 -func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
196 +func errLogValues(err error) (status int, msg string, fields func() []zapcore.Field) {
197 var handlerErr HandlerError
198 if errors.As(err, &handlerErr) {
199 status = handlerErr.StatusCode
@@ -202,10 +202,12 @@ func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
202 } else {
203 msg = handlerErr.Err.Error()
204 }
205 - fields = []zapcore.Field{
206 - zap.Int("status", handlerErr.StatusCode),
207 - zap.String("err_id", handlerErr.ID),
208 - zap.String("err_trace", handlerErr.Trace),
205 + fields = func() []zapcore.Field {
206 + return []zapcore.Field{
207 + zap.Int("status", handlerErr.StatusCode),
208 + zap.String("err_id", handlerErr.ID),
209 + zap.String("err_trace", handlerErr.Trace),
210 + }
211 }
212 return
213 }
modules/caddyhttp/push/handler.go
+15 -9
@@ -20,6 +20,7 @@ import (
20 "strings"
21
22 "go.uber.org/zap"
23 + "go.uber.org/zap/zapcore"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -92,14 +93,17 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
93
94 // push first!
95 for _, resource := range h.Resources {
95 - h.logger.Debug("pushing resource",
96 - zap.String("uri", r.RequestURI),
97 - zap.String("push_method", resource.Method),
98 - zap.String("push_target", resource.Target),
99 - zap.Object("push_headers", caddyhttp.LoggableHTTPHeader{
100 - Header: hdr,
101 - ShouldLogCredentials: shouldLogCredentials,
102 - }))
96 + if c := h.logger.Check(zapcore.DebugLevel, "pushing resource"); c != nil {
97 + c.Write(
98 + zap.String("uri", r.RequestURI),
99 + zap.String("push_method", resource.Method),
100 + zap.String("push_target", resource.Target),
101 + zap.Object("push_headers", caddyhttp.LoggableHTTPHeader{
102 + Header: hdr,
103 + ShouldLogCredentials: shouldLogCredentials,
104 + }),
105 + )
106 + }
107 err := pusher.Push(repl.ReplaceAll(resource.Target, "."), &http.PushOptions{
108 Method: resource.Method,
109 Header: hdr,
@@ -209,7 +213,9 @@ func (lp linkPusher) WriteHeader(statusCode int) {
213 if links, ok := lp.ResponseWriter.Header()["Link"]; ok {
214 // only initiate these pushes if it hasn't been done yet
215 if val := caddyhttp.GetVar(lp.request.Context(), pushedLink); val == nil {
212 - lp.handler.logger.Debug("pushing Link resources", zap.Strings("linked", links))
216 + if c := lp.handler.logger.Check(zapcore.DebugLevel, "pushing Link resources"); c != nil {
217 + c.Write(zap.Strings("linked", links))
218 + }
219 caddyhttp.SetVar(lp.request.Context(), pushedLink, true)
220 lp.handler.servePreloadLinks(lp.pusher, lp.header, links)
221 }
modules/caddyhttp/requestbody/requestbody.go
+7 -2
@@ -20,6 +20,7 @@ import (
20 "time"
21
22 "go.uber.org/zap"
23 + "go.uber.org/zap/zapcore"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -69,12 +70,16 @@ func (rb RequestBody) ServeHTTP(w http.ResponseWriter, r *http.Request, next cad
70 rc := http.NewResponseController(w)
71 if rb.ReadTimeout > 0 {
72 if err := rc.SetReadDeadline(time.Now().Add(rb.ReadTimeout)); err != nil {
72 - rb.logger.Error("could not set read deadline", zap.Error(err))
73 + if c := rb.logger.Check(zapcore.ErrorLevel, "could not set read deadline"); c != nil {
74 + c.Write(zap.Error(err))
75 + }
76 }
77 }
78 if rb.WriteTimeout > 0 {
79 if err := rc.SetWriteDeadline(time.Now().Add(rb.WriteTimeout)); err != nil {
77 - rb.logger.Error("could not set write deadline", zap.Error(err))
80 + if c := rb.logger.Check(zapcore.ErrorLevel, "could not set write deadline"); c != nil {
81 + c.Write(zap.Error(err))
82 + }
83 }
84 }
85 }
modules/caddyhttp/reverseproxy/fastcgi/client.go
+7 -3
@@ -40,6 +40,7 @@ import (
40 "time"
41
42 "go.uber.org/zap"
43 + "go.uber.org/zap/zapcore"
44 )
45
46 // FCGIListenSockFileno describes listen socket file number.
@@ -184,10 +185,13 @@ func (f clientCloser) Close() error {
185 return f.rwc.Close()
186 }
187
188 + logLevel := zapcore.WarnLevel
189 if f.status >= 400 {
188 - f.logger.Error("stderr", zap.ByteString("body", stderr))
189 - } else {
190 - f.logger.Warn("stderr", zap.ByteString("body", stderr))
190 + logLevel = zapcore.ErrorLevel
191 + }
192 +
193 + if c := f.logger.Check(logLevel, "stderr"); c != nil {
194 + c.Write(zap.ByteString("body", stderr))
195 }
196
197 return f.rwc.Close()
modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
+7 -4
@@ -148,10 +148,13 @@ func (t Transport) RoundTrip(r *http.Request) (*http.Response, error) {
148 zap.Object("request", loggableReq),
149 zap.Object("env", loggableEnv),
150 )
151 - logger.Debug("roundtrip",
152 - zap.String("dial", address),
153 - zap.Object("env", loggableEnv),
154 - zap.Object("request", loggableReq))
151 + if c := t.logger.Check(zapcore.DebugLevel, "roundtrip"); c != nil {
152 + c.Write(
153 + zap.String("dial", address),
154 + zap.Object("env", loggableEnv),
155 + zap.Object("request", loggableReq),
156 + )
157 + }
158
159 // connect to the backend
160 dialer := net.Dialer{Timeout: time.Duration(t.DialTimeout)}
modules/caddyhttp/reverseproxy/healthchecks.go
+98 -56
@@ -28,6 +28,7 @@ import (
28 "time"
29
30 "go.uber.org/zap"
31 + "go.uber.org/zap/zapcore"
32
33 "github.com/caddyserver/caddy/v2"
34 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -270,9 +271,12 @@ type CircuitBreaker interface {
271 func (h *Handler) activeHealthChecker() {
272 defer func() {
273 if err := recover(); err != nil {
273 - h.HealthChecks.Active.logger.Error("active health checker panicked",
274 - zap.Any("error", err),
275 - zap.ByteString("stack", debug.Stack()))
274 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health checker panicked"); c != nil {
275 + c.Write(
276 + zap.Any("error", err),
277 + zap.ByteString("stack", debug.Stack()),
278 + )
279 + }
280 }
281 }()
282 ticker := time.NewTicker(time.Duration(h.HealthChecks.Active.Interval))
@@ -295,26 +299,33 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
299 go func(upstream *Upstream) {
300 defer func() {
301 if err := recover(); err != nil {
298 - h.HealthChecks.Active.logger.Error("active health check panicked",
299 - zap.Any("error", err),
300 - zap.ByteString("stack", debug.Stack()))
302 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health checker panicked"); c != nil {
303 + c.Write(
304 + zap.Any("error", err),
305 + zap.ByteString("stack", debug.Stack()),
306 + )
307 + }
308 }
309 }()
310
311 networkAddr, err := caddy.NewReplacer().ReplaceOrErr(upstream.Dial, true, true)
312 if err != nil {
306 - h.HealthChecks.Active.logger.Error("invalid use of placeholders in dial address for active health checks",
307 - zap.String("address", networkAddr),
308 - zap.Error(err),
309 - )
313 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "invalid use of placeholders in dial address for active health checks"); c != nil {
314 + c.Write(
315 + zap.String("address", networkAddr),
316 + zap.Error(err),
317 + )
318 + }
319 return
320 }
321 addr, err := caddy.ParseNetworkAddress(networkAddr)
322 if err != nil {
314 - h.HealthChecks.Active.logger.Error("bad network address",
315 - zap.String("address", networkAddr),
316 - zap.Error(err),
317 - )
323 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "bad network address"); c != nil {
324 + c.Write(
325 + zap.String("address", networkAddr),
326 + zap.Error(err),
327 + )
328 + }
329 return
330 }
331 if hcp := uint(upstream.activeHealthCheckPort); hcp != 0 {
@@ -324,9 +335,11 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
335 addr.StartPort, addr.EndPort = hcp, hcp
336 }
337 if addr.PortRangeSize() != 1 {
327 - h.HealthChecks.Active.logger.Error("multiple addresses (upstream must map to only one address)",
328 - zap.String("address", networkAddr),
329 - )
338 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "multiple addresses (upstream must map to only one address)"); c != nil {
339 + c.Write(
340 + zap.String("address", networkAddr),
341 + )
342 + }
343 return
344 }
345 hostAddr := addr.JoinHostPort(0)
@@ -339,10 +352,12 @@ func (h *Handler) doActiveHealthCheckForAllHosts() {
352 }
353 err = h.doActiveHealthCheck(DialInfo{Network: addr.Network, Address: dialAddr}, hostAddr, networkAddr, upstream)
354 if err != nil {
342 - h.HealthChecks.Active.logger.Error("active health check failed",
343 - zap.String("address", hostAddr),
344 - zap.Error(err),
345 - )
355 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "active health check failed"); c != nil {
356 + c.Write(
357 + zap.String("address", hostAddr),
358 + zap.Error(err),
359 + )
360 + }
361 }
362 }(upstream)
363 }
@@ -441,9 +456,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
456 // increment failures and then check if it has reached the threshold to mark unhealthy
457 err := upstream.Host.countHealthFail(1)
458 if err != nil {
444 - h.HealthChecks.Active.logger.Error("could not count active health failure",
445 - zap.String("host", upstream.Dial),
446 - zap.Error(err))
459 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count active health failure"); c != nil {
460 + c.Write(
461 + zap.String("host", upstream.Dial),
462 + zap.Error(err),
463 + )
464 + }
465 return
466 }
467 if upstream.Host.activeHealthFails() >= h.HealthChecks.Active.Fails {
@@ -459,14 +477,19 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
477 // increment passes and then check if it has reached the threshold to be healthy
478 err := upstream.Host.countHealthPass(1)
479 if err != nil {
462 - h.HealthChecks.Active.logger.Error("could not count active health pass",
463 - zap.String("host", upstream.Dial),
464 - zap.Error(err))
480 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count active health pass"); c != nil {
481 + c.Write(
482 + zap.String("host", upstream.Dial),
483 + zap.Error(err),
484 + )
485 + }
486 return
487 }
488 if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes {
489 if upstream.setHealthy(true) {
469 - h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr))
490 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "host is up"); c != nil {
491 + c.Write(zap.String("host", hostAddr))
492 + }
493 h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr})
494 upstream.Host.resetHealth()
495 }
@@ -476,10 +499,12 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
499 // do the request, being careful to tame the response body
500 resp, err := h.HealthChecks.Active.httpClient.Do(req)
501 if err != nil {
479 - h.HealthChecks.Active.logger.Info("HTTP request failed",
480 - zap.String("host", hostAddr),
481 - zap.Error(err),
482 - )
502 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "HTTP request failed"); c != nil {
503 + c.Write(
504 + zap.String("host", hostAddr),
505 + zap.Error(err),
506 + )
507 + }
508 markUnhealthy()
509 return nil
510 }
@@ -496,18 +521,22 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
521 // if status code is outside criteria, mark down
522 if h.HealthChecks.Active.ExpectStatus > 0 {
523 if !caddyhttp.StatusCodeMatches(resp.StatusCode, h.HealthChecks.Active.ExpectStatus) {
499 - h.HealthChecks.Active.logger.Info("unexpected status code",
500 - zap.Int("status_code", resp.StatusCode),
501 - zap.String("host", hostAddr),
502 - )
524 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "unexpected status code"); c != nil {
525 + c.Write(
526 + zap.Int("status_code", resp.StatusCode),
527 + zap.String("host", hostAddr),
528 + )
529 + }
530 markUnhealthy()
531 return nil
532 }
533 } else if resp.StatusCode < 200 || resp.StatusCode >= 300 {
507 - h.HealthChecks.Active.logger.Info("status code out of tolerances",
508 - zap.Int("status_code", resp.StatusCode),
509 - zap.String("host", hostAddr),
510 - )
534 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "status code out of tolerances"); c != nil {
535 + c.Write(
536 + zap.Int("status_code", resp.StatusCode),
537 + zap.String("host", hostAddr),
538 + )
539 + }
540 markUnhealthy()
541 return nil
542 }
@@ -516,17 +545,21 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
545 if h.HealthChecks.Active.bodyRegexp != nil {
546 bodyBytes, err := io.ReadAll(body)
547 if err != nil {
519 - h.HealthChecks.Active.logger.Info("failed to read response body",
520 - zap.String("host", hostAddr),
521 - zap.Error(err),
522 - )
548 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "failed to read response body"); c != nil {
549 + c.Write(
550 + zap.String("host", hostAddr),
551 + zap.Error(err),
552 + )
553 + }
554 markUnhealthy()
555 return nil
556 }
557 if !h.HealthChecks.Active.bodyRegexp.Match(bodyBytes) {
527 - h.HealthChecks.Active.logger.Info("response body failed expectations",
528 - zap.String("host", hostAddr),
529 - )
558 + if c := h.HealthChecks.Active.logger.Check(zapcore.InfoLevel, "response body failed expectations"); c != nil {
559 + c.Write(
560 + zap.String("host", hostAddr),
561 + )
562 + }
563 markUnhealthy()
564 return nil
565 }
@@ -556,9 +589,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
589 // count failure immediately
590 err := upstream.Host.countFail(1)
591 if err != nil {
559 - h.HealthChecks.Passive.logger.Error("could not count failure",
560 - zap.String("host", upstream.Dial),
561 - zap.Error(err))
592 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count failure"); c != nil {
593 + c.Write(
594 + zap.String("host", upstream.Dial),
595 + zap.Error(err),
596 + )
597 + }
598 return
599 }
600
@@ -566,9 +602,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
602 go func(host *Host, failDuration time.Duration) {
603 defer func() {
604 if err := recover(); err != nil {
569 - h.HealthChecks.Passive.logger.Error("passive health check failure forgetter panicked",
570 - zap.Any("error", err),
571 - zap.ByteString("stack", debug.Stack()))
605 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "passive health check failure forgetter panicked"); c != nil {
606 + c.Write(
607 + zap.Any("error", err),
608 + zap.ByteString("stack", debug.Stack()),
609 + )
610 + }
611 }
612 }()
613 timer := time.NewTimer(failDuration)
@@ -581,9 +620,12 @@ func (h *Handler) countFailure(upstream *Upstream) {
620 }
621 err := host.countFail(-1)
622 if err != nil {
584 - h.HealthChecks.Passive.logger.Error("could not forget failure",
585 - zap.String("host", upstream.Dial),
586 - zap.Error(err))
623 + if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not forget failure"); c != nil {
624 + c.Write(
625 + zap.String("host", upstream.Dial),
626 + zap.Error(err),
627 + )
628 + }
629 }
630 }(upstream.Host, failDuration)
631 }
modules/caddyhttp/reverseproxy/httptransport.go
+7 -2
@@ -33,6 +33,7 @@ import (
33 "github.com/pires/go-proxyproto"
34 "github.com/quic-go/quic-go/http3"
35 "go.uber.org/zap"
36 + "go.uber.org/zap/zapcore"
37 "golang.org/x/net/http2"
38
39 "github.com/caddyserver/caddy/v2"
@@ -750,7 +751,9 @@ func (c *tcpRWTimeoutConn) Read(b []byte) (int, error) {
751 if c.readTimeout > 0 {
752 err := c.TCPConn.SetReadDeadline(time.Now().Add(c.readTimeout))
753 if err != nil {
753 - c.logger.Error("failed to set read deadline", zap.Error(err))
754 + if ce := c.logger.Check(zapcore.ErrorLevel, "failed to set read deadline"); ce != nil {
755 + ce.Write(zap.Error(err))
756 + }
757 }
758 }
759 return c.TCPConn.Read(b)
@@ -760,7 +763,9 @@ func (c *tcpRWTimeoutConn) Write(b []byte) (int, error) {
763 if c.writeTimeout > 0 {
764 err := c.TCPConn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
765 if err != nil {
763 - c.logger.Error("failed to set write deadline", zap.Error(err))
766 + if ce := c.logger.Check(zapcore.ErrorLevel, "failed to set write deadline"); ce != nil {
767 + ce.Write(zap.Error(err))
768 + }
769 }
770 }
771 return c.TCPConn.Write(b)
modules/caddyhttp/reverseproxy/metrics.go
+7 -3
@@ -8,6 +8,7 @@ import (
8 "github.com/prometheus/client_golang/prometheus"
9 "github.com/prometheus/client_golang/prometheus/promauto"
10 "go.uber.org/zap"
11 + "go.uber.org/zap/zapcore"
12 )
13
14 var reverseProxyMetrics = struct {
@@ -48,9 +49,12 @@ func (m *metricsUpstreamsHealthyUpdater) Init() {
49 go func() {
50 defer func() {
51 if err := recover(); err != nil {
51 - reverseProxyMetrics.logger.Error("upstreams healthy metrics updater panicked",
52 - zap.Any("error", err),
53 - zap.ByteString("stack", debug.Stack()))
52 + if c := reverseProxyMetrics.logger.Check(zapcore.ErrorLevel, "upstreams healthy metrics updater panicked"); c != nil {
53 + c.Write(
54 + zap.Any("error", err),
55 + zap.ByteString("stack", debug.Stack()),
56 + )
57 + }
58 }
59 }()
60
modules/caddyhttp/reverseproxy/reverseproxy.go
+38 -16
@@ -33,6 +33,7 @@ import (
33 "time"
34
35 "go.uber.org/zap"
36 + "go.uber.org/zap/zapcore"
37 "golang.org/x/net/http/httpguts"
38
39 "github.com/caddyserver/caddy/v2"
@@ -439,7 +440,9 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
440 if h.LoadBalancing != nil {
441 lbWait = time.Duration(h.LoadBalancing.TryInterval)
442 }
442 - h.logger.Debug("retrying", zap.Error(proxyErr), zap.Duration("after", lbWait))
443 + if c := h.logger.Check(zapcore.DebugLevel, "retrying"); c != nil {
444 + c.Write(zap.Error(proxyErr), zap.Duration("after", lbWait))
445 + }
446 }
447 retries++
448 }
@@ -466,13 +469,17 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
469 if h.DynamicUpstreams != nil {
470 dUpstreams, err := h.DynamicUpstreams.GetUpstreams(r)
471 if err != nil {
469 - h.logger.Error("failed getting dynamic upstreams; falling back to static upstreams", zap.Error(err))
472 + if c := h.logger.Check(zapcore.ErrorLevel, "failed getting dynamic upstreams; falling back to static upstreams"); c != nil {
473 + c.Write(zap.Error(err))
474 + }
475 } else {
476 upstreams = dUpstreams
477 for _, dUp := range dUpstreams {
478 h.provisionUpstream(dUp)
479 }
475 - h.logger.Debug("provisioned dynamic upstreams", zap.Int("count", len(dUpstreams)))
480 + if c := h.logger.Check(zapcore.DebugLevel, "provisioned dynamic upstreams"); c != nil {
481 + c.Write(zap.Int("count", len(dUpstreams)))
482 + }
483 defer func() {
484 // these upstreams are dynamic, so they are only used for this iteration
485 // of the proxy loop; be sure to let them go away when we're done with them
@@ -503,9 +510,12 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
510 return true, fmt.Errorf("making dial info: %v", err)
511 }
512
506 - h.logger.Debug("selected upstream",
507 - zap.String("dial", dialInfo.Address),
508 - zap.Int("total_upstreams", len(upstreams)))
513 + if c := h.logger.Check(zapcore.DebugLevel, "selected upstream"); c != nil {
514 + c.Write(
515 + zap.String("dial", dialInfo.Address),
516 + zap.Int("total_upstreams", len(upstreams)),
517 + )
518 + }
519
520 // attach to the request information about how to dial the upstream;
521 // this is necessary because the information cannot be sufficiently
@@ -811,16 +821,22 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe
821 ShouldLogCredentials: shouldLogCredentials,
822 }),
823 )
824 +
825 if err != nil {
815 - logger.Debug("upstream roundtrip", zap.Error(err))
826 + if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil {
827 + c.Write(zap.Error(err))
828 + }
829 return err
830 }
818 - logger.Debug("upstream roundtrip",
819 - zap.Object("headers", caddyhttp.LoggableHTTPHeader{
820 - Header: res.Header,
821 - ShouldLogCredentials: shouldLogCredentials,
822 - }),
823 - zap.Int("status", res.StatusCode))
831 + if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil {
832 + c.Write(
833 + zap.Object("headers", caddyhttp.LoggableHTTPHeader{
834 + Header: res.Header,
835 + ShouldLogCredentials: shouldLogCredentials,
836 + }),
837 + zap.Int("status", res.StatusCode),
838 + )
839 + }
840
841 // duration until upstream wrote response headers (roundtrip duration)
842 repl.Set("http.reverse_proxy.upstream.latency", duration)
@@ -879,7 +895,9 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe
895 repl.Set("http.reverse_proxy.status_code", res.StatusCode)
896 repl.Set("http.reverse_proxy.status_text", res.Status)
897
882 - logger.Debug("handling response", zap.Int("handler", i))
898 + if c := logger.Check(zapcore.DebugLevel, "handling response"); c != nil {
899 + c.Write(zap.Int("handler", i))
900 + }
901
902 // we make some data available via request context to child routes
903 // so that they may inherit some options and functions from the
@@ -975,7 +993,9 @@ func (h *Handler) finalizeResponse(
993 err := h.copyResponse(rw, res.Body, h.flushInterval(req, res), logger)
994 errClose := res.Body.Close() // close now, instead of defer, to populate res.Trailer
995 if h.VerboseLogs || errClose != nil {
978 - logger.Debug("closed response body from upstream", zap.Error(errClose))
996 + if c := logger.Check(zapcore.DebugLevel, "closed response body from upstream"); c != nil {
997 + c.Write(zap.Error(errClose))
998 + }
999 }
1000 if err != nil {
1001 // we're streaming the response and we've already written headers, so
@@ -983,7 +1003,9 @@ func (h *Handler) finalizeResponse(
1003 // we'll just log the error and abort the stream here and panic just as
1004 // the standard lib's proxy to propagate the stream error.
1005 // see issue https://github.com/caddyserver/caddy/issues/5951
986 - logger.Warn("aborting with incomplete response", zap.Error(err))
1006 + if c := logger.Check(zapcore.WarnLevel, "aborting with incomplete response"); c != nil {
1007 + c.Write(zap.Error(err))
1008 + }
1009 // no extra logging from stdlib
1010 panic(http.ErrAbortHandler)
1011 }
modules/caddyhttp/reverseproxy/streaming.go
+59 -25
@@ -31,6 +31,7 @@ import (
31 "unsafe"
32
33 "go.uber.org/zap"
34 + "go.uber.org/zap/zapcore"
35 "golang.org/x/net/http/httpguts"
36 )
37
@@ -41,14 +42,18 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
42 // Taken from https://github.com/golang/go/commit/5c489514bc5e61ad9b5b07bd7d8ec65d66a0512a
43 // We know reqUpType is ASCII, it's checked by the caller.
44 if !asciiIsPrint(resUpType) {
44 - logger.Debug("backend tried to switch to invalid protocol",
45 - zap.String("backend_upgrade", resUpType))
45 + if c := logger.Check(zapcore.DebugLevel, "backend tried to switch to invalid protocol"); c != nil {
46 + c.Write(zap.String("backend_upgrade", resUpType))
47 + }
48 return
49 }
50 if !asciiEqualFold(reqUpType, resUpType) {
49 - logger.Debug("backend tried to switch to unexpected protocol via Upgrade header",
50 - zap.String("backend_upgrade", resUpType),
51 - zap.String("requested_upgrade", reqUpType))
51 + if c := logger.Check(zapcore.DebugLevel, "backend tried to switch to unexpected protocol via Upgrade header"); c != nil {
52 + c.Write(
53 + zap.String("backend_upgrade", resUpType),
54 + zap.String("requested_upgrade", reqUpType),
55 + )
56 + }
57 return
58 }
59
@@ -68,12 +73,16 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
73 //nolint:bodyclose
74 conn, brw, hijackErr := http.NewResponseController(rw).Hijack()
75 if errors.Is(hijackErr, http.ErrNotSupported) {
71 - h.logger.Error("can't switch protocols using non-Hijacker ResponseWriter", zap.String("type", fmt.Sprintf("%T", rw)))
76 + if c := logger.Check(zapcore.ErrorLevel, "can't switch protocols using non-Hijacker ResponseWriter"); c != nil {
77 + c.Write(zap.String("type", fmt.Sprintf("%T", rw)))
78 + }
79 return
80 }
81
82 if hijackErr != nil {
76 - h.logger.Error("hijack failed on protocol switch", zap.Error(hijackErr))
83 + if c := logger.Check(zapcore.ErrorLevel, "hijack failed on protocol switch"); c != nil {
84 + c.Write(zap.Error(hijackErr))
85 + }
86 return
87 }
88
@@ -93,11 +102,15 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
102 start := time.Now()
103 defer func() {
104 conn.Close()
96 - logger.Debug("connection closed", zap.Duration("duration", time.Since(start)))
105 + if c := logger.Check(zapcore.DebugLevel, "hijack failed on protocol switch"); c != nil {
106 + c.Write(zap.Duration("duration", time.Since(start)))
107 + }
108 }()
109
110 if err := brw.Flush(); err != nil {
100 - logger.Debug("response flush", zap.Error(err))
111 + if c := logger.Check(zapcore.DebugLevel, "response flush"); c != nil {
112 + c.Write(zap.Error(err))
113 + }
114 return
115 }
116
@@ -107,7 +120,9 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
120 data, _ := brw.Peek(buffered)
121 _, err := backConn.Write(data)
122 if err != nil {
110 - logger.Debug("backConn write failed", zap.Error(err))
123 + if c := logger.Check(zapcore.DebugLevel, "backConn write failed"); c != nil {
124 + c.Write(zap.Error(err))
125 + }
126 return
127 }
128 }
@@ -148,9 +163,13 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
163 go spc.copyFromBackend(errc)
164 select {
165 case err := <-errc:
151 - logger.Debug("streaming error", zap.Error(err))
166 + if c := logger.Check(zapcore.DebugLevel, "streaming error"); c != nil {
167 + c.Write(zap.Error(err))
168 + }
169 case time := <-timeoutc:
153 - logger.Debug("stream timed out", zap.Time("timeout", time))
170 + if c := logger.Check(zapcore.DebugLevel, "stream timed out"); c != nil {
171 + c.Write(zap.Time("timeout", time))
172 + }
173 }
174 }
175
@@ -247,7 +266,9 @@ func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte, logger *za
266 logger.Debug("waiting to read from upstream")
267 nr, rerr := src.Read(buf)
268 logger := logger.With(zap.Int("read", nr))
250 - logger.Debug("read from upstream", zap.Error(rerr))
269 + if c := logger.Check(zapcore.DebugLevel, "read from upstream"); c != nil {
270 + c.Write(zap.Error(rerr))
271 + }
272 if rerr != nil && rerr != io.EOF && rerr != context.Canceled {
273 // TODO: this could be useful to know (indeed, it revealed an error in our
274 // fastcgi PoC earlier; but it's this single error report here that necessitates
@@ -256,7 +277,9 @@ func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte, logger *za
277 // something we need to report to the client, but read errors are a problem on our
278 // end for sure. so we need to decide what we want.)
279 // p.logf("copyBuffer: ReverseProxy read error during body copy: %v", rerr)
259 - h.logger.Error("reading from backend", zap.Error(rerr))
280 + if c := logger.Check(zapcore.ErrorLevel, "reading from backend"); c != nil {
281 + c.Write(zap.Error(rerr))
282 + }
283 }
284 if nr > 0 {
285 logger.Debug("writing to downstream")
@@ -264,10 +287,13 @@ func (h Handler) copyBuffer(dst io.Writer, src io.Reader, buf []byte, logger *za
287 if nw > 0 {
288 written += int64(nw)
289 }
267 - logger.Debug("wrote to downstream",
268 - zap.Int("written", nw),
269 - zap.Int64("written_total", written),
270 - zap.Error(werr))
290 + if c := logger.Check(zapcore.DebugLevel, "wrote to downstream"); c != nil {
291 + c.Write(
292 + zap.Int("written", nw),
293 + zap.Int64("written_total", written),
294 + zap.Error(werr),
295 + )
296 + }
297 if werr != nil {
298 return written, fmt.Errorf("writing: %w", werr)
299 }
@@ -347,13 +373,17 @@ func (h *Handler) cleanupConnections() error {
373 if len(h.connections) > 0 {
374 delay := time.Duration(h.StreamCloseDelay)
375 h.connectionsCloseTimer = time.AfterFunc(delay, func() {
350 - h.logger.Debug("closing streaming connections after delay",
351 - zap.Duration("delay", delay))
376 + if c := h.logger.Check(zapcore.DebugLevel, "closing streaming connections after delay"); c != nil {
377 + c.Write(zap.Duration("delay", delay))
378 + }
379 err := h.closeConnections()
380 if err != nil {
354 - h.logger.Error("failed to closed connections after delay",
355 - zap.Error(err),
356 - zap.Duration("delay", delay))
381 + if c := h.logger.Check(zapcore.ErrorLevel, "failed to closed connections after delay"); c != nil {
382 + c.Write(
383 + zap.Error(err),
384 + zap.Duration("delay", delay),
385 + )
386 + }
387 }
388 })
389 }
@@ -494,7 +524,9 @@ func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
524 m.mu.Lock()
525 defer m.mu.Unlock()
526 n, err = m.dst.Write(p)
497 - m.logger.Debug("wrote bytes", zap.Int("n", n), zap.Error(err))
527 + if c := m.logger.Check(zapcore.DebugLevel, "wrote bytes"); c != nil {
528 + c.Write(zap.Int("n", n), zap.Error(err))
529 + }
530 if m.latency < 0 {
531 m.logger.Debug("flushing immediately")
532 //nolint:errcheck
@@ -510,7 +542,9 @@ func (m *maxLatencyWriter) Write(p []byte) (n int, err error) {
542 } else {
543 m.t.Reset(m.latency)
544 }
513 - m.logger.Debug("timer set for delayed flush", zap.Duration("duration", m.latency))
545 + if c := m.logger.Check(zapcore.DebugLevel, "timer set for delayed flush"); c != nil {
546 + c.Write(zap.Duration("duration", m.latency))
547 + }
548 m.flushPending = true
549 return
550 }
modules/caddyhttp/reverseproxy/upstreams.go
+41 -21
@@ -12,6 +12,7 @@ import (
12 "time"
13
14 "go.uber.org/zap"
15 + "go.uber.org/zap/zapcore"
16
17 "github.com/caddyserver/caddy/v2"
18 )
@@ -136,10 +137,13 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
137 return allNew(cached.upstreams), nil
138 }
139
139 - su.logger.Debug("refreshing SRV upstreams",
140 - zap.String("service", service),
141 - zap.String("proto", proto),
142 - zap.String("name", name))
140 + if c := su.logger.Check(zapcore.DebugLevel, "refreshing SRV upstreams"); c != nil {
141 + c.Write(
142 + zap.String("service", service),
143 + zap.String("proto", proto),
144 + zap.String("name", name),
145 + )
146 + }
147
148 _, records, err := su.resolver.LookupSRV(r.Context(), service, proto, name)
149 if err != nil {
@@ -148,23 +152,30 @@ func (su SRVUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
152 // only return an error if no records were also returned.
153 if len(records) == 0 {
154 if su.GracePeriod > 0 {
151 - su.logger.Error("SRV lookup failed; using previously cached", zap.Error(err))
155 + if c := su.logger.Check(zapcore.ErrorLevel, "SRV lookup failed; using previously cached"); c != nil {
156 + c.Write(zap.Error(err))
157 + }
158 cached.freshness = time.Now().Add(time.Duration(su.GracePeriod) - time.Duration(su.Refresh))
159 srvs[suAddr] = cached
160 return allNew(cached.upstreams), nil
161 }
162 return nil, err
163 }
158 - su.logger.Warn("SRV records filtered", zap.Error(err))
164 + if c := su.logger.Check(zapcore.WarnLevel, "SRV records filtered"); c != nil {
165 + c.Write(zap.Error(err))
166 + }
167 }
168
169 upstreams := make([]Upstream, len(records))
170 for i, rec := range records {
163 - su.logger.Debug("discovered SRV record",
164 - zap.String("target", rec.Target),
165 - zap.Uint16("port", rec.Port),
166 - zap.Uint16("priority", rec.Priority),
167 - zap.Uint16("weight", rec.Weight))
171 + if c := su.logger.Check(zapcore.DebugLevel, "discovered SRV record"); c != nil {
172 + c.Write(
173 + zap.String("target", rec.Target),
174 + zap.Uint16("port", rec.Port),
175 + zap.Uint16("priority", rec.Priority),
176 + zap.Uint16("weight", rec.Weight),
177 + )
178 + }
179 addr := net.JoinHostPort(rec.Target, strconv.Itoa(int(rec.Port)))
180 upstreams[i] = Upstream{Dial: addr}
181 }
@@ -361,10 +372,13 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
372 name := repl.ReplaceAll(au.Name, "")
373 port := repl.ReplaceAll(au.Port, "")
374
364 - au.logger.Debug("refreshing A upstreams",
365 - zap.String("version", ipVersion),
366 - zap.String("name", name),
367 - zap.String("port", port))
375 + if c := au.logger.Check(zapcore.DebugLevel, "refreshing A upstreams"); c != nil {
376 + c.Write(
377 + zap.String("version", ipVersion),
378 + zap.String("name", name),
379 + zap.String("port", port),
380 + )
381 + }
382
383 ips, err := au.resolver.LookupIP(r.Context(), ipVersion, name)
384 if err != nil {
@@ -373,8 +387,9 @@ func (au AUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
387
388 upstreams := make([]Upstream, len(ips))
389 for i, ip := range ips {
376 - au.logger.Debug("discovered A record",
377 - zap.String("ip", ip.String()))
390 + if c := au.logger.Check(zapcore.DebugLevel, "discovered A record"); c != nil {
391 + c.Write(zap.String("ip", ip.String()))
392 + }
393 upstreams[i] = Upstream{
394 Dial: net.JoinHostPort(ip.String(), port),
395 }
@@ -467,11 +482,16 @@ func (mu MultiUpstreams) GetUpstreams(r *http.Request) ([]*Upstream, error) {
482
483 up, err := src.GetUpstreams(r)
484 if err != nil {
470 - mu.logger.Error("upstream source returned error",
471 - zap.Int("source_idx", i),
472 - zap.Error(err))
485 + if c := mu.logger.Check(zapcore.ErrorLevel, "upstream source returned error"); c != nil {
486 + c.Write(
487 + zap.Int("source_idx", i),
488 + zap.Error(err),
489 + )
490 + }
491 } else if len(up) == 0 {
474 - mu.logger.Warn("upstream source returned 0 upstreams", zap.Int("source_idx", i))
492 + if c := mu.logger.Check(zapcore.WarnLevel, "upstream source returned 0 upstreams"); c != nil {
493 + c.Write(zap.Int("source_idx", i))
494 + }
495 } else {
496 upstreams = append(upstreams, up...)
497 }
modules/caddyhttp/rewrite/rewrite.go
+4 -6
@@ -133,19 +133,17 @@ func (rewr Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy
133 repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
134 const message = "rewrote request"
135
136 - if rewr.logger.Check(zap.DebugLevel, message) == nil {
136 + c := rewr.logger.Check(zap.DebugLevel, message)
137 + if c == nil {
138 rewr.Rewrite(r, repl)
139 return next.ServeHTTP(w, r)
140 }
141
141 - logger := rewr.logger.With(
142 - zap.Object("request", caddyhttp.LoggableHTTPRequest{Request: r}),
143 - )
144 -
142 changed := rewr.Rewrite(r, repl)
143
144 if changed {
148 - logger.Debug(message,
145 + c.Write(
146 + zap.Object("request", caddyhttp.LoggableHTTPRequest{Request: r}),
147 zap.String("method", r.Method),
148 zap.String("uri", r.RequestURI),
149 )
modules/caddyhttp/server.go
+91 -50
@@ -275,7 +275,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
275 if r.ProtoMajor < 3 {
276 err := s.h3server.SetQUICHeaders(w.Header())
277 if err != nil {
278 - s.logger.Error("setting HTTP/3 Alt-Svc header", zap.Error(err))
278 + if c := s.logger.Check(zapcore.ErrorLevel, "setting HTTP/3 Alt-Svc header"); c != nil {
279 + c.Write(zap.Error(err))
280 + }
281 }
282 }
283 }
@@ -283,9 +285,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
285 // reject very long methods; probably a mistake or an attack
286 if len(r.Method) > 32 {
287 if s.shouldLogRequest(r) {
286 - s.accessLogger.Debug("rejecting request with long method",
287 - zap.String("method_trunc", r.Method[:32]),
288 - zap.String("remote_addr", r.RemoteAddr))
288 + if c := s.accessLogger.Check(zapcore.DebugLevel, "rejecting request with long method"); c != nil {
289 + c.Write(
290 + zap.String("method_trunc", r.Method[:32]),
291 + zap.String("remote_addr", r.RemoteAddr),
292 + )
293 + }
294 }
295 w.WriteHeader(http.StatusMethodNotAllowed)
296 return
@@ -300,7 +305,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
305 //nolint:bodyclose
306 err := http.NewResponseController(w).EnableFullDuplex()
307 if err != nil {
303 - s.logger.Warn("failed to enable full duplex", zap.Error(err))
308 + if c := s.logger.Check(zapcore.WarnLevel, "failed to enable full duplex"); c != nil {
309 + c.Write(zap.Error(err))
310 + }
311 }
312 }
313
@@ -379,6 +386,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
386 // add HTTP error information to request context
387 r = s.Errors.WithError(r, err)
388
389 + var fields []zapcore.Field
390 if s.Errors != nil && len(s.Errors.Routes) > 0 {
391 // execute user-defined error handling route
392 err2 := s.errorHandlerChain.ServeHTTP(w, r)
@@ -386,17 +394,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
394 // user's error route handled the error response
395 // successfully, so now just log the error
396 for _, logger := range errLoggers {
389 - logger.Debug(errMsg, errFields...)
397 + if c := logger.Check(zapcore.DebugLevel, errMsg); c != nil {
398 + if fields == nil {
399 + fields = errFields()
400 + }
401 +
402 + c.Write(fields...)
403 + }
404 }
405 } else {
406 // well... this is awkward
393 - errFields = append([]zapcore.Field{
394 - zap.String("error", err2.Error()),
395 - zap.Namespace("first_error"),
396 - zap.String("msg", errMsg),
397 - }, errFields...)
407 for _, logger := range errLoggers {
399 - logger.Error("error handling handler error", errFields...)
408 + if c := logger.Check(zapcore.ErrorLevel, "error handling handler error"); c != nil {
409 + if fields == nil {
410 + fields = errFields()
411 + fields = append([]zapcore.Field{
412 + zap.String("error", err2.Error()),
413 + zap.Namespace("first_error"),
414 + zap.String("msg", errMsg),
415 + }, fields...)
416 + }
417 + c.Write(fields...)
418 + }
419 }
420 if handlerErr, ok := err.(HandlerError); ok {
421 w.WriteHeader(handlerErr.StatusCode)
@@ -405,11 +424,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
424 }
425 }
426 } else {
427 + logLevel := zapcore.DebugLevel
428 + if errStatus >= 500 {
429 + logLevel = zapcore.ErrorLevel
430 + }
431 +
432 for _, logger := range errLoggers {
409 - if errStatus >= 500 {
410 - logger.Error(errMsg, errFields...)
411 - } else {
412 - logger.Debug(errMsg, errFields...)
433 + if c := logger.Check(logLevel, errMsg); c != nil {
434 + if fields == nil {
435 + fields = errFields()
436 + }
437 + c.Write(fields...)
438 }
439 }
440 w.WriteHeader(errStatus)
@@ -746,7 +771,9 @@ func (s *Server) logTrace(mh MiddlewareHandler) {
771 if s.Logs == nil || !s.Logs.Trace {
772 return
773 }
749 - s.traceLogger.Debug(caddy.GetModuleName(mh), zap.Any("module", mh))
774 + if c := s.traceLogger.Check(zapcore.DebugLevel, caddy.GetModuleName(mh)); c != nil {
775 + c.Write(zap.Any("module", mh))
776 + }
777 }
778
779 // logRequest logs the request to access logs, unless skipped.
@@ -759,50 +786,64 @@ func (s *Server) logRequest(
786 return
787 }
788
762 - repl.Set("http.response.status", wrec.Status()) // will be 0 if no response is written by us (Go will write 200 to client)
763 - repl.Set("http.response.size", wrec.Size())
789 + status := wrec.Status()
790 + size := wrec.Size()
791 +
792 + repl.Set("http.response.status", status) // will be 0 if no response is written by us (Go will write 200 to client)
793 + repl.Set("http.response.size", size)
794 repl.Set("http.response.duration", duration)
795 repl.Set("http.response.duration_ms", duration.Seconds()*1e3) // multiply seconds to preserve decimal (see #4666)
796
767 - userID, _ := repl.GetString("http.auth.user.id")
768 -
769 - reqBodyLength := 0
770 - if bodyReader != nil {
771 - reqBodyLength = bodyReader.Length
772 - }
773 -
774 - extra := r.Context().Value(ExtraLogFieldsCtxKey).(*ExtraLogFields)
775 -
776 - fieldCount := 6
777 - fields := make([]zapcore.Field, 0, fieldCount+len(extra.fields))
778 - fields = append(fields,
779 - zap.Int("bytes_read", reqBodyLength),
780 - zap.String("user_id", userID),
781 - zap.Duration("duration", *duration),
782 - zap.Int("size", wrec.Size()),
783 - zap.Int("status", wrec.Status()),
784 - zap.Object("resp_headers", LoggableHTTPHeader{
785 - Header: wrec.Header(),
786 - ShouldLogCredentials: shouldLogCredentials,
787 - }))
788 - fields = append(fields, extra.fields...)
789 -
797 loggers := []*zap.Logger{accLog}
798 if s.Logs != nil {
799 loggers = s.Logs.wrapLogger(accLog, r)
800 }
801
795 - // wrapping may return multiple loggers, so we log to all of them
802 + message := "handled request"
803 + if nop, ok := GetVar(r.Context(), "unhandled").(bool); ok && nop {
804 + message = "NOP"
805 + }
806 +
807 + logLevel := zapcore.InfoLevel
808 + if status >= 500 {
809 + logLevel = zapcore.ErrorLevel
810 + }
811 +
812 + var fields []zapcore.Field
813 for _, logger := range loggers {
797 - logAtLevel := logger.Info
798 - if wrec.Status() >= 500 {
799 - logAtLevel = logger.Error
814 + c := logger.Check(logLevel, message)
815 + if c == nil {
816 + continue
817 }
801 - message := "handled request"
802 - if nop, ok := GetVar(r.Context(), "unhandled").(bool); ok && nop {
803 - message = "NOP"
818 +
819 + if fields == nil {
820 +
821 + userID, _ := repl.GetString("http.auth.user.id")
822 +
823 + reqBodyLength := 0
824 + if bodyReader != nil {
825 + reqBodyLength = bodyReader.Length
826 + }
827 +
828 + extra := r.Context().Value(ExtraLogFieldsCtxKey).(*ExtraLogFields)
829 +
830 + fieldCount := 6
831 + fields = make([]zapcore.Field, 0, fieldCount+len(extra.fields))
832 + fields = append(fields,
833 + zap.Int("bytes_read", reqBodyLength),
834 + zap.String("user_id", userID),
835 + zap.Duration("duration", *duration),
836 + zap.Int("size", size),
837 + zap.Int("status", status),
838 + zap.Object("resp_headers", LoggableHTTPHeader{
839 + Header: wrec.Header(),
840 + ShouldLogCredentials: shouldLogCredentials,
841 + }),
842 + )
843 + fields = append(fields, extra.fields...)
844 }
805 - logAtLevel(message, fields...)
845 +
846 + c.Write(fields...)
847 }
848 }
849
modules/caddyhttp/server_test.go
+23
@@ -121,6 +121,29 @@ func BenchmarkServer_LogRequest(b *testing.B) {
121 }
122 }
123
124 +func BenchmarkServer_LogRequest_NopLogger(b *testing.B) {
125 + s := &Server{}
126 +
127 + extra := new(ExtraLogFields)
128 + ctx := context.WithValue(context.Background(), ExtraLogFieldsCtxKey, extra)
129 +
130 + req := httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
131 + rec := httptest.NewRecorder()
132 + wrec := NewResponseRecorder(rec, nil, nil)
133 +
134 + duration := 50 * time.Millisecond
135 + repl := NewTestReplacer(req)
136 + bodyReader := &lengthReader{Source: req.Body}
137 +
138 + accLog := zap.NewNop()
139 +
140 + b.ResetTimer()
141 +
142 + for i := 0; i < b.N; i++ {
143 + s.logRequest(accLog, req, wrec, &duration, repl, bodyReader, false)
144 + }
145 +}
146 +
147 func BenchmarkServer_LogRequest_WithTraceID(b *testing.B) {
148 s := &Server{}
149
modules/caddyhttp/tracing/tracerprovider.go
+4 -1
@@ -7,6 +7,7 @@ import (
7
8 sdktrace "go.opentelemetry.io/otel/sdk/trace"
9 "go.uber.org/zap"
10 + "go.uber.org/zap/zapcore"
11 )
12
13 // globalTracerProvider stores global tracer provider and is responsible for graceful shutdown when nobody is using it.
@@ -47,7 +48,9 @@ func (t *tracerProvider) cleanupTracerProvider(logger *zap.Logger) error {
48 if t.tracerProvider != nil {
49 // tracerProvider.ForceFlush SHOULD be invoked according to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#forceflush
50 if err := t.tracerProvider.ForceFlush(context.Background()); err != nil {
50 - logger.Error("forcing flush", zap.Error(err))
51 + if c := logger.Check(zapcore.ErrorLevel, "forcing flush"); c != nil {
52 + c.Write(zap.Error(err))
53 + }
54 }
55
56 // tracerProvider.Shutdown MUST be invoked according to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#shutdown
modules/caddypki/acmeserver/acmeserver.go
+10 -3
@@ -35,6 +35,7 @@ import (
35 "github.com/smallstep/certificates/db"
36 "github.com/smallstep/nosql"
37 "go.uber.org/zap"
38 + "go.uber.org/zap/zapcore"
39
40 "github.com/caddyserver/caddy/v2"
41 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
@@ -243,10 +244,14 @@ func (ash Handler) Cleanup() error {
244 key := ash.getDatabaseKey()
245 deleted, err := databasePool.Delete(key)
246 if deleted {
246 - ash.logger.Debug("unloading unused CA database", zap.String("db_key", key))
247 + if c := ash.logger.Check(zapcore.DebugLevel, "unloading unused CA database"); c != nil {
248 + c.Write(zap.String("db_key", key))
249 + }
250 }
251 if err != nil {
249 - ash.logger.Error("closing CA database", zap.String("db_key", key), zap.Error(err))
252 + if c := ash.logger.Check(zapcore.ErrorLevel, "closing CA database"); c != nil {
253 + c.Write(zap.String("db_key", key), zap.Error(err))
254 + }
255 }
256 return err
257 }
@@ -271,7 +276,9 @@ func (ash Handler) openDatabase() (*db.AuthDB, error) {
276 })
277
278 if loaded {
274 - ash.logger.Debug("loaded preexisting CA database", zap.String("db_key", key))
279 + if c := ash.logger.Check(zapcore.DebugLevel, "loaded preexisting CA database"); c != nil {
280 + c.Write(zap.String("db_key", key))
281 + }
282 }
283
284 return database.(databaseCloser).DB, err
modules/caddytls/acmeissuer.go
+4 -1
@@ -30,6 +30,7 @@ import (
30 "github.com/caddyserver/zerossl"
31 "github.com/mholt/acmez/v2/acme"
32 "go.uber.org/zap"
33 + "go.uber.org/zap/zapcore"
34
35 "github.com/caddyserver/caddy/v2"
36 "github.com/caddyserver/caddy/v2/caddyconfig"
@@ -321,7 +322,9 @@ func (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct a
322 return nil, acct, fmt.Errorf("failed getting EAB credentials: HTTP %d", resp.StatusCode)
323 }
324
324 - iss.logger.Info("generated EAB credentials", zap.String("key_id", result.EABKID))
325 + if c := iss.logger.Check(zapcore.InfoLevel, "generated EAB credentials"); c != nil {
326 + c.Write(zap.String("key_id", result.EABKID))
327 + }
328
329 return &acme.EAB{
330 KeyID: result.EABKID,
modules/caddytls/automation.go
+19 -9
@@ -26,6 +26,7 @@ import (
26 "github.com/caddyserver/certmagic"
27 "github.com/mholt/acmez/v2"
28 "go.uber.org/zap"
29 + "go.uber.org/zap/zapcore"
30
31 "github.com/caddyserver/caddy/v2"
32 )
@@ -292,21 +293,30 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
293 remoteIP, _, _ = net.SplitHostPort(remote.String())
294 }
295 }
295 - tlsApp.logger.Debug("asking for permission for on-demand certificate",
296 - zap.String("remote_ip", remoteIP),
297 - zap.String("domain", name))
296 + if c := tlsApp.logger.Check(zapcore.DebugLevel, "asking for permission for on-demand certificate"); c != nil {
297 + c.Write(
298 + zap.String("remote_ip", remoteIP),
299 + zap.String("domain", name),
300 + )
301 + }
302
303 // ask the permission module if this cert is allowed
304 if err := tlsApp.Automation.OnDemand.permission.CertificateAllowed(ctx, name); err != nil {
305 // distinguish true errors from denials, because it's important to elevate actual errors
306 if errors.Is(err, ErrPermissionDenied) {
303 - tlsApp.logger.Debug("on-demand certificate issuance denied",
304 - zap.String("domain", name),
305 - zap.Error(err))
307 + if c := tlsApp.logger.Check(zapcore.DebugLevel, "on-demand certificate issuance denied"); c != nil {
308 + c.Write(
309 + zap.String("domain", name),
310 + zap.Error(err),
311 + )
312 + }
313 } else {
307 - tlsApp.logger.Error("failed to get permission for on-demand certificate",
308 - zap.String("domain", name),
309 - zap.Error(err))
314 + if c := tlsApp.logger.Check(zapcore.ErrorLevel, "failed to get permission for on-demand certificate"); c != nil {
315 + c.Write(
316 + zap.String("domain", name),
317 + zap.Error(err),
318 + )
319 + }
320 }
321 return err
322 }
modules/caddytls/certmanagers.go
+4 -1
@@ -12,6 +12,7 @@ import (
12 "github.com/caddyserver/certmagic"
13 "github.com/tailscale/tscert"
14 "go.uber.org/zap"
15 + "go.uber.org/zap/zapcore"
16
17 "github.com/caddyserver/caddy/v2"
18 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -46,7 +47,9 @@ func (ts Tailscale) GetCertificate(ctx context.Context, hello *tls.ClientHelloIn
47 return nil, nil // pass-thru: Tailscale can't offer a cert for this name
48 }
49 if err != nil {
49 - ts.logger.Warn("could not get status; will try to get certificate anyway", zap.Error(err))
50 + if c := ts.logger.Check(zapcore.WarnLevel, "could not get status; will try to get certificate anyway"); c != nil {
51 + c.Write(zap.Error(err))
52 + }
53 }
54 return tscert.GetCertificateWithContext(ctx, hello)
55 }
modules/caddytls/connpolicy.go
+4 -2
@@ -28,6 +28,7 @@ import (
28
29 "github.com/mholt/acmez/v2"
30 "go.uber.org/zap"
31 + "go.uber.org/zap/zapcore"
32
33 "github.com/caddyserver/caddy/v2"
34 "github.com/caddyserver/caddy/v2/caddyconfig"
@@ -338,8 +339,9 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
339
340 cfg.KeyLogWriter = logFile.(io.Writer)
341
341 - tlsApp.logger.Warn("TLS SECURITY COMPROMISED: secrets logging is enabled!",
342 - zap.String("log_filename", filename))
342 + if c := tlsApp.logger.Check(zapcore.WarnLevel, "TLS SECURITY COMPROMISED: secrets logging is enabled!"); c != nil {
343 + c.Write(zap.String("log_filename", filename))
344 + }
345 }
346
347 setDefaultTLSParams(cfg)
modules/caddytls/matchers.go
+7 -2
@@ -25,6 +25,7 @@ import (
25
26 "github.com/caddyserver/certmagic"
27 "go.uber.org/zap"
28 + "go.uber.org/zap/zapcore"
29
30 "github.com/caddyserver/caddy/v2"
31 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -291,7 +292,9 @@ func (m MatchRemoteIP) Match(hello *tls.ClientHelloInfo) bool {
292 }
293 ipAddr, err := netip.ParseAddr(ipStr)
294 if err != nil {
294 - m.logger.Error("invalid client IP address", zap.String("ip", ipStr))
295 + if c := m.logger.Check(zapcore.ErrorLevel, "invalid client IP address"); c != nil {
296 + c.Write(zap.String("ip", ipStr))
297 + }
298 return false
299 }
300 return (len(m.cidrs) == 0 || m.matches(ipAddr, m.cidrs)) &&
@@ -408,7 +411,9 @@ func (m MatchLocalIP) Match(hello *tls.ClientHelloInfo) bool {
411 }
412 ipAddr, err := netip.ParseAddr(ipStr)
413 if err != nil {
411 - m.logger.Error("invalid local IP address", zap.String("ip", ipStr))
414 + if c := m.logger.Check(zapcore.ErrorLevel, "invalid local IP address"); c != nil {
415 + c.Write(zap.String("ip", ipStr))
416 + }
417 return false
418 }
419 return (len(m.cidrs) == 0 || m.matches(ipAddr, m.cidrs))
modules/caddytls/ondemand.go
+16 -9
@@ -26,6 +26,7 @@ import (
26
27 "github.com/caddyserver/certmagic"
28 "go.uber.org/zap"
29 + "go.uber.org/zap/zapcore"
30
31 "github.com/caddyserver/caddy/v2"
32 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -156,10 +157,13 @@ func (p PermissionByHTTP) CertificateAllowed(ctx context.Context, name string) e
157 remote = chi.Conn.RemoteAddr().String()
158 }
159
159 - p.logger.Debug("asking permission endpoint",
160 - zap.String("remote", remote),
161 - zap.String("domain", name),
162 - zap.String("url", askURLString))
160 + if c := p.logger.Check(zapcore.DebugLevel, "asking permission endpoint"); c != nil {
161 + c.Write(
162 + zap.String("remote", remote),
163 + zap.String("domain", name),
164 + zap.String("url", askURLString),
165 + )
166 + }
167
168 resp, err := onDemandAskClient.Get(askURLString)
169 if err != nil {
@@ -168,11 +172,14 @@ func (p PermissionByHTTP) CertificateAllowed(ctx context.Context, name string) e
172 }
173 resp.Body.Close()
174
171 - p.logger.Debug("response from permission endpoint",
172 - zap.String("remote", remote),
173 - zap.String("domain", name),
174 - zap.String("url", askURLString),
175 - zap.Int("status", resp.StatusCode))
175 + if c := p.logger.Check(zapcore.DebugLevel, "response from permission endpoint"); c != nil {
176 + c.Write(
177 + zap.String("remote", remote),
178 + zap.String("domain", name),
179 + zap.String("url", askURLString),
180 + zap.Int("status", resp.StatusCode),
181 + )
182 + }
183
184 if resp.StatusCode < 200 || resp.StatusCode > 299 {
185 return fmt.Errorf("%s: %w %s - non-2xx status code %d", name, ErrPermissionDenied, askEndpoint, resp.StatusCode)
modules/caddytls/tls.go
+19 -8
@@ -27,6 +27,7 @@ import (
27
28 "github.com/caddyserver/certmagic"
29 "go.uber.org/zap"
30 + "go.uber.org/zap/zapcore"
31
32 "github.com/caddyserver/caddy/v2"
33 "github.com/caddyserver/caddy/v2/modules/caddyevents"
@@ -323,8 +324,9 @@ func (t *TLS) Start() error {
324 if t.Automation.OnDemand == nil || (t.Automation.OnDemand.Ask == "" && t.Automation.OnDemand.permission == nil) {
325 for _, ap := range t.Automation.Policies {
326 if ap.OnDemand && ap.isWildcardOrDefault() {
326 - t.logger.Warn("YOUR SERVER MAY BE VULNERABLE TO ABUSE: on-demand TLS is enabled, but no protections are in place",
327 - zap.String("docs", "https://caddyserver.com/docs/automatic-https#on-demand-tls"))
327 + if c := t.logger.Check(zapcore.WarnLevel, "YOUR SERVER MAY BE VULNERABLE TO ABUSE: on-demand TLS is enabled, but no protections are in place"); c != nil {
328 + c.Write(zap.String("docs", "https://caddyserver.com/docs/automatic-https#on-demand-tls"))
329 + }
330 break
331 }
332 }
@@ -408,9 +410,12 @@ func (t *TLS) Cleanup() error {
410 // give the new TLS app a "kick" to manage certs that it is configured for
411 // with its own configuration instead of the one we just evicted
412 if err := nextTLSApp.Manage(reManage); err != nil {
411 - t.logger.Error("re-managing unloaded certificates with new config",
412 - zap.Strings("subjects", reManage),
413 - zap.Error(err))
413 + if c := t.logger.Check(zapcore.ErrorLevel, "re-managing unloaded certificates with new config"); c != nil {
414 + c.Write(
415 + zap.Strings("subjects", reManage),
416 + zap.Error(err),
417 + )
418 + }
419 }
420 } else {
421 // no more TLS app running, so delete in-memory cert cache
@@ -653,7 +658,9 @@ func (t *TLS) cleanStorageUnits() {
658
659 id, err := caddy.InstanceID()
660 if err != nil {
656 - t.logger.Warn("unable to get instance ID; storage clean stamps will be incomplete", zap.Error(err))
661 + if c := t.logger.Check(zapcore.WarnLevel, "unable to get instance ID; storage clean stamps will be incomplete"); c != nil {
662 + c.Write(zap.Error(err))
663 + }
664 }
665 options := certmagic.CleanStorageOptions{
666 Logger: t.logger,
@@ -669,7 +676,9 @@ func (t *TLS) cleanStorageUnits() {
676 if err != nil {
677 // probably don't want to return early, since we should still
678 // see if any other storages can get cleaned up
672 - t.logger.Error("could not clean default/global storage", zap.Error(err))
679 + if c := t.logger.Check(zapcore.ErrorLevel, "could not clean default/global storage"); c != nil {
680 + c.Write(zap.Error(err))
681 + }
682 }
683
684 // then clean each storage defined in ACME automation policies
@@ -679,7 +688,9 @@ func (t *TLS) cleanStorageUnits() {
688 continue
689 }
690 if err := certmagic.CleanStorage(t.ctx, ap.storage, options); err != nil {
682 - t.logger.Error("could not clean storage configured in automation policy", zap.Error(err))
691 + if c := t.logger.Check(zapcore.ErrorLevel, "could not clean storage configured in automation policy"); c != nil {
692 + c.Write(zap.Error(err))
693 + }
694 }
695 }
696 }