package log import ( "encoding/hex" "math/rand" "net/http" "time" "github.com/inconshreveable/log15" ) const ( gaeRequestIDHeader = "X-AppEngine-Request-Log-Id" ) var random = rand.New(rand.NewSource(time.Now().UnixNano())) type httpContextHandler struct { log log15.Logger next http.Handler onAppEngine bool } // NewHTTPContextHandler adds a context logger based on the given logger to // each request. After a request passes through this handler, // Error(req.Context(), "foo") will log to that logger and add useful context // to each log entry. func NewHTTPContextHandler(h http.Handler, l log15.Logger, onAppEngine bool) http.Handler { if l == nil { l = log15.Root() } return &httpContextHandler{ log: l, next: h, onAppEngine: onAppEngine, } } func (h *httpContextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // We will accept an App Engine Request Header. If there isn't one, we will // fallback to 16 random bytes (hex encoded). reqID := r.Header.Get(gaeRequestIDHeader) if !h.onAppEngine || reqID == "" { buf := make([]byte, 16) random.Read(buf) reqID = hex.EncodeToString(buf) } requestLogger := h.log.New(log15.Ctx{ "request_id": reqID, }) r = r.WithContext(NewContext(ctx, requestLogger)) h.next.ServeHTTP(w, r) }