Files
micromdm/pkg/log/http.go
Victor Vrantchan d3aadc271d log: add trace id and http request logs.
- Added the concept of a "trace id", a unique ID which gets associated
with every log/request by default. Will eventually use
opentelemetry/opentracing, or perhaps something better... Still very
alpha quality + lots of dependencies/API surface to bring in. For the
purpose of MicroMDM, a unique identifier per request is fine.

- Added HTTP logging middleware.
2020-07-05 15:04:00 -04:00

76 lines
1.7 KiB
Go

package log
import (
"context"
"net"
"net/http"
"github.com/felixge/httpsnoop"
)
// HTTP returns an HTTP logging middleware using the provided base logger.
func HTTP(l Logger) func(http.Handler) http.Handler { return handler{logger: l}.decorate }
type handler struct{ logger Logger }
func (h handler) decorate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := newTraceContext(r.Context())
ctx = NewContext(ctx, h.logger)
// https://github.com/felixge/httpsnoop#why-this-package-exists
// https://github.com/golang/go/issues/18997
var metrics httpsnoop.Metrics
defer func() {
logRequest(ctx, metrics.Code, r)
}()
metrics = httpsnoop.CaptureMetrics(next, w, r.WithContext(ctx))
})
}
func logRequest(ctx context.Context, code int, r *http.Request) {
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
host = r.RemoteAddr
}
url := *r.URL
uri := r.RequestURI
// Requests using the CONNECT method over HTTP/2.0 must use
// the authority field (aka r.Host) to identify the target.
// Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
if r.ProtoMajor == 2 && r.Method == "CONNECT" {
uri = r.Host
}
if uri == "" {
uri = url.RequestURI()
}
keyvals := []interface{}{
"method", r.Method,
"status", code,
"proto", r.Proto,
"host", host,
"user_agent", r.UserAgent(),
"path", uri,
}
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
keyvals = append(keyvals, "x_forwarded_for", fwd)
}
if referer := r.Referer(); referer != "" {
keyvals = append(keyvals, "referer", referer)
}
if code >= 500 {
Info(FromContext(ctx)).Log(keyvals...)
} else {
Debug(FromContext(ctx)).Log(keyvals...)
}
}