Skip to content

Instantly share code, notes, and snippets.

@blixt
Last active May 11, 2024 05:40
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

I think we can delete wroteHeader, what does it do?

@Jay54520
Copy link

Jay54520 commented May 14, 2019

I think we can delete wroteHeader, what does it do?

I have find its usage, replace 0 with 200.

From ResponseWriter interface:

// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.

@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