// Credits go to: https://gist.github.com/cespare/3985516 package main import ( "fmt" "io" "net/http" "strings" "time" ) const ApacheFormatPattern = "%s - - [%s] \"%s %d %d\" %.4fms \"%s\"\n" type ApacheLogRecord struct { http.ResponseWriter ip string time time.Time method, uri, protocol string status int responseBytes int64 elapsedTime time.Duration } func (r *ApacheLogRecord) Log(out io.Writer, origHdr http.Header) { timeFormatted := r.time.Format("02/Jan/2006 03:04:05") requestLine := fmt.Sprintf("%s %s %s", r.method, r.uri, r.protocol) fmt.Fprintf(out, ApacheFormatPattern, r.ip, timeFormatted, requestLine, r.status, r.responseBytes, r.elapsedTime.Seconds()*1000, origHdr.Get("X-Panic")) } func (r *ApacheLogRecord) Write(p []byte) (int, error) { written, err := r.ResponseWriter.Write(p) r.responseBytes += int64(written) return written, err } func (r *ApacheLogRecord) WriteHeader(status int) { r.status = status r.ResponseWriter.WriteHeader(status) } type ApacheLoggingHandler struct { handler http.Handler out io.Writer } func NewApacheLoggingHandler(handler http.Handler, out io.Writer) http.Handler { return &ApacheLoggingHandler{ handler: handler, out: out, } } func (h *ApacheLoggingHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { clientIP := r.RemoteAddr if colon := strings.LastIndex(clientIP, ":"); colon != -1 { clientIP = clientIP[:colon] } record := &ApacheLogRecord{ ResponseWriter: rw, ip: clientIP, time: time.Time{}, method: r.Method, uri: r.RequestURI, protocol: r.Proto, status: http.StatusOK, elapsedTime: time.Duration(0), } startTime := time.Now() h.handler.ServeHTTP(record, r) finishTime := time.Now() record.time = finishTime.UTC() record.elapsedTime = finishTime.Sub(startTime) if r.Header.Get("X-Silence-Logging") != "yes" { record.Log(h.out, r.Header) } }