Skip to content

Instantly share code, notes, and snippets.

@blixt
Last active July 5, 2024 22:02
Show Gist options
  • Save blixt/01d6bdf8aa8ae57d5c72c1907b6db670 to your computer and use it in GitHub Desktop.
Save blixt/01d6bdf8aa8ae57d5c72c1907b6db670 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
}
@Jay54520
Copy link

out is not used.

@bentcoder
Copy link

Nice work HOWEVER for those who are planning to use such solution, I would suggest thinking twice or maybe more than that! Carry on reading.

@neel-bp
Copy link

neel-bp commented Sep 11, 2022

Nice work HOWEVER for those who are planning to use such solution, I would suggest thinking twice or maybe more than that! Carry on reading.

very informative post my friend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment