package log import ( "net/http" "strconv" "sync" "time" ) func New(logger Logger, enabled bool) ( middleware func(http.Handler) http.Handler) { return func(handler http.Handler) http.Handler { return &logMiddleware{ childHandler: handler, logger: logger, timeNow: time.Now, enabled: enabled, } } } type logMiddleware struct { childHandler http.Handler logger Logger timeNow func() time.Time enabled bool enabledMu sync.RWMutex } func (m *logMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { if !m.isEnabled() { m.childHandler.ServeHTTP(w, r) return } tStart := m.timeNow() statefulWriter := &statefulResponseWriter{httpWriter: w} m.childHandler.ServeHTTP(statefulWriter, r) duration := m.timeNow().Sub(tStart) m.logger.Info(strconv.Itoa(statefulWriter.statusCode) + " " + r.Method + " " + r.RequestURI + " wrote " + strconv.Itoa(statefulWriter.length) + "B to " + r.RemoteAddr + " in " + duration.String()) } func (m *logMiddleware) SetEnabled(enabled bool) { m.enabledMu.Lock() defer m.enabledMu.Unlock() m.enabled = enabled } func (m *logMiddleware) isEnabled() (enabled bool) { m.enabledMu.RLock() defer m.enabledMu.RUnlock() return m.enabled } type statefulResponseWriter struct { httpWriter http.ResponseWriter statusCode int length int } func (w *statefulResponseWriter) Write(b []byte) (n int, err error) { n, err = w.httpWriter.Write(b) if w.statusCode == 0 { w.statusCode = http.StatusOK } w.length += n return n, err } func (w *statefulResponseWriter) WriteHeader(statusCode int) { w.statusCode = statusCode w.httpWriter.WriteHeader(statusCode) } func (w *statefulResponseWriter) Header() http.Header { return w.httpWriter.Header() }