124 lines
4.3 KiB
Go
124 lines
4.3 KiB
Go
// 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
|
|
}
|