mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 10:12:29 +08:00 
			
		
		
		
	* caddyhttp: Implement `skip_log` handler * Refactor to use vars middleware Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
		
			
				
	
	
		
			145 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2015 Matthew Holt and The Caddy Authors
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package caddyhttp
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"net"
 | 
						|
	"net/http"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"go.uber.org/zap"
 | 
						|
	"go.uber.org/zap/zapcore"
 | 
						|
)
 | 
						|
 | 
						|
// ServerLogConfig describes a server's logging configuration. If
 | 
						|
// enabled without customization, all requests to this server are
 | 
						|
// logged to the default logger; logger destinations may be
 | 
						|
// customized per-request-host.
 | 
						|
type ServerLogConfig struct {
 | 
						|
	// The default logger name for all logs emitted by this server for
 | 
						|
	// hostnames that are not in the LoggerNames (logger_names) map.
 | 
						|
	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, host string) *zap.Logger {
 | 
						|
	if loggerName := slc.getLoggerName(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
 | 
						|
	}
 | 
						|
 | 
						|
	// try the exact hostname first
 | 
						|
	if loggerName, ok := tryHost(host); ok {
 | 
						|
		return loggerName
 | 
						|
	}
 | 
						|
 | 
						|
	// try matching wildcard domains if other non-specific loggers exist
 | 
						|
	labels := strings.Split(host, ".")
 | 
						|
	for i := range labels {
 | 
						|
		if labels[i] == "" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		labels[i] = "*"
 | 
						|
		wildcardHost := strings.Join(labels, ".")
 | 
						|
		if loggerName, ok := tryHost(wildcardHost); ok {
 | 
						|
			return loggerName
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return slc.DefaultLoggerName
 | 
						|
}
 | 
						|
 | 
						|
func (slc *ServerLogConfig) clone() *ServerLogConfig {
 | 
						|
	clone := &ServerLogConfig{
 | 
						|
		DefaultLoggerName:    slc.DefaultLoggerName,
 | 
						|
		LoggerNames:          make(map[string]string),
 | 
						|
		SkipHosts:            append([]string{}, slc.SkipHosts...),
 | 
						|
		SkipUnmappedHosts:    slc.SkipUnmappedHosts,
 | 
						|
		ShouldLogCredentials: slc.ShouldLogCredentials,
 | 
						|
	}
 | 
						|
	for k, v := range slc.LoggerNames {
 | 
						|
		clone.LoggerNames[k] = v
 | 
						|
	}
 | 
						|
	return clone
 | 
						|
}
 | 
						|
 | 
						|
// errLogValues inspects err and returns the status code
 | 
						|
// to use, the error log message, and any extra fields.
 | 
						|
// If err is a HandlerError, the returned values will
 | 
						|
// have richer information.
 | 
						|
func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
 | 
						|
	var handlerErr HandlerError
 | 
						|
	if errors.As(err, &handlerErr) {
 | 
						|
		status = handlerErr.StatusCode
 | 
						|
		if handlerErr.Err == nil {
 | 
						|
			msg = err.Error()
 | 
						|
		} else {
 | 
						|
			msg = handlerErr.Err.Error()
 | 
						|
		}
 | 
						|
		fields = []zapcore.Field{
 | 
						|
			zap.Int("status", handlerErr.StatusCode),
 | 
						|
			zap.String("err_id", handlerErr.ID),
 | 
						|
			zap.String("err_trace", handlerErr.Trace),
 | 
						|
		}
 | 
						|
		return
 | 
						|
	}
 | 
						|
	status = http.StatusInternalServerError
 | 
						|
	msg = err.Error()
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// Variable name used to indicate that this request
 | 
						|
// should be omitted from the access logs
 | 
						|
const SkipLogVar = "skip_log"
 |