request_logger.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package logger
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. "github.com/go-chi/chi/v5/middleware"
  7. "github.com/rs/zerolog"
  8. "github.com/drakkan/sftpgo/metrics"
  9. )
  10. // StructuredLogger defines a simple wrapper around zerolog logger.
  11. // It implements chi.middleware.LogFormatter interface
  12. type StructuredLogger struct {
  13. Logger *zerolog.Logger
  14. }
  15. // StructuredLoggerEntry defines a log entry.
  16. // It implements chi.middleware.LogEntry interface
  17. type StructuredLoggerEntry struct {
  18. // The zerolog logger
  19. Logger *zerolog.Logger
  20. // fields to write in the log
  21. fields map[string]interface{}
  22. }
  23. // NewStructuredLogger returns a chi.middleware.RequestLogger using our StructuredLogger.
  24. // This structured logger is called by the chi.middleware.Logger handler to log each HTTP request
  25. func NewStructuredLogger(logger *zerolog.Logger) func(next http.Handler) http.Handler {
  26. return middleware.RequestLogger(&StructuredLogger{logger})
  27. }
  28. // NewLogEntry creates a new log entry for an HTTP request
  29. func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
  30. scheme := "http"
  31. if r.TLS != nil {
  32. scheme = "https"
  33. }
  34. fields := map[string]interface{}{
  35. "remote_addr": r.RemoteAddr,
  36. "proto": r.Proto,
  37. "method": r.Method,
  38. "user_agent": r.UserAgent(),
  39. "uri": fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)}
  40. reqID := middleware.GetReqID(r.Context())
  41. if reqID != "" {
  42. fields["request_id"] = reqID
  43. }
  44. return &StructuredLoggerEntry{Logger: l.Logger, fields: fields}
  45. }
  46. // Write logs a new entry at the end of the HTTP request
  47. func (l *StructuredLoggerEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
  48. metrics.HTTPRequestServed(status)
  49. l.Logger.Info().
  50. Timestamp().
  51. Str("sender", "httpd").
  52. Fields(l.fields).
  53. Int("resp_status", status).
  54. Int("resp_size", bytes).
  55. Int64("elapsed_ms", elapsed.Nanoseconds()/1000000).
  56. Send()
  57. }
  58. // Panic logs panics
  59. func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
  60. l.Logger.Error().
  61. Timestamp().
  62. Str("sender", "httpd").
  63. Fields(l.fields).
  64. Str("stack", string(stack)).
  65. Str("panic", fmt.Sprintf("%+v", v)).
  66. Send()
  67. }