inmem_endpoint.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package metrics
  2. import (
  3. "fmt"
  4. "net/http"
  5. "sort"
  6. "time"
  7. )
  8. // MetricsSummary holds a roll-up of metrics info for a given interval
  9. type MetricsSummary struct {
  10. Timestamp string
  11. Gauges []GaugeValue
  12. Points []PointValue
  13. Counters []SampledValue
  14. Samples []SampledValue
  15. }
  16. type GaugeValue struct {
  17. Name string
  18. Hash string `json:"-"`
  19. Value float32
  20. Labels []Label `json:"-"`
  21. DisplayLabels map[string]string `json:"Labels"`
  22. }
  23. type PointValue struct {
  24. Name string
  25. Points []float32
  26. }
  27. type SampledValue struct {
  28. Name string
  29. Hash string `json:"-"`
  30. *AggregateSample
  31. Mean float64
  32. Stddev float64
  33. Labels []Label `json:"-"`
  34. DisplayLabels map[string]string `json:"Labels"`
  35. }
  36. // deepCopy allocates a new instance of AggregateSample
  37. func (source *SampledValue) deepCopy() SampledValue {
  38. dest := *source
  39. if source.AggregateSample != nil {
  40. dest.AggregateSample = &AggregateSample{}
  41. *dest.AggregateSample = *source.AggregateSample
  42. }
  43. return dest
  44. }
  45. // DisplayMetrics returns a summary of the metrics from the most recent finished interval.
  46. func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
  47. data := i.Data()
  48. var interval *IntervalMetrics
  49. n := len(data)
  50. switch {
  51. case n == 0:
  52. return nil, fmt.Errorf("no metric intervals have been initialized yet")
  53. case n == 1:
  54. // Show the current interval if it's all we have
  55. interval = data[0]
  56. default:
  57. // Show the most recent finished interval if we have one
  58. interval = data[n-2]
  59. }
  60. interval.RLock()
  61. defer interval.RUnlock()
  62. summary := MetricsSummary{
  63. Timestamp: interval.Interval.Round(time.Second).UTC().String(),
  64. Gauges: make([]GaugeValue, 0, len(interval.Gauges)),
  65. Points: make([]PointValue, 0, len(interval.Points)),
  66. }
  67. // Format and sort the output of each metric type, so it gets displayed in a
  68. // deterministic order.
  69. for name, points := range interval.Points {
  70. summary.Points = append(summary.Points, PointValue{name, points})
  71. }
  72. sort.Slice(summary.Points, func(i, j int) bool {
  73. return summary.Points[i].Name < summary.Points[j].Name
  74. })
  75. for hash, value := range interval.Gauges {
  76. value.Hash = hash
  77. value.DisplayLabels = make(map[string]string)
  78. for _, label := range value.Labels {
  79. value.DisplayLabels[label.Name] = label.Value
  80. }
  81. value.Labels = nil
  82. summary.Gauges = append(summary.Gauges, value)
  83. }
  84. sort.Slice(summary.Gauges, func(i, j int) bool {
  85. return summary.Gauges[i].Hash < summary.Gauges[j].Hash
  86. })
  87. summary.Counters = formatSamples(interval.Counters)
  88. summary.Samples = formatSamples(interval.Samples)
  89. return summary, nil
  90. }
  91. func formatSamples(source map[string]SampledValue) []SampledValue {
  92. output := make([]SampledValue, 0, len(source))
  93. for hash, sample := range source {
  94. displayLabels := make(map[string]string)
  95. for _, label := range sample.Labels {
  96. displayLabels[label.Name] = label.Value
  97. }
  98. output = append(output, SampledValue{
  99. Name: sample.Name,
  100. Hash: hash,
  101. AggregateSample: sample.AggregateSample,
  102. Mean: sample.AggregateSample.Mean(),
  103. Stddev: sample.AggregateSample.Stddev(),
  104. DisplayLabels: displayLabels,
  105. })
  106. }
  107. sort.Slice(output, func(i, j int) bool {
  108. return output[i].Hash < output[j].Hash
  109. })
  110. return output
  111. }