handler.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. package requestlog
  2. import (
  3. "io"
  4. "io/ioutil"
  5. "net/http"
  6. "time"
  7. lr "github.com/porter-dev/porter/internal/logger"
  8. )
  9. // Handler wraps an HTTP handler so that a log entry can be generated for
  10. // request/response stats
  11. type Handler struct {
  12. handler http.Handler
  13. logger *lr.Logger
  14. }
  15. // NewHandler creates a new Handler instance based on an http.HandlerFunc
  16. func NewHandler(h http.HandlerFunc, l *lr.Logger) *Handler {
  17. return &Handler{
  18. handler: h,
  19. logger: l,
  20. }
  21. }
  22. // ServeHTTP calls its underlying handler's ServeHTTP method, then calls
  23. // Log after the handler returns.
  24. //
  25. // ServeHTTP will always consume the request body up to the first error,
  26. // even if the underlying handler does not.
  27. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  28. start := time.Now()
  29. le := &logEntry{
  30. ReceivedTime: start,
  31. RequestMethod: r.Method,
  32. RequestURL: r.URL.String(),
  33. RequestHeaderSize: headerSize(r.Header),
  34. UserAgent: r.UserAgent(),
  35. Referer: r.Referer(),
  36. Proto: r.Proto,
  37. }
  38. r2 := new(http.Request)
  39. *r2 = *r
  40. rcc := &readCounterCloser{r: r.Body}
  41. r2.Body = rcc
  42. w2 := &responseStats{w: w}
  43. h.handler.ServeHTTP(w2, r2)
  44. le.Latency = time.Since(start)
  45. if rcc.err == nil && rcc.r != nil {
  46. // If the handler hasn't encountered an error in the Body (like EOF),
  47. // then consume the rest of the Body to provide an accurate rcc.n.
  48. io.Copy(ioutil.Discard, rcc)
  49. }
  50. le.RequestBodySize = rcc.n
  51. le.Status = w2.code
  52. if le.Status == 0 {
  53. le.Status = http.StatusOK
  54. }
  55. le.ResponseHeaderSize, le.ResponseBodySize = w2.size()
  56. h.logger.Info().
  57. Time("received_time", le.ReceivedTime).
  58. Str("method", le.RequestMethod).
  59. Str("url", le.RequestURL).
  60. Int64("header_size", le.RequestHeaderSize).
  61. Int64("body_size", le.RequestBodySize).
  62. Str("agent", le.UserAgent).
  63. Str("referer", le.Referer).
  64. Str("proto", le.Proto).
  65. Int("status", le.Status).
  66. Int64("resp_header_size", le.ResponseHeaderSize).
  67. Int64("resp_body_size", le.ResponseBodySize).
  68. Dur("latency", le.Latency).
  69. Msg("")
  70. }