Files
weave-scope/vendor/github.com/uber-go/tally/scope.go
Marc Carré 652cc90f98 Update weaveworks/common to latest version
```
$ gvt delete github.com/weaveworks/common
$ gvt fetch --revision 4d96fd8dcf2c7b417912c6219b310112cb4a4626 github.com/weaveworks/common
2018/07/23 15:31:11 Fetching: github.com/weaveworks/common
2018/07/23 15:31:14 · Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:31:14 · Fetching recursive dependency: github.com/pkg/errors
2018/07/23 15:31:16 · Skipping (existing): github.com/aws/aws-sdk-go/aws
2018/07/23 15:31:16 · Fetching recursive dependency: github.com/sirupsen/logrus
2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/sys/unix
2018/07/23 15:31:18 ·· Skipping (existing): golang.org/x/crypto/ssh/terminal
2018/07/23 15:31:18 · Skipping (existing): google.golang.org/grpc/status
2018/07/23 15:31:18 · Skipping (existing): github.com/gorilla/mux
2018/07/23 15:31:18 · Fetching recursive dependency: github.com/opentracing-contrib/go-stdlib/nethttp
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:20 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:20 · Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:31:20 · Skipping (existing): google.golang.org/grpc
2018/07/23 15:31:20 · Skipping (existing): github.com/pmezard/go-difflib/difflib
2018/07/23 15:31:20 · Fetching recursive dependency: github.com/go-kit/kit/log
2018/07/23 15:31:23 ·· Fetching recursive dependency: github.com/go-logfmt/logfmt
2018/07/23 15:31:25 ··· Fetching recursive dependency: github.com/kr/logfmt
2018/07/23 15:31:27 ·· Fetching recursive dependency: github.com/go-stack/stack
2018/07/23 15:31:29 · Fetching recursive dependency: google.golang.org/genproto/googleapis/rpc/status
2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:37 ·· Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:31:37 · Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:37 · Fetching recursive dependency: github.com/sercand/kuberesolver
2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:31:39 ·· Skipping (existing): google.golang.org/grpc/resolver
2018/07/23 15:31:39 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:39 · Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:39 · Skipping (existing): github.com/armon/go-socks5
2018/07/23 15:31:39 · Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:39 · Skipping (existing): github.com/davecgh/go-spew/spew
2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/ptypes
2018/07/23 15:31:39 · Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:39 · Fetching recursive dependency: github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:41 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:31:41 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:41 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:31:41 ·· Skipping (existing): google.golang.org/grpc/status
2018/07/23 15:31:41 · Fetching recursive dependency: github.com/uber/jaeger-client-go/config
2018/07/23 15:31:44 ·· Fetching recursive dependency: github.com/uber/jaeger-client-go/internal/throttler/remote
2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go/utils
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/agent
2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/jaeger
2018/07/23 15:31:44 ····· Fetching recursive dependency: github.com/uber/jaeger-client-go/thrift-gen/zipkincore
2018/07/23 15:31:44 ··· Fetching recursive dependency: github.com/uber/jaeger-client-go
2018/07/23 15:31:44 ···· Fetching recursive dependency: github.com/crossdock/crossdock-go
2018/07/23 15:31:46 ····· Skipping (existing): github.com/davecgh/go-spew/spew
2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context/ctxhttp
2018/07/23 15:31:46 ····· Skipping (existing): golang.org/x/net/context
2018/07/23 15:31:46 ····· Skipping (existing): github.com/pmezard/go-difflib/difflib
2018/07/23 15:31:46 ···· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:31:46 ···· Fetching recursive dependency: go.uber.org/zap/zapcore
2018/07/23 15:31:49 ····· Fetching recursive dependency: go.uber.org/atomic
2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/zap/internal/bufferpool
2018/07/23 15:31:51 ······ Fetching recursive dependency: go.uber.org/zap/buffer
2018/07/23 15:31:51 ····· Fetching recursive dependency: go.uber.org/multierr
2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/exit
2018/07/23 15:31:54 ····· Fetching recursive dependency: go.uber.org/zap/internal/color
2018/07/23 15:31:54 ···· Fetching recursive dependency: go.uber.org/zap
2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:31:54 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:31:54 ···· Fetching recursive dependency: github.com/uber/jaeger-lib/metrics
2018/07/23 15:31:56 ····· Fetching recursive dependency: github.com/uber-go/tally
2018/07/23 15:31:58 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus/promhttp
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/common/expfmt
2018/07/23 15:32:00 ······· Skipping (existing): github.com/prometheus/client_model/go
2018/07/23 15:32:00 ······ Fetching recursive dependency: gopkg.in/validator.v2
2018/07/23 15:32:06 ······ Fetching recursive dependency: github.com/cactus/go-statsd-client/statsd
2018/07/23 15:32:08 ······ Skipping (existing): gopkg.in/yaml.v2
2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/m3db/prometheus_client_golang/prometheus
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/procfs
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_model/go
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/expfmt
2018/07/23 15:32:08 ······· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:08 ······· Skipping (existing): github.com/beorn7/perks/quantile
2018/07/23 15:32:08 ······· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/common/model
2018/07/23 15:32:08 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:08 ······ Fetching recursive dependency: github.com/apache/thrift/lib/go/thrift
2018/07/23 15:32:13 ····· Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:13 ····· Fetching recursive dependency: github.com/go-kit/kit/metrics/influx
2018/07/23 15:32:13 ······ Fetching recursive dependency: github.com/influxdata/influxdb/client/v2
2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/influxdata/influxdb/models
2018/07/23 15:32:17 ········ Fetching recursive dependency: github.com/influxdata/influxdb/pkg/escape
2018/07/23 15:32:17 ······ Fetching recursive dependency: github.com/go-kit/kit/metrics
2018/07/23 15:32:17 ······· Fetching recursive dependency: github.com/performancecopilot/speed
2018/07/23 15:32:19 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/aws
2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/sdk
2018/07/23 15:32:28 ········ Skipping (existing): github.com/go-ini/ini
2018/07/23 15:32:28 ········ Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/sts
2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol/query
2018/07/23 15:32:28 ·········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/private/protocol
2018/07/23 15:32:28 ········· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/internal/awsutil
2018/07/23 15:32:28 ·········· Skipping (existing): github.com/jmespath/go-jmespath
2018/07/23 15:32:28 ······· Fetching recursive dependency: github.com/aws/aws-sdk-go-v2/service/cloudwatch
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/aws
2018/07/23 15:32:29 ······· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch
2018/07/23 15:32:29 ······· Skipping (existing): github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface
2018/07/23 15:32:29 ······· Fetching recursive dependency: golang.org/x/sync/errgroup
2018/07/23 15:32:31 ········ Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/go-kit/kit/util/conn
2018/07/23 15:32:31 ······· Fetching recursive dependency: github.com/VividCortex/gohistogram
2018/07/23 15:32:33 ····· Skipping (existing): github.com/prometheus/client_golang/prometheus
2018/07/23 15:32:33 ····· Fetching recursive dependency: github.com/codahale/hdrhistogram
2018/07/23 15:32:35 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:35 · Fetching recursive dependency: github.com/mwitkow/go-grpc-middleware
2018/07/23 15:32:37 ·· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware/logging
2018/07/23 15:32:39 ··· Fetching recursive dependency: github.com/grpc-ecosystem/go-grpc-middleware
2018/07/23 15:32:39 ···· Fetching recursive dependency: github.com/golang/protobuf/jsonpb
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/timestamp
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/duration
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/any
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/struct
2018/07/23 15:32:42 ····· Skipping (existing): github.com/golang/protobuf/ptypes/wrappers
2018/07/23 15:32:42 ···· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:32:42 ···· Fetching recursive dependency: github.com/stretchr/testify/suite
2018/07/23 15:32:45 ····· Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:45 ····· Fetching recursive dependency: github.com/stretchr/testify/require
2018/07/23 15:32:45 ······ Skipping (existing): github.com/stretchr/testify/assert
2018/07/23 15:32:45 ···· Skipping (existing): google.golang.org/grpc/peer
2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:45 ···· Skipping (existing): golang.org/x/net/trace
2018/07/23 15:32:45 ···· Fetching recursive dependency: github.com/gogo/protobuf/gogoproto
2018/07/23 15:32:48 ····· Fetching recursive dependency: github.com/gogo/protobuf/protoc-gen-gogo/descriptor
2018/07/23 15:32:48 ······ Skipping (existing): github.com/gogo/protobuf/proto
2018/07/23 15:32:48 ····· Skipping (existing): github.com/gogo/protobuf/proto
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/credentials
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ···· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ···· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:32:48 ···· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:32:48 ··· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ··· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ··· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc
2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/codes
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/grpclog
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/log
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/metadata
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/peer
2018/07/23 15:32:48 ·· Skipping (existing): google.golang.org/grpc/credentials
2018/07/23 15:32:48 ·· Skipping (existing): github.com/golang/protobuf/proto
2018/07/23 15:32:48 ·· Skipping (existing): golang.org/x/net/trace
2018/07/23 15:32:48 ·· Skipping (existing): github.com/opentracing/opentracing-go/ext
2018/07/23 15:32:48 · Fetching recursive dependency: github.com/weaveworks/promrus
2018/07/23 15:32:53 ·· Skipping (existing): gopkg.in/yaml.v2
2018/07/23 15:32:53 ·· Skipping (existing): golang.org/x/net/context/ctxhttp
2018/07/23 15:32:53 ·· Fetching recursive dependency: github.com/stretchr/objx
2018/07/23 15:32:55 ·· Fetching recursive dependency: gopkg.in/alecthomas/kingpin.v2
2018/07/23 15:32:58 ··· Fetching recursive dependency: github.com/alecthomas/units
2018/07/23 15:33:00 ··· Fetching recursive dependency: github.com/alecthomas/template
2018/07/23 15:33:02 ·· Fetching recursive dependency: github.com/julienschmidt/httprouter
2018/07/23 15:33:05 ·· Skipping (existing): golang.org/x/net/context
2018/07/23 15:33:05 · Skipping (existing): github.com/aws/aws-sdk-go/aws/credentials
2018/07/23 15:33:05 · Skipping (existing): github.com/golang/protobuf/ptypes/empty
2018/07/23 15:33:05 · Skipping (existing): golang.org/x/net/context
2018/07/23 15:33:05 · Skipping (existing): golang.org/x/tools/cover
2018/07/23 15:33:05 · Skipping (existing): github.com/mgutz/ansi
```
2018-07-23 20:10:13 +02:00

753 lines
17 KiB
Go

// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package tally
import (
"fmt"
"io"
"sync"
"time"
)
var (
// NoopScope is a scope that does nothing
NoopScope, _ = NewRootScope(ScopeOptions{Reporter: NullStatsReporter}, 0)
// DefaultSeparator is the default separator used to join nested scopes
DefaultSeparator = "."
globalNow = time.Now
defaultScopeBuckets = DurationBuckets{
0 * time.Millisecond,
10 * time.Millisecond,
25 * time.Millisecond,
50 * time.Millisecond,
75 * time.Millisecond,
100 * time.Millisecond,
200 * time.Millisecond,
300 * time.Millisecond,
400 * time.Millisecond,
500 * time.Millisecond,
600 * time.Millisecond,
800 * time.Millisecond,
1 * time.Second,
2 * time.Second,
5 * time.Second,
}
)
type scope struct {
separator string
prefix string
tags map[string]string
reporter StatsReporter
cachedReporter CachedStatsReporter
baseReporter BaseStatsReporter
defaultBuckets Buckets
sanitizer Sanitizer
registry *scopeRegistry
status scopeStatus
cm sync.RWMutex
gm sync.RWMutex
tm sync.RWMutex
hm sync.RWMutex
counters map[string]*counter
gauges map[string]*gauge
timers map[string]*timer
histograms map[string]*histogram
}
type scopeStatus struct {
sync.RWMutex
closed bool
quit chan struct{}
}
type scopeRegistry struct {
sync.RWMutex
subscopes map[string]*scope
}
var scopeRegistryKey = KeyForPrefixedStringMap
// ScopeOptions is a set of options to construct a scope.
type ScopeOptions struct {
Tags map[string]string
Prefix string
Reporter StatsReporter
CachedReporter CachedStatsReporter
Separator string
DefaultBuckets Buckets
SanitizeOptions *SanitizeOptions
}
// NewRootScope creates a new root Scope with a set of options and
// a reporting interval.
// Must provide either a StatsReporter or a CachedStatsReporter.
func NewRootScope(opts ScopeOptions, interval time.Duration) (Scope, io.Closer) {
s := newRootScope(opts, interval)
return s, s
}
// NewTestScope creates a new Scope without a stats reporter with the
// given prefix and adds the ability to take snapshots of metrics emitted
// to it.
func NewTestScope(
prefix string,
tags map[string]string,
) TestScope {
return newRootScope(ScopeOptions{Prefix: prefix, Tags: tags}, 0)
}
func newRootScope(opts ScopeOptions, interval time.Duration) *scope {
sanitizer := NewNoOpSanitizer()
if o := opts.SanitizeOptions; o != nil {
sanitizer = NewSanitizer(*o)
}
if opts.Tags == nil {
opts.Tags = make(map[string]string)
}
if opts.Separator == "" {
opts.Separator = DefaultSeparator
}
var baseReporter BaseStatsReporter
if opts.Reporter != nil {
baseReporter = opts.Reporter
} else if opts.CachedReporter != nil {
baseReporter = opts.CachedReporter
}
if opts.DefaultBuckets == nil || opts.DefaultBuckets.Len() < 1 {
opts.DefaultBuckets = defaultScopeBuckets
}
s := &scope{
separator: sanitizer.Name(opts.Separator),
prefix: sanitizer.Name(opts.Prefix),
reporter: opts.Reporter,
cachedReporter: opts.CachedReporter,
baseReporter: baseReporter,
defaultBuckets: opts.DefaultBuckets,
sanitizer: sanitizer,
registry: &scopeRegistry{
subscopes: make(map[string]*scope),
},
status: scopeStatus{
closed: false,
quit: make(chan struct{}, 1),
},
counters: make(map[string]*counter),
gauges: make(map[string]*gauge),
timers: make(map[string]*timer),
histograms: make(map[string]*histogram),
}
// NB(r): Take a copy of the tags on creation
// so that it cannot be modified after set.
s.tags = s.copyAndSanitizeMap(opts.Tags)
// Register the root scope
s.registry.subscopes[scopeRegistryKey(s.prefix, s.tags)] = s
if interval > 0 {
go s.reportLoop(interval)
}
return s
}
// report dumps all aggregated stats into the reporter. Should be called automatically by the root scope periodically.
func (s *scope) report(r StatsReporter) {
s.cm.RLock()
for name, counter := range s.counters {
counter.report(s.fullyQualifiedName(name), s.tags, r)
}
s.cm.RUnlock()
s.gm.RLock()
for name, gauge := range s.gauges {
gauge.report(s.fullyQualifiedName(name), s.tags, r)
}
s.gm.RUnlock()
// we do nothing for timers here because timers report directly to ths StatsReporter without buffering
s.hm.RLock()
for name, histogram := range s.histograms {
histogram.report(s.fullyQualifiedName(name), s.tags, r)
}
s.hm.RUnlock()
r.Flush()
}
func (s *scope) cachedReport(c CachedStatsReporter) {
s.cm.RLock()
for _, counter := range s.counters {
counter.cachedReport()
}
s.cm.RUnlock()
s.gm.RLock()
for _, gauge := range s.gauges {
gauge.cachedReport()
}
s.gm.RUnlock()
// we do nothing for timers here because timers report directly to ths StatsReporter without buffering
s.hm.RLock()
for _, histogram := range s.histograms {
histogram.cachedReport()
}
s.hm.RUnlock()
c.Flush()
}
// reportLoop is used by the root scope for periodic reporting
func (s *scope) reportLoop(interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
s.reportLoopRun()
case <-s.status.quit:
return
}
}
}
func (s *scope) reportLoopRun() {
// Need to hold a status lock to ensure not to report
// and flush after a close
s.status.RLock()
if s.status.closed {
s.status.RUnlock()
return
}
s.reportRegistryWithLock()
s.status.RUnlock()
}
// reports current registry with scope status lock held
func (s *scope) reportRegistryWithLock() {
s.registry.RLock()
if s.reporter != nil {
for _, ss := range s.registry.subscopes {
ss.report(s.reporter)
}
} else if s.cachedReporter != nil {
for _, ss := range s.registry.subscopes {
ss.cachedReport(s.cachedReporter)
}
}
s.registry.RUnlock()
}
func (s *scope) Counter(name string) Counter {
name = s.sanitizer.Name(name)
s.cm.RLock()
val, ok := s.counters[name]
s.cm.RUnlock()
if !ok {
s.cm.Lock()
val, ok = s.counters[name]
if !ok {
var cachedCounter CachedCount
if s.cachedReporter != nil {
cachedCounter = s.cachedReporter.AllocateCounter(
s.fullyQualifiedName(name), s.tags,
)
}
val = newCounter(cachedCounter)
s.counters[name] = val
}
s.cm.Unlock()
}
return val
}
func (s *scope) Gauge(name string) Gauge {
name = s.sanitizer.Name(name)
s.gm.RLock()
val, ok := s.gauges[name]
s.gm.RUnlock()
if !ok {
s.gm.Lock()
val, ok = s.gauges[name]
if !ok {
var cachedGauge CachedGauge
if s.cachedReporter != nil {
cachedGauge = s.cachedReporter.AllocateGauge(
s.fullyQualifiedName(name), s.tags,
)
}
val = newGauge(cachedGauge)
s.gauges[name] = val
}
s.gm.Unlock()
}
return val
}
func (s *scope) Timer(name string) Timer {
name = s.sanitizer.Name(name)
s.tm.RLock()
val, ok := s.timers[name]
s.tm.RUnlock()
if !ok {
s.tm.Lock()
val, ok = s.timers[name]
if !ok {
var cachedTimer CachedTimer
if s.cachedReporter != nil {
cachedTimer = s.cachedReporter.AllocateTimer(
s.fullyQualifiedName(name), s.tags,
)
}
val = newTimer(
s.fullyQualifiedName(name), s.tags, s.reporter, cachedTimer,
)
s.timers[name] = val
}
s.tm.Unlock()
}
return val
}
func (s *scope) Histogram(name string, b Buckets) Histogram {
name = s.sanitizer.Name(name)
if b == nil {
b = s.defaultBuckets
}
s.hm.RLock()
val, ok := s.histograms[name]
s.hm.RUnlock()
if !ok {
s.hm.Lock()
val, ok = s.histograms[name]
if !ok {
var cachedHistogram CachedHistogram
if s.cachedReporter != nil {
cachedHistogram = s.cachedReporter.AllocateHistogram(
s.fullyQualifiedName(name), s.tags, b,
)
}
val = newHistogram(
s.fullyQualifiedName(name), s.tags, s.reporter, b, cachedHistogram,
)
s.histograms[name] = val
}
s.hm.Unlock()
}
return val
}
func (s *scope) Tagged(tags map[string]string) Scope {
tags = s.copyAndSanitizeMap(tags)
return s.subscope(s.prefix, tags)
}
func (s *scope) SubScope(prefix string) Scope {
prefix = s.sanitizer.Name(prefix)
return s.subscope(s.fullyQualifiedName(prefix), nil)
}
func (s *scope) subscope(prefix string, immutableTags map[string]string) Scope {
immutableTags = mergeRightTags(s.tags, immutableTags)
key := scopeRegistryKey(prefix, immutableTags)
s.registry.RLock()
existing, ok := s.registry.subscopes[key]
if ok {
s.registry.RUnlock()
return existing
}
s.registry.RUnlock()
s.registry.Lock()
defer s.registry.Unlock()
existing, ok = s.registry.subscopes[key]
if ok {
return existing
}
subscope := &scope{
separator: s.separator,
prefix: prefix,
// NB(prateek): don't need to copy the tags here,
// we assume the map provided is immutable.
tags: immutableTags,
reporter: s.reporter,
cachedReporter: s.cachedReporter,
baseReporter: s.baseReporter,
defaultBuckets: s.defaultBuckets,
sanitizer: s.sanitizer,
registry: s.registry,
counters: make(map[string]*counter),
gauges: make(map[string]*gauge),
timers: make(map[string]*timer),
histograms: make(map[string]*histogram),
}
s.registry.subscopes[key] = subscope
return subscope
}
func (s *scope) Capabilities() Capabilities {
if s.baseReporter == nil {
return capabilitiesNone
}
return s.baseReporter.Capabilities()
}
func (s *scope) Snapshot() Snapshot {
snap := newSnapshot()
s.registry.RLock()
for _, ss := range s.registry.subscopes {
// NB(r): tags are immutable, no lock required to read.
tags := make(map[string]string, len(s.tags))
for k, v := range ss.tags {
tags[k] = v
}
ss.cm.RLock()
for key, c := range ss.counters {
name := ss.fullyQualifiedName(key)
id := KeyForPrefixedStringMap(name, tags)
snap.counters[id] = &counterSnapshot{
name: name,
tags: tags,
value: c.snapshot(),
}
}
ss.cm.RUnlock()
ss.gm.RLock()
for key, g := range ss.gauges {
name := ss.fullyQualifiedName(key)
id := KeyForPrefixedStringMap(name, tags)
snap.gauges[id] = &gaugeSnapshot{
name: name,
tags: tags,
value: g.snapshot(),
}
}
ss.gm.RUnlock()
ss.tm.RLock()
for key, t := range ss.timers {
name := ss.fullyQualifiedName(key)
id := KeyForPrefixedStringMap(name, tags)
snap.timers[id] = &timerSnapshot{
name: name,
tags: tags,
values: t.snapshot(),
}
}
ss.tm.RUnlock()
ss.hm.RLock()
for key, h := range ss.histograms {
name := ss.fullyQualifiedName(key)
id := KeyForPrefixedStringMap(name, tags)
snap.histograms[id] = &histogramSnapshot{
name: name,
tags: tags,
values: h.snapshotValues(),
durations: h.snapshotDurations(),
}
}
ss.hm.RUnlock()
}
s.registry.RUnlock()
return snap
}
func (s *scope) Close() error {
s.status.Lock()
// don't wait to close more than once (panic on double close of
// s.status.quit)
if s.status.closed {
s.status.Unlock()
return nil
}
s.status.closed = true
close(s.status.quit)
s.reportRegistryWithLock()
s.status.Unlock()
if closer, ok := s.baseReporter.(io.Closer); ok {
return closer.Close()
}
return nil
}
// NB(prateek): We assume concatenation of sanitized inputs is
// sanitized. If that stops being true, then we need to sanitize the
// output of this function.
func (s *scope) fullyQualifiedName(name string) string {
if len(s.prefix) == 0 {
return name
}
// NB: we don't need to sanitize the output of this function as we
// sanitize all the the inputs (prefix, separator, name); and the
// output we're creating is a concatenation of the sanitized inputs.
// If we change the concatenation to involve other inputs or characters,
// we'll need to sanitize them too.
return fmt.Sprintf("%s%s%s", s.prefix, s.separator, name)
}
func (s *scope) copyAndSanitizeMap(tags map[string]string) map[string]string {
result := make(map[string]string, len(tags))
for k, v := range tags {
k = s.sanitizer.Key(k)
v = s.sanitizer.Value(v)
result[k] = v
}
return result
}
// TestScope is a metrics collector that has no reporting, ensuring that
// all emitted values have a given prefix or set of tags
type TestScope interface {
Scope
// Snapshot returns a copy of all values since the last report execution,
// this is an expensive operation and should only be use for testing purposes
Snapshot() Snapshot
}
// Snapshot is a snapshot of values since last report execution
type Snapshot interface {
// Counters returns a snapshot of all counter summations since last report execution
Counters() map[string]CounterSnapshot
// Gauges returns a snapshot of gauge last values since last report execution
Gauges() map[string]GaugeSnapshot
// Timers returns a snapshot of timer values since last report execution
Timers() map[string]TimerSnapshot
// Histograms returns a snapshot of histogram samples since last report execution
Histograms() map[string]HistogramSnapshot
}
// CounterSnapshot is a snapshot of a counter
type CounterSnapshot interface {
// Name returns the name
Name() string
// Tags returns the tags
Tags() map[string]string
// Value returns the value
Value() int64
}
// GaugeSnapshot is a snapshot of a gauge
type GaugeSnapshot interface {
// Name returns the name
Name() string
// Tags returns the tags
Tags() map[string]string
// Value returns the value
Value() float64
}
// TimerSnapshot is a snapshot of a timer
type TimerSnapshot interface {
// Name returns the name
Name() string
// Tags returns the tags
Tags() map[string]string
// Values returns the values
Values() []time.Duration
}
// HistogramSnapshot is a snapshot of a histogram
type HistogramSnapshot interface {
// Name returns the name
Name() string
// Tags returns the tags
Tags() map[string]string
// Values returns the sample values by upper bound for a valueHistogram
Values() map[float64]int64
// Durations returns the sample values by upper bound for a durationHistogram
Durations() map[time.Duration]int64
}
// mergeRightTags merges 2 sets of tags with the tags from tagsRight overriding values from tagsLeft
func mergeRightTags(tagsLeft, tagsRight map[string]string) map[string]string {
if tagsLeft == nil && tagsRight == nil {
return nil
}
if len(tagsRight) == 0 {
return tagsLeft
}
if len(tagsLeft) == 0 {
return tagsRight
}
result := make(map[string]string, len(tagsLeft)+len(tagsRight))
for k, v := range tagsLeft {
result[k] = v
}
for k, v := range tagsRight {
result[k] = v
}
return result
}
type snapshot struct {
counters map[string]CounterSnapshot
gauges map[string]GaugeSnapshot
timers map[string]TimerSnapshot
histograms map[string]HistogramSnapshot
}
func newSnapshot() *snapshot {
return &snapshot{
counters: make(map[string]CounterSnapshot),
gauges: make(map[string]GaugeSnapshot),
timers: make(map[string]TimerSnapshot),
histograms: make(map[string]HistogramSnapshot),
}
}
func (s *snapshot) Counters() map[string]CounterSnapshot {
return s.counters
}
func (s *snapshot) Gauges() map[string]GaugeSnapshot {
return s.gauges
}
func (s *snapshot) Timers() map[string]TimerSnapshot {
return s.timers
}
func (s *snapshot) Histograms() map[string]HistogramSnapshot {
return s.histograms
}
type counterSnapshot struct {
name string
tags map[string]string
value int64
}
func (s *counterSnapshot) Name() string {
return s.name
}
func (s *counterSnapshot) Tags() map[string]string {
return s.tags
}
func (s *counterSnapshot) Value() int64 {
return s.value
}
type gaugeSnapshot struct {
name string
tags map[string]string
value float64
}
func (s *gaugeSnapshot) Name() string {
return s.name
}
func (s *gaugeSnapshot) Tags() map[string]string {
return s.tags
}
func (s *gaugeSnapshot) Value() float64 {
return s.value
}
type timerSnapshot struct {
name string
tags map[string]string
values []time.Duration
}
func (s *timerSnapshot) Name() string {
return s.name
}
func (s *timerSnapshot) Tags() map[string]string {
return s.tags
}
func (s *timerSnapshot) Values() []time.Duration {
return s.values
}
type histogramSnapshot struct {
name string
tags map[string]string
values map[float64]int64
durations map[time.Duration]int64
}
func (s *histogramSnapshot) Name() string {
return s.name
}
func (s *histogramSnapshot) Tags() map[string]string {
return s.tags
}
func (s *histogramSnapshot) Values() map[float64]int64 {
return s.values
}
func (s *histogramSnapshot) Durations() map[time.Duration]int64 {
return s.durations
}