mirror of
https://github.com/rclone/rclone.git
synced 2024-12-24 16:09:01 +01:00
220 lines
5.6 KiB
Go
220 lines
5.6 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 (
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/spacemonkeygo/monkit/v3/monotime"
|
|
)
|
|
|
|
// FuncStats keeps track of statistics about a possible function's execution.
|
|
// Should be created with NewFuncStats, though expected creation is through a
|
|
// Func object:
|
|
//
|
|
// var mon = monkit.Package()
|
|
//
|
|
// func MyFunc() {
|
|
// f := mon.Func()
|
|
// ...
|
|
// }
|
|
//
|
|
type FuncStats struct {
|
|
// sync/atomic things
|
|
current int64
|
|
highwater int64
|
|
parentsAndMutex funcSet
|
|
|
|
// mutex things (reuses mutex from parents)
|
|
errors map[string]int64
|
|
panics int64
|
|
successTimes DurationDist
|
|
failureTimes DurationDist
|
|
key SeriesKey
|
|
}
|
|
|
|
func initFuncStats(f *FuncStats, key SeriesKey) {
|
|
f.key = key
|
|
f.errors = map[string]int64{}
|
|
|
|
key.Measurement += "_times"
|
|
initDurationDist(&f.successTimes, key.WithTag("kind", "success"))
|
|
initDurationDist(&f.failureTimes, key.WithTag("kind", "failure"))
|
|
}
|
|
|
|
// NewFuncStats creates a FuncStats
|
|
func NewFuncStats(key SeriesKey) (f *FuncStats) {
|
|
f = &FuncStats{}
|
|
initFuncStats(f, key)
|
|
return f
|
|
}
|
|
|
|
// Reset resets all recorded data.
|
|
func (f *FuncStats) Reset() {
|
|
atomic.StoreInt64(&f.current, 0)
|
|
atomic.StoreInt64(&f.highwater, 0)
|
|
f.parentsAndMutex.Lock()
|
|
f.errors = make(map[string]int64, len(f.errors))
|
|
f.panics = 0
|
|
f.successTimes.Reset()
|
|
f.failureTimes.Reset()
|
|
f.parentsAndMutex.Unlock()
|
|
}
|
|
|
|
func (f *FuncStats) start(parent *Func) {
|
|
f.parentsAndMutex.Add(parent)
|
|
current := atomic.AddInt64(&f.current, 1)
|
|
for {
|
|
highwater := atomic.LoadInt64(&f.highwater)
|
|
if current <= highwater ||
|
|
atomic.CompareAndSwapInt64(&f.highwater, highwater, current) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *FuncStats) end(err error, panicked bool, duration time.Duration) {
|
|
atomic.AddInt64(&f.current, -1)
|
|
f.parentsAndMutex.Lock()
|
|
if panicked {
|
|
f.panics += 1
|
|
f.failureTimes.Insert(duration)
|
|
f.parentsAndMutex.Unlock()
|
|
return
|
|
}
|
|
if err == nil {
|
|
f.successTimes.Insert(duration)
|
|
f.parentsAndMutex.Unlock()
|
|
return
|
|
}
|
|
f.failureTimes.Insert(duration)
|
|
f.errors[getErrorName(err)] += 1
|
|
f.parentsAndMutex.Unlock()
|
|
}
|
|
|
|
// Current returns how many concurrent instances of this function are currently
|
|
// being observed.
|
|
func (f *FuncStats) Current() int64 { return atomic.LoadInt64(&f.current) }
|
|
|
|
// Highwater returns the highest value Current() would ever return.
|
|
func (f *FuncStats) Highwater() int64 { return atomic.LoadInt64(&f.highwater) }
|
|
|
|
// Success returns the number of successes that have been observed
|
|
func (f *FuncStats) Success() (rv int64) {
|
|
f.parentsAndMutex.Lock()
|
|
rv = f.successTimes.Count
|
|
f.parentsAndMutex.Unlock()
|
|
return rv
|
|
}
|
|
|
|
// Panics returns the number of panics that have been observed
|
|
func (f *FuncStats) Panics() (rv int64) {
|
|
f.parentsAndMutex.Lock()
|
|
rv = f.panics
|
|
f.parentsAndMutex.Unlock()
|
|
return rv
|
|
}
|
|
|
|
// Errors returns the number of errors observed by error type. The error type
|
|
// is determined by handlers from AddErrorNameHandler, or a default that works
|
|
// with most error types.
|
|
func (f *FuncStats) Errors() (rv map[string]int64) {
|
|
f.parentsAndMutex.Lock()
|
|
rv = make(map[string]int64, len(f.errors))
|
|
for errname, count := range f.errors {
|
|
rv[errname] = count
|
|
}
|
|
f.parentsAndMutex.Unlock()
|
|
return rv
|
|
}
|
|
|
|
func (f *FuncStats) parents(cb func(f *Func)) {
|
|
f.parentsAndMutex.Iterate(cb)
|
|
}
|
|
|
|
// Stats implements the StatSource interface
|
|
func (f *FuncStats) Stats(cb func(key SeriesKey, field string, val float64)) {
|
|
cb(f.key, "current", float64(f.Current()))
|
|
cb(f.key, "highwater", float64(f.Highwater()))
|
|
|
|
f.parentsAndMutex.Lock()
|
|
panics := f.panics
|
|
errs := make(map[string]int64, len(f.errors))
|
|
for errname, count := range f.errors {
|
|
errs[errname] = count
|
|
}
|
|
st := f.successTimes.Copy()
|
|
ft := f.failureTimes.Copy()
|
|
f.parentsAndMutex.Unlock()
|
|
|
|
cb(f.key, "successes", float64(st.Count))
|
|
e_count := int64(0)
|
|
for errname, count := range errs {
|
|
e_count += count
|
|
cb(f.key.WithTag("error_name", errname), "count", float64(count))
|
|
}
|
|
cb(f.key, "errors", float64(e_count))
|
|
cb(f.key, "panics", float64(panics))
|
|
cb(f.key, "failures", float64(e_count+panics))
|
|
cb(f.key, "total", float64(st.Count+e_count+panics))
|
|
|
|
st.Stats(cb)
|
|
ft.Stats(cb)
|
|
}
|
|
|
|
// SuccessTimes returns a DurationDist of successes
|
|
func (f *FuncStats) SuccessTimes() *DurationDist {
|
|
f.parentsAndMutex.Lock()
|
|
d := f.successTimes.Copy()
|
|
f.parentsAndMutex.Unlock()
|
|
return d
|
|
}
|
|
|
|
// FailureTimes returns a DurationDist of failures (includes panics and errors)
|
|
func (f *FuncStats) FailureTimes() *DurationDist {
|
|
f.parentsAndMutex.Lock()
|
|
d := f.failureTimes.Copy()
|
|
f.parentsAndMutex.Unlock()
|
|
return d
|
|
}
|
|
|
|
// Observe starts the stopwatch for observing this function and returns a
|
|
// function to be called at the end of the function execution. Expected usage
|
|
// like:
|
|
//
|
|
// func MyFunc() (err error) {
|
|
// defer funcStats.Observe()(&err)
|
|
// ...
|
|
// }
|
|
//
|
|
func (f *FuncStats) Observe() func(errptr *error) {
|
|
f.start(nil)
|
|
start := monotime.Now()
|
|
return func(errptr *error) {
|
|
rec := recover()
|
|
panicked := rec != nil
|
|
finish := monotime.Now()
|
|
var err error
|
|
if errptr != nil {
|
|
err = *errptr
|
|
}
|
|
f.end(err, panicked, finish.Sub(start))
|
|
if panicked {
|
|
panic(rec)
|
|
}
|
|
}
|
|
}
|