Skip to content

Instantly share code, notes, and snippets.

@refs
Forked from blixt/logger_middleware.go
Created November 4, 2020 12:03
Show Gist options
  • Save refs/2b5ebdaa04527addcae1a58939b33eba to your computer and use it in GitHub Desktop.
Save refs/2b5ebdaa04527addcae1a58939b33eba to your computer and use it in GitHub Desktop.
Logger middleware for Go HTTP servers which logs every request with response status code in the Apache format.
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
// Example log output:
// 127.0.0.1 - - [28/Oct/2016:18:35:05 -0400] "GET / HTTP/1.1" 200 13 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36"
// 127.0.0.1 - - [28/Oct/2016:18:35:05 -0400] "GET /favicon.ico HTTP/1.1" 404 10 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
fmt.Fprintln(w, "Hello World!")
})
log.Println("Serving...")
// Logger takes an io.Writer and an http.Handler function to wrap:
http.ListenAndServe(":8080", Logger(os.Stderr, http.DefaultServeMux))
}
// Logs incoming requests, including response status.
func Logger(out io.Writer, h http.Handler) http.Handler {
logger := log.New(out, "", 0)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
o := &responseObserver{ResponseWriter: w}
h.ServeHTTP(o, r)
addr := r.RemoteAddr
if i := strings.LastIndex(addr, ":"); i != -1 {
addr = addr[:i]
}
logger.Printf("%s - - [%s] %q %d %d %q %q",
addr,
time.Now().Format("02/Jan/2006:15:04:05 -0700"),
fmt.Sprintf("%s %s %s", r.Method, r.URL, r.Proto),
o.status,
o.written,
r.Referer(),
r.UserAgent())
})
}
type responseObserver struct {
http.ResponseWriter
status int
written int64
wroteHeader bool
}
func (o *responseObserver) Write(p []byte) (n int, err error) {
if !o.wroteHeader {
o.WriteHeader(http.StatusOK)
}
n, err = o.ResponseWriter.Write(p)
o.written += int64(n)
return
}
func (o *responseObserver) WriteHeader(code int) {
o.ResponseWriter.WriteHeader(code)
if o.wroteHeader {
return
}
o.wroteHeader = true
o.status = code
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment