metrics.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package rpcmetrics
  15. import (
  16. "sync"
  17. "github.com/uber/jaeger-lib/metrics"
  18. )
  19. const (
  20. otherEndpointsPlaceholder = "other"
  21. endpointNameMetricTag = "endpoint"
  22. )
  23. // Metrics is a collection of metrics for an endpoint describing
  24. // throughput, success, errors, and performance.
  25. type Metrics struct {
  26. // RequestCountSuccess is a counter of the total number of successes.
  27. RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"`
  28. // RequestCountFailures is a counter of the number of times any failure has been observed.
  29. RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"`
  30. // RequestLatencySuccess is a latency histogram of succesful requests.
  31. RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"`
  32. // RequestLatencyFailures is a latency histogram of failed requests.
  33. RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"`
  34. // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299
  35. HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"`
  36. // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399
  37. HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"`
  38. // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499
  39. HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"`
  40. // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599
  41. HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"`
  42. }
  43. func (m *Metrics) recordHTTPStatusCode(statusCode uint16) {
  44. if statusCode >= 200 && statusCode < 300 {
  45. m.HTTPStatusCode2xx.Inc(1)
  46. } else if statusCode >= 300 && statusCode < 400 {
  47. m.HTTPStatusCode3xx.Inc(1)
  48. } else if statusCode >= 400 && statusCode < 500 {
  49. m.HTTPStatusCode4xx.Inc(1)
  50. } else if statusCode >= 500 && statusCode < 600 {
  51. m.HTTPStatusCode5xx.Inc(1)
  52. }
  53. }
  54. // MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name.
  55. // Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped
  56. // to a generic endpoint name "other".
  57. type MetricsByEndpoint struct {
  58. metricsFactory metrics.Factory
  59. endpoints *normalizedEndpoints
  60. metricsByEndpoint map[string]*Metrics
  61. mux sync.RWMutex
  62. }
  63. func newMetricsByEndpoint(
  64. metricsFactory metrics.Factory,
  65. normalizer NameNormalizer,
  66. maxNumberOfEndpoints int,
  67. ) *MetricsByEndpoint {
  68. return &MetricsByEndpoint{
  69. metricsFactory: metricsFactory,
  70. endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer),
  71. metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other"
  72. }
  73. }
  74. func (m *MetricsByEndpoint) get(endpoint string) *Metrics {
  75. safeName := m.endpoints.normalize(endpoint)
  76. if safeName == "" {
  77. safeName = otherEndpointsPlaceholder
  78. }
  79. m.mux.RLock()
  80. met := m.metricsByEndpoint[safeName]
  81. m.mux.RUnlock()
  82. if met != nil {
  83. return met
  84. }
  85. return m.getWithWriteLock(safeName)
  86. }
  87. // split to make easier to test
  88. func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics {
  89. m.mux.Lock()
  90. defer m.mux.Unlock()
  91. // it is possible that the name has been already registered after we released
  92. // the read lock and before we grabbed the write lock, so check for that.
  93. if met, ok := m.metricsByEndpoint[safeName]; ok {
  94. return met
  95. }
  96. // it would be nice to create the struct before locking, since Init() is somewhat
  97. // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics.
  98. met := &Metrics{}
  99. tags := map[string]string{endpointNameMetricTag: safeName}
  100. metrics.Init(met, m.metricsFactory, tags)
  101. m.metricsByEndpoint[safeName] = met
  102. return met
  103. }