mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	OnStartup and OnShutdown callbacks now run as part of restarts, too. The startup and shutdown directives only run their commands NOT as part of restarts, as before. Some middleware that use OnStartup may need to switch to OnFirstStartup and implement OnFinalShutdown to do any cleanup as needed.
		
			
				
	
	
		
			86 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			86 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package log implements request (access) logging middleware.
 | |
| package log
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 
 | |
| 	"github.com/mholt/caddy"
 | |
| 	"github.com/mholt/caddy/caddyhttp/httpserver"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	caddy.RegisterPlugin("log", caddy.Plugin{
 | |
| 		ServerType: "http",
 | |
| 		Action:     setup,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // Logger is a basic request logging middleware.
 | |
| type Logger struct {
 | |
| 	Next      httpserver.Handler
 | |
| 	Rules     []Rule
 | |
| 	ErrorFunc func(http.ResponseWriter, *http.Request, int) // failover error handler
 | |
| }
 | |
| 
 | |
| func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
 | |
| 	for _, rule := range l.Rules {
 | |
| 		if httpserver.Path(r.URL.Path).Matches(rule.PathScope) {
 | |
| 			// Record the response
 | |
| 			responseRecorder := httpserver.NewResponseRecorder(w)
 | |
| 
 | |
| 			// Attach the Replacer we'll use so that other middlewares can
 | |
| 			// set their own placeholders if they want to.
 | |
| 			rep := httpserver.NewReplacer(r, responseRecorder, CommonLogEmptyValue)
 | |
| 			responseRecorder.Replacer = rep
 | |
| 
 | |
| 			// Bon voyage, request!
 | |
| 			status, err := l.Next.ServeHTTP(responseRecorder, r)
 | |
| 
 | |
| 			if status >= 400 {
 | |
| 				// There was an error up the chain, but no response has been written yet.
 | |
| 				// The error must be handled here so the log entry will record the response size.
 | |
| 				if l.ErrorFunc != nil {
 | |
| 					l.ErrorFunc(responseRecorder, r, status)
 | |
| 				} else {
 | |
| 					// Default failover error handler
 | |
| 					responseRecorder.WriteHeader(status)
 | |
| 					fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status))
 | |
| 				}
 | |
| 				status = 0
 | |
| 			}
 | |
| 
 | |
| 			// Write log entry
 | |
| 			rule.Log.Println(rep.Replace(rule.Format))
 | |
| 
 | |
| 			return status, err
 | |
| 		}
 | |
| 	}
 | |
| 	return l.Next.ServeHTTP(w, r)
 | |
| }
 | |
| 
 | |
| // Rule configures the logging middleware.
 | |
| type Rule struct {
 | |
| 	PathScope  string
 | |
| 	OutputFile string
 | |
| 	Format     string
 | |
| 	Log        *log.Logger
 | |
| 	Roller     *httpserver.LogRoller
 | |
| 	file       *os.File // if logging to a file that needs to be closed
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	// DefaultLogFilename is the default log filename.
 | |
| 	DefaultLogFilename = "access.log"
 | |
| 	// CommonLogFormat is the common log format.
 | |
| 	CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{method} {uri} {proto}" {status} {size}`
 | |
| 	// CommonLogEmptyValue is the common empty log value.
 | |
| 	CommonLogEmptyValue = "-"
 | |
| 	// CombinedLogFormat is the combined log format.
 | |
| 	CombinedLogFormat = CommonLogFormat + ` "{>Referer}" "{>User-Agent}"`
 | |
| 	// DefaultLogFormat is the default log format.
 | |
| 	DefaultLogFormat = CommonLogFormat
 | |
| )
 |