mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
146 lines
3.6 KiB
Go
146 lines
3.6 KiB
Go
package middlewares
|
|
|
|
// Borrowed from https://gist.github.com/elithrar/887d162dfd0c539b700ab4049c76e22b
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type LoggingMiddleware struct {
|
|
handler http.Handler
|
|
output *log.Logger
|
|
excludePrefixes []string
|
|
}
|
|
|
|
func NewLoggingMiddleware(output *log.Logger, excludePrefixes []string) func(http.Handler) http.Handler {
|
|
return func(h http.Handler) http.Handler {
|
|
return &LoggingMiddleware{
|
|
handler: h,
|
|
output: output,
|
|
excludePrefixes: excludePrefixes,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (lg *LoggingMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
ww := wrapWriter(w)
|
|
|
|
start := time.Now()
|
|
lg.handler.ServeHTTP(ww, r)
|
|
end := time.Now()
|
|
duration := end.Sub(start)
|
|
|
|
path := strings.ToLower(r.URL.Path)
|
|
for _, prefix := range lg.excludePrefixes {
|
|
if strings.HasPrefix(path, prefix) {
|
|
return
|
|
}
|
|
}
|
|
|
|
lg.output.Printf(
|
|
"%v status=%d, method=%s, uri=%s, duration=%v, bytes=%d, addr=%s\n",
|
|
time.Now().Format(time.RFC3339Nano),
|
|
ww.Status(),
|
|
r.Method,
|
|
r.URL.String(),
|
|
duration,
|
|
ww.BytesWritten(),
|
|
readUserIP(r),
|
|
)
|
|
}
|
|
|
|
func readUserIP(r *http.Request) string {
|
|
ip := r.Header.Get("X-Real-Ip")
|
|
if ip == "" {
|
|
ip = r.Header.Get("X-Forwarded-For")
|
|
}
|
|
if ip == "" {
|
|
ip = r.RemoteAddr
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// The below writer-wrapping code has been lifted from
|
|
// https://github.com/zenazn/goji/blob/master/web/middleware/logger.go - because
|
|
// it does exactly what is needed, and it's unlikely to change in any
|
|
// significant way that makes copying worse-off than importing. MIT licensed
|
|
// and (c) Carl Jackson.
|
|
|
|
// writerProxy is a proxy around an http.ResponseWriter that allows you to hook
|
|
// into various parts of the response process.
|
|
type writerProxy interface {
|
|
http.ResponseWriter
|
|
// Status returns the HTTP status of the request, or 0 if one has not
|
|
// yet been sent.
|
|
Status() int
|
|
// BytesWritten returns the total number of bytes sent to the client.
|
|
BytesWritten() int
|
|
// Tee causes the response body to be written to the given io.Writer in
|
|
// addition to proxying the writes through. Only one io.Writer can be
|
|
// tee'd to at once: setting a second one will overwrite the first.
|
|
// Writes will be sent to the proxy before being written to this
|
|
// io.Writer. It is illegal for the tee'd writer to be modified
|
|
// concurrently with writes.
|
|
Tee(io.Writer)
|
|
// Unwrap returns the original proxied target.
|
|
Unwrap() http.ResponseWriter
|
|
}
|
|
|
|
// wrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to
|
|
// hook into various parts of the response process.
|
|
func wrapWriter(w http.ResponseWriter) writerProxy {
|
|
return &basicWriter{ResponseWriter: w}
|
|
}
|
|
|
|
// basicWriter wraps a http.ResponseWriter that implements the minimal
|
|
// http.ResponseWriter interface.
|
|
type basicWriter struct {
|
|
http.ResponseWriter
|
|
wroteHeader bool
|
|
code int
|
|
bytes int
|
|
tee io.Writer
|
|
}
|
|
|
|
func (b *basicWriter) WriteHeader(code int) {
|
|
if !b.wroteHeader {
|
|
b.code = code
|
|
b.wroteHeader = true
|
|
b.ResponseWriter.WriteHeader(code)
|
|
}
|
|
}
|
|
func (b *basicWriter) Write(buf []byte) (int, error) {
|
|
b.WriteHeader(http.StatusOK)
|
|
n, err := b.ResponseWriter.Write(buf)
|
|
if b.tee != nil {
|
|
_, err2 := b.tee.Write(buf[:n])
|
|
// Prefer errors generated by the proxied writer.
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
}
|
|
b.bytes += n
|
|
return n, err
|
|
}
|
|
func (b *basicWriter) maybeWriteHeader() {
|
|
if !b.wroteHeader {
|
|
b.WriteHeader(http.StatusOK)
|
|
}
|
|
}
|
|
func (b *basicWriter) Status() int {
|
|
return b.code
|
|
}
|
|
func (b *basicWriter) BytesWritten() int {
|
|
return b.bytes
|
|
}
|
|
func (b *basicWriter) Tee(w io.Writer) {
|
|
b.tee = w
|
|
}
|
|
func (b *basicWriter) Unwrap() http.ResponseWriter {
|
|
return b.ResponseWriter
|
|
}
|