123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- // Copyright (c) 2017 Uber Technologies, Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package rpcmetrics
- import (
- "sync"
- "github.com/uber/jaeger-lib/metrics"
- )
- const (
- otherEndpointsPlaceholder = "other"
- endpointNameMetricTag = "endpoint"
- )
- // Metrics is a collection of metrics for an endpoint describing
- // throughput, success, errors, and performance.
- type Metrics struct {
- // RequestCountSuccess is a counter of the total number of successes.
- RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"`
- // RequestCountFailures is a counter of the number of times any failure has been observed.
- RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"`
- // RequestLatencySuccess is a latency histogram of succesful requests.
- RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"`
- // RequestLatencyFailures is a latency histogram of failed requests.
- RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"`
- // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299
- HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"`
- // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399
- HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"`
- // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499
- HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"`
- // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599
- HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"`
- }
- func (m *Metrics) recordHTTPStatusCode(statusCode uint16) {
- if statusCode >= 200 && statusCode < 300 {
- m.HTTPStatusCode2xx.Inc(1)
- } else if statusCode >= 300 && statusCode < 400 {
- m.HTTPStatusCode3xx.Inc(1)
- } else if statusCode >= 400 && statusCode < 500 {
- m.HTTPStatusCode4xx.Inc(1)
- } else if statusCode >= 500 && statusCode < 600 {
- m.HTTPStatusCode5xx.Inc(1)
- }
- }
- // MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name.
- // Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped
- // to a generic endpoint name "other".
- type MetricsByEndpoint struct {
- metricsFactory metrics.Factory
- endpoints *normalizedEndpoints
- metricsByEndpoint map[string]*Metrics
- mux sync.RWMutex
- }
- func newMetricsByEndpoint(
- metricsFactory metrics.Factory,
- normalizer NameNormalizer,
- maxNumberOfEndpoints int,
- ) *MetricsByEndpoint {
- return &MetricsByEndpoint{
- metricsFactory: metricsFactory,
- endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer),
- metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other"
- }
- }
- func (m *MetricsByEndpoint) get(endpoint string) *Metrics {
- safeName := m.endpoints.normalize(endpoint)
- if safeName == "" {
- safeName = otherEndpointsPlaceholder
- }
- m.mux.RLock()
- met := m.metricsByEndpoint[safeName]
- m.mux.RUnlock()
- if met != nil {
- return met
- }
- return m.getWithWriteLock(safeName)
- }
- // split to make easier to test
- func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics {
- m.mux.Lock()
- defer m.mux.Unlock()
- // it is possible that the name has been already registered after we released
- // the read lock and before we grabbed the write lock, so check for that.
- if met, ok := m.metricsByEndpoint[safeName]; ok {
- return met
- }
- // it would be nice to create the struct before locking, since Init() is somewhat
- // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics.
- met := &Metrics{}
- tags := map[string]string{endpointNameMetricTag: safeName}
- metrics.Init(met, m.metricsFactory, tags)
- m.metricsByEndpoint[safeName] = met
- return met
- }
|