log_entry.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package requestlog
  2. import (
  3. "bufio"
  4. "errors"
  5. "io"
  6. "net"
  7. "net/http"
  8. "time"
  9. )
  10. type logEntry struct {
  11. ReceivedTime time.Time
  12. RequestMethod string
  13. RequestURL string
  14. RequestHeaderSize int64
  15. RequestBodySize int64
  16. UserAgent string
  17. Referer string
  18. Proto string
  19. Status int
  20. ResponseHeaderSize int64
  21. ResponseBodySize int64
  22. Latency time.Duration
  23. }
  24. func ipFromHostPort(hp string) string {
  25. h, _, err := net.SplitHostPort(hp)
  26. if err != nil {
  27. return ""
  28. }
  29. if len(h) > 0 && h[0] == '[' {
  30. return h[1 : len(h)-1]
  31. }
  32. return h
  33. }
  34. type readCounterCloser struct {
  35. r io.ReadCloser
  36. n int64
  37. err error
  38. }
  39. func (rcc *readCounterCloser) Read(p []byte) (n int, err error) {
  40. if rcc.err != nil {
  41. return 0, rcc.err
  42. }
  43. n, rcc.err = rcc.r.Read(p)
  44. rcc.n += int64(n)
  45. return n, rcc.err
  46. }
  47. func (rcc *readCounterCloser) Close() error {
  48. rcc.err = errors.New("read from closed reader")
  49. return rcc.r.Close()
  50. }
  51. type writeCounter int64
  52. func (wc *writeCounter) Write(p []byte) (n int, err error) {
  53. *wc += writeCounter(len(p))
  54. return len(p), nil
  55. }
  56. func headerSize(h http.Header) int64 {
  57. var wc writeCounter
  58. h.Write(&wc)
  59. return int64(wc) + 2 // for CRLF
  60. }
  61. type responseStats struct {
  62. w http.ResponseWriter
  63. hsize int64
  64. wc writeCounter
  65. code int
  66. }
  67. func (r *responseStats) Header() http.Header {
  68. return r.w.Header()
  69. }
  70. func (r *responseStats) WriteHeader(statusCode int) {
  71. if r.code != 0 {
  72. return
  73. }
  74. r.hsize = headerSize(r.w.Header())
  75. r.w.WriteHeader(statusCode)
  76. r.code = statusCode
  77. }
  78. func (r *responseStats) Write(p []byte) (n int, err error) {
  79. if r.code == 0 {
  80. r.WriteHeader(http.StatusOK)
  81. }
  82. n, err = r.w.Write(p)
  83. r.wc.Write(p[:n])
  84. return
  85. }
  86. func (r *responseStats) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  87. h, ok := r.w.(http.Hijacker)
  88. if !ok {
  89. return nil, nil, errors.New("ResponseWriter Interface does not support hijacking")
  90. }
  91. return h.Hijack()
  92. }
  93. func (r *responseStats) size() (hdr, body int64) {
  94. if r.code == 0 {
  95. return headerSize(r.w.Header()), 0
  96. }
  97. // Use the header size from the time WriteHeader was called.
  98. // The Header map can be mutated after the call to add HTTP Trailers,
  99. // which we don't want to count.
  100. return r.hsize, int64(r.wc)
  101. }