package logger import ( "fmt" "net/http" "time" "github.com/go-chi/chi/middleware" "github.com/sirupsen/logrus" ) // NewStructuredLogger creates a new logger for chi router func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler { return middleware.RequestLogger(&StructuredLogger{logger}) } // StructuredLogger is a logger for chi router type StructuredLogger struct { Logger *logrus.Logger } // NewLogEntry creates a new log entry for the given HTTP request func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry { entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)} logFields := logrus.Fields{} if reqID := middleware.GetReqID(r.Context()); reqID != "" { logFields["req_id"] = reqID } scheme := "http" if r.TLS != nil { scheme = "https" } logFields["http_scheme"] = scheme logFields["http_proto"] = r.Proto logFields["http_method"] = r.Method logFields["remote_addr"] = r.RemoteAddr logFields["user_agent"] = r.UserAgent() logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI) entry.Logger = entry.Logger.WithFields(logFields) return entry } // StructuredLoggerEntry is a log entry will all relevant information about a specific http request type StructuredLoggerEntry struct { Logger logrus.FieldLogger } // Write logs information about http request response body func (l *StructuredLoggerEntry) Write(status, bytes int, elapsed time.Duration) { l.Logger = l.Logger.WithFields(logrus.Fields{ "resp_status": status, "resp_bytes_length": bytes, "resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0, }) l.Logger.Debugln("request complete") } // Panic logs if the request panics func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) { l.Logger = l.Logger.WithFields(logrus.Fields{ "stack": string(stack), "panic": fmt.Sprintf("%+v", v), }) } // GetLogEntry helper function for getting log entry for request func GetLogEntry(r *http.Request) logrus.FieldLogger { entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry) return entry.Logger } // LogEntrySetField sets a key's value func LogEntrySetField(r *http.Request, key string, value interface{}) { if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok { entry.Logger = entry.Logger.WithField(key, value) } } // LogEntrySetFields sets the log entry's fields func LogEntrySetFields(r *http.Request, fields map[string]interface{}) { if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok { entry.Logger = entry.Logger.WithFields(fields) } }