rclone/vendor/github.com/spacemonkeygo/monkit/v3/scope.go
2020-05-12 15:56:50 +00:00

302 lines
8.3 KiB
Go

// Copyright (C) 2015 Space Monkey, 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 monkit
import (
"fmt"
"strings"
"sync"
)
// Scope represents a named collection of StatSources. Scopes are constructed
// through Registries.
type Scope struct {
r *Registry
name string
mtx sync.RWMutex
sources map[string]StatSource
chains []StatSource
}
func newScope(r *Registry, name string) *Scope {
return &Scope{
r: r,
name: name,
sources: map[string]StatSource{}}
}
// Func retrieves or creates a Func named after the currently executing
// function name (via runtime.Caller. See FuncNamed to choose your own name.
func (s *Scope) Func() *Func {
return s.FuncNamed(callerFunc(0))
}
func (s *Scope) newSource(name string, constructor func() StatSource) (
rv StatSource) {
s.mtx.RLock()
source, exists := s.sources[name]
s.mtx.RUnlock()
if exists {
return source
}
s.mtx.Lock()
if source, exists := s.sources[name]; exists {
s.mtx.Unlock()
return source
}
ss := constructor()
s.sources[name] = ss
s.mtx.Unlock()
return ss
}
// FuncNamed retrieves or creates a Func named using the given name and
// SeriesTags. See Func() for automatic name determination.
//
// Each unique combination of keys/values in each SeriesTag will result in a
// unique Func. SeriesTags are not sorted, so keep the order consistent to avoid
// unintentionally creating new unique Funcs.
func (s *Scope) FuncNamed(name string, tags ...SeriesTag) *Func {
var sourceName strings.Builder
sourceName.WriteString("func:")
sourceName.WriteString(name)
for _, tag := range tags {
sourceName.WriteByte(',')
sourceName.WriteString(tag.Key)
sourceName.WriteByte('=')
sourceName.WriteString(tag.Val)
}
source := s.newSource(sourceName.String(), func() StatSource {
key := NewSeriesKey("function").WithTag("name", name)
for _, tag := range tags {
key = key.WithTag(tag.Key, tag.Val)
}
return newFunc(s, key)
})
f, ok := source.(*Func)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return f
}
// Funcs calls 'cb' for all Funcs registered on this Scope.
func (s *Scope) Funcs(cb func(f *Func)) {
s.mtx.Lock()
funcs := make(map[*Func]struct{}, len(s.sources))
for _, source := range s.sources {
if f, ok := source.(*Func); ok {
funcs[f] = struct{}{}
}
}
s.mtx.Unlock()
for f := range funcs {
cb(f)
}
}
// Meter retrieves or creates a Meter named after the given name. See Event.
func (s *Scope) Meter(name string) *Meter {
source := s.newSource(name, func() StatSource { return NewMeter(NewSeriesKey(name)) })
m, ok := source.(*Meter)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// Event retrieves or creates a Meter named after the given name and then
// calls Mark(1) on that meter.
func (s *Scope) Event(name string) {
s.Meter(name).Mark(1)
}
// DiffMeter retrieves or creates a DiffMeter after the given name and two
// submeters.
func (s *Scope) DiffMeter(name string, m1, m2 *Meter) {
source := s.newSource(name, func() StatSource {
return NewDiffMeter(NewSeriesKey(name), m1, m2)
})
if _, ok := source.(*DiffMeter); !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
}
// IntVal retrieves or creates an IntVal after the given name.
func (s *Scope) IntVal(name string) *IntVal {
source := s.newSource(name, func() StatSource { return NewIntVal(NewSeriesKey(name)) })
m, ok := source.(*IntVal)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// IntValf retrieves or creates an IntVal after the given printf-formatted
// name.
func (s *Scope) IntValf(template string, args ...interface{}) *IntVal {
return s.IntVal(fmt.Sprintf(template, args...))
}
// FloatVal retrieves or creates a FloatVal after the given name.
func (s *Scope) FloatVal(name string) *FloatVal {
source := s.newSource(name, func() StatSource { return NewFloatVal(NewSeriesKey(name)) })
m, ok := source.(*FloatVal)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// FloatValf retrieves or creates a FloatVal after the given printf-formatted
// name.
func (s *Scope) FloatValf(template string, args ...interface{}) *FloatVal {
return s.FloatVal(fmt.Sprintf(template, args...))
}
// BoolVal retrieves or creates a BoolVal after the given name.
func (s *Scope) BoolVal(name string) *BoolVal {
source := s.newSource(name, func() StatSource { return NewBoolVal(NewSeriesKey(name)) })
m, ok := source.(*BoolVal)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// BoolValf retrieves or creates a BoolVal after the given printf-formatted
// name.
func (s *Scope) BoolValf(template string, args ...interface{}) *BoolVal {
return s.BoolVal(fmt.Sprintf(template, args...))
}
// StructVal retrieves or creates a StructVal after the given name.
func (s *Scope) StructVal(name string) *StructVal {
source := s.newSource(name, func() StatSource { return NewStructVal(NewSeriesKey(name)) })
m, ok := source.(*StructVal)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// Timer retrieves or creates a Timer after the given name.
func (s *Scope) Timer(name string) *Timer {
source := s.newSource(name, func() StatSource { return NewTimer(NewSeriesKey(name)) })
m, ok := source.(*Timer)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// Counter retrieves or creates a Counter after the given name.
func (s *Scope) Counter(name string) *Counter {
source := s.newSource(name, func() StatSource { return NewCounter(NewSeriesKey(name)) })
m, ok := source.(*Counter)
if !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
return m
}
// Gauge registers a callback that returns a float as the given name in the
// Scope's StatSource table.
func (s *Scope) Gauge(name string, cb func() float64) {
type gauge struct{ StatSource }
// gauges allow overwriting
s.mtx.Lock()
defer s.mtx.Unlock()
if source, exists := s.sources[name]; exists {
if _, ok := source.(gauge); !ok {
panic(fmt.Sprintf("%s already used for another stats source: %#v",
name, source))
}
}
s.sources[name] = gauge{StatSource: StatSourceFunc(
func(scb func(key SeriesKey, field string, value float64)) {
scb(NewSeriesKey(name), "value", cb())
}),
}
}
// Chain registers a full StatSource as the given name in the Scope's
// StatSource table.
func (s *Scope) Chain(source StatSource) {
// chains allow overwriting
s.mtx.Lock()
defer s.mtx.Unlock()
s.chains = append(s.chains, source)
}
func (s *Scope) allNamedSources() (sources []namedSource) {
s.mtx.Lock()
sources = make([]namedSource, 0, len(s.sources))
for name, source := range s.sources {
sources = append(sources, namedSource{name: name, source: source})
}
s.mtx.Unlock()
return sources
}
// Stats implements the StatSource interface.
func (s *Scope) Stats(cb func(key SeriesKey, field string, val float64)) {
for _, namedSource := range s.allNamedSources() {
namedSource.source.Stats(cb)
}
s.mtx.Lock()
chains := append([]StatSource(nil), s.chains...)
s.mtx.Unlock()
for _, source := range chains {
source.Stats(cb)
}
}
// Name returns the name of the Scope, often the Package name.
func (s *Scope) Name() string { return s.name }
var _ StatSource = (*Scope)(nil)
type namedSource struct {
name string
source StatSource
}
type namedSourceList []namedSource
func (l namedSourceList) Len() int { return len(l) }
func (l namedSourceList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l namedSourceList) Less(i, j int) bool { return l[i].name < l[j].name }