mirror of
https://github.com/rclone/rclone.git
synced 2024-12-02 13:25:51 +01:00
302 lines
8.3 KiB
Go
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 }
|