2
0

httpmetricmiddleware.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package metrics
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. "github.com/kubecost/events"
  7. )
  8. // ResponseMetricMiddleware dispatches metric events for handles request and responses.
  9. func ResponseMetricMiddleware(handler http.Handler) http.Handler {
  10. dispatcher := events.GlobalDispatcherFor[HttpHandlerMetricEvent]()
  11. return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  12. // use a ResponseWriter implementation to record telemetry for the response
  13. respWriter := &responseWriterAdapter{w: rw}
  14. // record method and path of the request
  15. method := r.Method
  16. path := r.URL.Path
  17. // time and execute the handler
  18. start := time.Now()
  19. handler.ServeHTTP(respWriter, r)
  20. duration := time.Since(start)
  21. // record the response code and size
  22. code := respWriter.StatusCode()
  23. size := respWriter.TotalResponseSize()
  24. dispatcher.Dispatch(HttpHandlerMetricEvent{
  25. Handler: path,
  26. Method: method,
  27. Code: code,
  28. ResponseTime: duration,
  29. ResponseSize: size,
  30. })
  31. })
  32. }
  33. // responseWriterAdapter implements http.ResponseWriter and extracts the statusCode.
  34. type responseWriterAdapter struct {
  35. w http.ResponseWriter
  36. written bool
  37. statusCode int
  38. size uint64
  39. }
  40. func (wd *responseWriterAdapter) Header() http.Header {
  41. return wd.w.Header()
  42. }
  43. func (wd *responseWriterAdapter) Write(bytes []byte) (int, error) {
  44. numBytes, err := wd.w.Write(bytes)
  45. wd.size += uint64(numBytes)
  46. return numBytes, err
  47. }
  48. func (wd *responseWriterAdapter) WriteHeader(statusCode int) {
  49. wd.written = true
  50. wd.statusCode = statusCode
  51. wd.w.WriteHeader(statusCode)
  52. }
  53. func (wd *responseWriterAdapter) StatusCode() int {
  54. if !wd.written {
  55. return http.StatusOK
  56. }
  57. return wd.statusCode
  58. }
  59. func (wd *responseWriterAdapter) Status() string {
  60. return fmt.Sprintf("%d", wd.StatusCode())
  61. }
  62. func (wd *responseWriterAdapter) TotalResponseSize() uint64 {
  63. return wd.size
  64. }