@marcel91200 / caddy Mirror · read-only / commits / 10db5702
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 14, 05:15 UTC

caddyhttp: General improvements to access logging (#3301)

* httpcaddyfile: Exclude access logs written to files from default log Even though any logs can just be ignored, most users don't seem to like configuring an access log to go to a file only to have it doubly appear in the default log. Related to: - #3294 - https://caddy.community/t/v2-logging-format/7642/4?u=matt - https://caddy.community/t/caddyfile-questions/7651/3?u=matt * caddyhttp: General improvements to access log controls (fixes #3310) * caddyhttp: Move log config nil check higher * Rename LoggerName -> DefaultLoggerName

Matt Holt committed Apr 28, 2020 at 08:32 UTC 10db57027df6925c1afc82db1e7ddd0ff2cc53a2
2 files changed +106 -34
caddyconfig/httpcaddyfile/httptype.go
+62 -25
@@ -38,7 +38,7 @@ type ServerType struct {
38 }
39
40 // Setup makes a config from the tokens.
41 -func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
41 +func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
42 options map[string]interface{}) (*caddy.Config, []caddyconfig.Warning, error) {
43 var warnings []caddyconfig.Warning
44 gc := counter{new(int)}
@@ -50,15 +50,15 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
50 // chosen to handle a request - we actually will make each
51 // server block's route terminal so that only one will run
52 sbKeys := make(map[string]struct{})
53 - serverBlocks := make([]serverBlock, 0, len(originalServerBlocks))
54 - for i, sblock := range originalServerBlocks {
53 + originalServerBlocks := make([]serverBlock, 0, len(inputServerBlocks))
54 + for i, sblock := range inputServerBlocks {
55 for j, k := range sblock.Keys {
56 if _, ok := sbKeys[k]; ok {
57 return nil, warnings, fmt.Errorf("duplicate site address not allowed: '%s' in %v (site block %d, key %d)", k, sblock.Keys, i, j)
58 }
59 sbKeys[k] = struct{}{}
60 }
61 - serverBlocks = append(serverBlocks, serverBlock{
61 + originalServerBlocks = append(originalServerBlocks, serverBlock{
62 block: sblock,
63 pile: make(map[string][]ConfigValue),
64 })
@@ -66,12 +66,12 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
66
67 // apply any global options
68 var err error
69 - serverBlocks, err = st.evaluateGlobalOptionsBlock(serverBlocks, options)
69 + originalServerBlocks, err = st.evaluateGlobalOptionsBlock(originalServerBlocks, options)
70 if err != nil {
71 return nil, warnings, err
72 }
73
74 - for _, sb := range serverBlocks {
74 + for _, sb := range originalServerBlocks {
75 // replace shorthand placeholders (which are
76 // convenient when writing a Caddyfile) with
77 // their actual placeholder identifiers or
@@ -155,7 +155,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
155 }
156
157 // map
158 - sbmap, err := st.mapAddressToServerBlocks(serverBlocks, options)
158 + sbmap, err := st.mapAddressToServerBlocks(originalServerBlocks, options)
159 if err != nil {
160 return nil, warnings, err
161 }
@@ -193,21 +193,24 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
193 // extract any custom logs, and enforce configured levels
194 var customLogs []namedCustomLog
195 var hasDefaultLog bool
196 - for _, sb := range serverBlocks {
197 - for _, clVal := range sb.pile["custom_log"] {
198 - ncl := clVal.Value.(namedCustomLog)
199 - if ncl.name == "" {
200 - continue
201 - }
202 - if ncl.name == "default" {
203 - hasDefaultLog = true
204 - }
205 - if _, ok := options["debug"]; ok && ncl.log.Level == "" {
206 - ncl.log.Level = "DEBUG"
196 + for _, p := range pairings {
197 + for _, sb := range p.serverBlocks {
198 + for _, clVal := range sb.pile["custom_log"] {
199 + ncl := clVal.Value.(namedCustomLog)
200 + if ncl.name == "" {
201 + continue
202 + }
203 + if ncl.name == "default" {
204 + hasDefaultLog = true
205 + }
206 + if _, ok := options["debug"]; ok && ncl.log.Level == "" {
207 + ncl.log.Level = "DEBUG"
208 + }
209 + customLogs = append(customLogs, ncl)
210 }
208 - customLogs = append(customLogs, ncl)
211 }
212 }
213 +
214 if !hasDefaultLog {
215 // if the default log was not customized, ensure we
216 // configure it with any applicable options
@@ -250,6 +253,17 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
253 if ncl.name != "" {
254 cfg.Logging.Logs[ncl.name] = ncl.log
255 }
256 + // most users seem to prefer not writing access logs
257 + // to the default log when they are directed to a
258 + // file or have any other special customization
259 + if len(ncl.log.Include) > 0 {
260 + defaultLog, ok := cfg.Logging.Logs["default"]
261 + if !ok {
262 + defaultLog = new(caddy.CustomLog)
263 + cfg.Logging.Logs["default"] = defaultLog
264 + }
265 + defaultLog.Exclude = append(defaultLog.Exclude, ncl.log.Include...)
266 + }
267 }
268 }
269
@@ -451,23 +465,46 @@ func (st *ServerType) serversFromPairings(
465 }
466
467 // add log associations
468 + // see https://github.com/caddyserver/caddy/issues/3310
469 + sblockLogHosts := sblock.hostsFromKeys(true)
470 for _, cval := range sblock.pile["custom_log"] {
471 ncl := cval.Value.(namedCustomLog)
472 if srv.Logs == nil {
457 - srv.Logs = &caddyhttp.ServerLogConfig{
458 - LoggerNames: make(map[string]string),
459 - }
473 + srv.Logs = new(caddyhttp.ServerLogConfig)
474 }
475 if sblock.hasHostCatchAllKey() {
462 - srv.Logs.LoggerName = ncl.name
476 + // all requests for hosts not able to be listed should use
477 + // this log because it's a catch-all-hosts server block
478 + srv.Logs.DefaultLoggerName = ncl.name
479 } else {
464 - for _, h := range sblock.hostsFromKeys(true) {
465 - if ncl.name != "" {
480 + // map each host to the user's desired logger name
481 + for _, h := range sblockLogHosts {
482 + // if the custom logger name is non-empty, add it to
483 + // the map; otherwise, only map to an empty logger
484 + // name if the server block has a catch-all host (in
485 + // which case only requests with mapped hostnames will
486 + // be access-logged, so it'll be necessary to add them
487 + // to the map even if they use default logger)
488 + if ncl.name != "" || len(hosts) == 0 {
489 + if srv.Logs.LoggerNames == nil {
490 + srv.Logs.LoggerNames = make(map[string]string)
491 + }
492 srv.Logs.LoggerNames[h] = ncl.name
493 }
494 }
495 }
496 }
497 + if srv.Logs != nil && len(sblock.pile["custom_log"]) == 0 {
498 + // server has access logs enabled, but this server block does not
499 + // enable access logs; therefore, all hosts of this server block
500 + // should not be access-logged
501 + if len(hosts) == 0 {
502 + // if the server block has a catch-all-hosts key, then we should
503 + // not log reqs to any host unless it appears in the map
504 + srv.Logs.SkipUnmappedHosts = true
505 + }
506 + srv.Logs.SkipHosts = append(srv.Logs.SkipHosts, sblockLogHosts...)
507 + }
508 }
509
510 // a server cannot (natively) serve both HTTP and HTTPS at the
modules/caddyhttp/server.go
+44 -9
@@ -101,9 +101,9 @@ type Server struct {
101 // client authentication.
102 StrictSNIHost *bool `json:"strict_sni_host,omitempty"`
103
104 - // Customizes how access logs are handled in this server. To
105 - // minimally enable access logs, simply set this to a non-null,
106 - // empty struct.
104 + // Enables access logging and configures how access logs are handled
105 + // in this server. To minimally enable access logs, simply set this
106 + // to a non-null, empty struct.
107 Logs *ServerLogConfig `json:"logs,omitempty"`
108
109 // Enable experimental HTTP/3 support. Note that HTTP/3 is not a
@@ -157,7 +157,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
157 loggableReq,
158 )
159
160 - if s.accessLogger != nil {
160 + if s.shouldLogRequest(r) {
161 wrec := NewResponseRecorder(w, nil, nil)
162 w = wrec
163
@@ -390,17 +390,52 @@ func (*HTTPErrorConfig) WithError(r *http.Request, err error) *http.Request {
390 return r
391 }
392
393 -// ServerLogConfig describes a server's logging configuration.
393 +// shouldLogRequest returns true if this request should be logged.
394 +func (s *Server) shouldLogRequest(r *http.Request) bool {
395 + if s.accessLogger == nil || s.Logs == nil {
396 + // logging is disabled
397 + return false
398 + }
399 + for _, dh := range s.Logs.SkipHosts {
400 + // logging for this particular host is disabled
401 + if r.Host == dh {
402 + return false
403 + }
404 + }
405 + if _, ok := s.Logs.LoggerNames[r.Host]; ok {
406 + // this host is mapped to a particular logger name
407 + return true
408 + }
409 + if s.Logs.SkipUnmappedHosts {
410 + // this host is not mapped and thus must not be logged
411 + return false
412 + }
413 + return true
414 +}
415 +
416 +// ServerLogConfig describes a server's logging configuration. If
417 +// enabled without customization, all requests to this server are
418 +// logged to the default logger; logger destinations may be
419 +// customized per-request-host.
420 type ServerLogConfig struct {
395 - // The logger name for all logs emitted by this server unless
396 - // the hostname is found in the LoggerNames (logger_names) map.
397 - LoggerName string `json:"log_name,omitempty"`
421 + // The default logger name for all logs emitted by this server for
422 + // hostnames that are not in the LoggerNames (logger_names) map.
423 + DefaultLoggerName string `json:"default_logger_name,omitempty"`
424
425 // LoggerNames maps request hostnames to a custom logger name.
426 // For example, a mapping of "example.com" to "example" would
427 // cause access logs from requests with a Host of example.com
428 // to be emitted by a logger named "http.log.access.example".
429 LoggerNames map[string]string `json:"logger_names,omitempty"`
430 +
431 + // By default, all requests to this server will be logged if
432 + // access logging is enabled. This field lists the request
433 + // hosts for which access logging should be disabled.
434 + SkipHosts []string `json:"skip_hosts,omitempty"`
435 +
436 + // If true, requests to any host not appearing in the
437 + // LoggerNames (logger_names) map will not be logged.
438 + SkipUnmappedHosts bool `json:"skip_unmapped_hosts,omitempty"`
439 }
440
441 // wrapLogger wraps logger in a logger named according to user preferences for the given host.
@@ -415,7 +450,7 @@ func (slc ServerLogConfig) getLoggerName(host string) string {
450 if loggerName, ok := slc.LoggerNames[host]; ok {
451 return loggerName
452 }
418 - return slc.LoggerName
453 + return slc.DefaultLoggerName
454 }
455
456 // errLogValues inspects err and returns the status code