log.go 1.80 KiB
package log
import (
"context"
"net"
"net/http"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/kjk/betterguid"
)
type contextKey string
// Request wraps the given HTTP handler with a log entry that logs how long it has been running.
// It makes a preconfigured logger for the request available as part of the request context.
func Request(next http.Handler, logger log.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
remote := r.Header.Get("X-Forwarded-For")
if remote == "" {
remote = r.RemoteAddr
}
// Try to figure out a name for the remote. If there are multiple, we select the first.
names, err := net.DefaultResolver.LookupAddr(r.Context(), remote)
if err != nil {
// Can't resolve name, but that's ok. There might not be a PTR record for the remote.
names = []string{"n/a"}
}
l := log.With(logger,
"method", r.Method,
"url", r.URL,
"remote", remote,
"host", names[0],
"request_id", betterguid.New())
start := time.Now()
defer func() {
d := time.Since(start)
_ = level.Info(l).
Log("duration", d, "msg", "request handled")
}()
key := contextKey("logger")
ctx := context.WithValue(r.Context(), key, l)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Get returns the logger for the given HTTP request. It will return a logger that discards all writes
// if r does not have a logger in its context.
func Get(r *http.Request) log.Logger {
return GetContext(r.Context())
}
// GetContext returns the logger for the given context. If ctx has no logger, it returns
// a logger that will discard all logged events.
func GetContext(ctx context.Context) log.Logger {
val := ctx.Value(contextKey("logger"))
if val == nil {
val = log.NewNopLogger()
}
return val.(log.Logger)
}