metrics.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package rpcmetrics
  21. import (
  22. "sync"
  23. "github.com/uber/jaeger-lib/metrics"
  24. )
  25. const (
  26. otherEndpointsPlaceholder = "other"
  27. endpointNameMetricTag = "endpoint"
  28. )
  29. // Metrics is a collection of metrics for an endpoint describing
  30. // throughput, success, errors, and performance.
  31. type Metrics struct {
  32. // RequestCountSuccess is a counter of the total number of successes.
  33. RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"`
  34. // RequestCountFailures is a counter of the number of times any failure has been observed.
  35. RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"`
  36. // RequestLatencySuccess is a latency histogram of succesful requests.
  37. RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"`
  38. // RequestLatencyFailures is a latency histogram of failed requests.
  39. RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"`
  40. // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299
  41. HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"`
  42. // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399
  43. HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"`
  44. // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499
  45. HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"`
  46. // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599
  47. HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"`
  48. }
  49. func (m *Metrics) recordHTTPStatusCode(statusCode uint16) {
  50. if statusCode >= 200 && statusCode < 300 {
  51. m.HTTPStatusCode2xx.Inc(1)
  52. } else if statusCode >= 300 && statusCode < 400 {
  53. m.HTTPStatusCode3xx.Inc(1)
  54. } else if statusCode >= 400 && statusCode < 500 {
  55. m.HTTPStatusCode4xx.Inc(1)
  56. } else if statusCode >= 500 && statusCode < 600 {
  57. m.HTTPStatusCode5xx.Inc(1)
  58. }
  59. }
  60. // MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name.
  61. // Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped
  62. // to a generic endpoint name "other".
  63. type MetricsByEndpoint struct {
  64. metricsFactory metrics.Factory
  65. endpoints *normalizedEndpoints
  66. metricsByEndpoint map[string]*Metrics
  67. mux sync.RWMutex
  68. }
  69. func newMetricsByEndpoint(
  70. metricsFactory metrics.Factory,
  71. normalizer NameNormalizer,
  72. maxNumberOfEndpoints int,
  73. ) *MetricsByEndpoint {
  74. return &MetricsByEndpoint{
  75. metricsFactory: metricsFactory,
  76. endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer),
  77. metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other"
  78. }
  79. }
  80. func (m *MetricsByEndpoint) get(endpoint string) *Metrics {
  81. safeName := m.endpoints.normalize(endpoint)
  82. if safeName == "" {
  83. safeName = otherEndpointsPlaceholder
  84. }
  85. m.mux.RLock()
  86. met := m.metricsByEndpoint[safeName]
  87. m.mux.RUnlock()
  88. if met != nil {
  89. return met
  90. }
  91. return m.getWithWriteLock(safeName)
  92. }
  93. // split to make easier to test
  94. func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics {
  95. m.mux.Lock()
  96. defer m.mux.Unlock()
  97. // it is possible that the name has been already registered after we released
  98. // the read lock and before we grabbed the write lock, so check for that.
  99. if met, ok := m.metricsByEndpoint[safeName]; ok {
  100. return met
  101. }
  102. // it would be nice to create the struct before locking, since Init() is somewhat
  103. // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics.
  104. met := &Metrics{}
  105. tags := map[string]string{endpointNameMetricTag: safeName}
  106. metrics.Init(met, m.metricsFactory, tags)
  107. m.metricsByEndpoint[safeName] = met
  108. return met
  109. }