A powerful HTTP router and URL matcher for building Go web servers with 🦍
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
2.9 KiB

package mux
import (
"fmt"
"io"
"net/http"
"os"
"time"
)
const (
green = "\033[97;42m"
white = "\033[90;47m"
yellow = "\033[90;43m"
red = "\033[97;41m"
blue = "\033[97;44m"
magenta = "\033[97;45m"
cyan = "\033[97;46m"
reset = "\033[0m"
)
type LogConfig struct {
Output io.Writer
}
type LogFormatterParams struct {
Request *http.Request
// TimeStamp shows the time after the server returns a response.
TimeStamp time.Time
// StatusCode is HTTP response code.
StatusCode int
// Latency is how much time the server cost to process a certain request.
Latency time.Duration
// Method is the HTTP method given to the request.
Method string
// Path is a path the client requests.
Path string
}
type statusWriter struct {
http.ResponseWriter
status int
}
func (w *statusWriter) WriteHeader(s int) {
w.status = s
w.ResponseWriter.WriteHeader(s)
}
func (p *LogFormatterParams) StatusCodeColor() string {
code := p.StatusCode
switch {
case code >= http.StatusOK && code < http.StatusMultipleChoices:
return green
case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
return white
case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
return yellow
default:
return red
}
}
func (p *LogFormatterParams) MethodColor() string {
method := p.Method
switch method {
case http.MethodGet:
return blue
case http.MethodPost:
return cyan
case http.MethodPut:
return yellow
case http.MethodDelete:
return red
case http.MethodPatch:
return green
case http.MethodHead:
return magenta
case http.MethodOptions:
return white
default:
return reset
}
}
func (p *LogFormatterParams) ResetColor() string {
return reset
}
func Logger(next http.Handler) http.Handler {
return LoggerWithConfig(LogConfig{})(next)
}
func LoggerWithConfig(c LogConfig) MiddlewareFunc {
return func(next http.Handler) http.Handler {
out := c.Output
if out == nil {
out = os.Stdout
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
path := r.URL.Path
raw := r.URL.RawQuery
sw := statusWriter{
ResponseWriter: w,
status: 200,
}
next.ServeHTTP(&sw, r)
if raw != "" {
path = fmt.Sprintf("%s?%s", path, raw)
}
stop := time.Now()
p := LogFormatterParams{
Request: r,
TimeStamp: stop,
Latency: stop.Sub(start),
Method: r.Method,
StatusCode: sw.status,
Path: path,
}
fmt.Fprintf(out, formatter(p))
})
}
}
func formatter(p LogFormatterParams) string {
statusColor := p.StatusCodeColor()
methodColor := p.MethodColor()
resetColor := p.ResetColor()
if p.Latency > time.Minute {
p.Latency = p.Latency - p.Latency%time.Second
}
return fmt.Sprintf("[MUX] %v |%s %3d %s| %13v |%s %-7s %s %s\n",
p.TimeStamp.Format("2006/01/02 - 15:04:05"),
statusColor, p.StatusCode, resetColor,
p.Latency,
methodColor, p.Method, resetColor,
p.Path,
)
}