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