Repository: caddyserver/caddy
File: modules/caddyhttp/logging.go (Go)

Code before:
	DefaultLoggerName string `json:"default_logger_name,omitempty"`

	// LoggerNames maps request hostnames to a custom logger name.
	// For example, a mapping of "example.com" to "example" would
	// cause access logs from requests with a Host of example.com
	// to be emitted by a logger named "http.log.access.example".
	LoggerNames map[string]string `json:"logger_names,omitempty"`

	// By default, all requests to this server will be logged if
	// access logging is enabled. This field lists the request
	// hosts for which access logging should be disabled.
	SkipHosts []string `json:"skip_hosts,omitempty"`

	// If true, requests to any host not appearing in the
	// LoggerNames (logger_names) map will not be logged.
	SkipUnmappedHosts bool `json:"skip_unmapped_hosts,omitempty"`

	// If true, credentials that are otherwise omitted, will be logged.
	// The definition of credentials is defined by https://fetch.spec.whatwg.org/#credentials,
	// and this includes some request and response headers, i.e `Cookie`,
	// `Set-Cookie`, `Authorization`, and `Proxy-Authorization`.
	ShouldLogCredentials bool `json:"should_log_credentials,omitempty"`
}

// wrapLogger wraps logger in a logger named according to user preferences for the given host.
func (slc ServerLogConfig) wrapLogger(logger *zap.Logger, req *http.Request) *zap.Logger {
	if access_logger_name := GetVar(req.Context(), "access_logger_name"); access_logger_name != nil {
		return logger.Named(access_logger_name.(string))
	}
	if loggerName := slc.getLoggerName(req.Host); loggerName != "" {
		return logger.Named(loggerName)
	}
	return logger
}

func (slc ServerLogConfig) getLoggerName(host string) string {
	tryHost := func(key string) (string, bool) {
		// first try exact match
		if loggerName, ok := slc.LoggerNames[key]; ok {
			return loggerName, ok
		}
		// strip port and try again (i.e. Host header of "example.com:1234" should
		// match "example.com" if there is no "example.com:1234" in the map)
		hostOnly, _, err := net.SplitHostPort(key)
		if err != nil {
			return "", false
		}
		loggerName, ok := slc.LoggerNames[hostOnly]
		return loggerName, ok
	}


Review comment (suggestion):
Just a heads up, I'm changing this in this PR as well, so we'll have a merge conflict https://github.com/caddyserver/caddy/pull/6088

I'm not quite sure which way we'd resolve it though. Since currently, there's only a single logger name, but with my PR I'm making it possible to have multiple logger names for a single hostname. So I don't know how that would interact with `access_logger_name`.

Do we make it override the whole list and only do that one, or do we append to the existing list?

I think appending would have the opposite of the intended effect, because I think the point is to prevent logging to the other loggers when overriding.

We don't have any way for `access_logger_name` to have multiple values right now. I have an idea how we could make that work, but I'm not sure about it yet.

Code after:
	// hostnames that are not in the logger_names map.
	DefaultLoggerName string `json:"default_logger_name,omitempty"`

	// LoggerNames maps request hostnames to one or more custom logger
	// names. For example, a mapping of `"example.com": ["example"]` would
	// cause access logs from requests with a Host of example.com to be
	// emitted by a logger named "http.log.access.example". If there are
	// multiple logger names, then the log will be emitted to all of them.
	// If the logger name is an empty, the default logger is used, i.e.
	// the logger "http.log.access".
	//
	// Keys must be hostnames (without ports), and may contain wildcards
	// to match subdomains. The value is an array of logger names.
	//
	// For backwards compatibility, if the value is a string, it is treated
	// as a single-element array.
	LoggerNames map[string]StringArray `json:"logger_names,omitempty"`

	// By default, all requests to this server will be logged if
	// access logging is enabled. This field lists the request
	// hosts for which access logging should be disabled.
	SkipHosts []string `json:"skip_hosts,omitempty"`

	// If true, requests to any host not appearing in the
	// logger_names map will not be logged.
	SkipUnmappedHosts bool `json:"skip_unmapped_hosts,omitempty"`

	// If true, credentials that are otherwise omitted, will be logged.
	// The definition of credentials is defined by https://fetch.spec.whatwg.org/#credentials,
	// and this includes some request and response headers, i.e `Cookie`,
	// `Set-Cookie`, `Authorization`, and `Proxy-Authorization`.
	ShouldLogCredentials bool `json:"should_log_credentials,omitempty"`
}

// wrapLogger wraps logger in one or more logger named
// according to user preferences for the given host.
func (slc ServerLogConfig) wrapLogger(logger *zap.Logger, req *http.Request) []*zap.Logger {
	// using the `log_name` directive or the `access_logger_names` variable,
	// the logger names can be overridden for the current request
	if names := GetVar(req.Context(), AccessLoggerNameVarKey); names != nil {
		if namesSlice, ok := names.([]any); ok {
			loggers := make([]*zap.Logger, 0, len(namesSlice))
			for _, loggerName := range namesSlice {
				// no name, use the default logger
				if loggerName == "" {
					loggers = append(loggers, logger)
					continue
				}
				// make a logger with the given name
				loggers = append(loggers, logger.Named(loggerName.(string)))
			}