blob: a466e0b68f1de5a5e9018af151926ae5a335cd62 [file] [log] [blame]
// accum accumulates metrics that were previously reported individually.
package accum
import (
"sort"
"strings"
"go.skia.org/infra/go/metrics2"
"go.skia.org/infra/go/sklog"
)
// Reporter is the type of a func that is called to report metrics after they have been accumulated.
type Reporter func(metric string, tags map[string]string, value int64)
// DefaultReporter reports to metrics2.
func DefaultReporter(metric string, tags map[string]string, value int64) {
metrics2.GetInt64Metric(metric, tags).Update(value)
}
// metricAccum is the information we track per metric trace.
type metricAccum struct {
total int64
num int64
tags map[string]string
}
// Accum accumulates measurements with common measurement names and tags.
type Accum struct {
// map[<meas>]map[<keys>]
values map[string]map[string]*metricAccum
reporter Reporter
}
func New(reporter Reporter) *Accum {
return &Accum{
values: map[string]map[string]*metricAccum{},
reporter: reporter,
}
}
func keyFromTags(tags map[string]string) string {
keys := make([]string, 0, len(tags))
for key, _ := range tags {
keys = append(keys, key)
}
sort.Strings(keys)
ret := []string{}
for _, key := range keys {
ret = append(ret, key, tags[key])
}
return strings.Join(ret, " ")
}
// Add another measurement for 'metric' with 'tags'.
func (a *Accum) Add(metric string, tags map[string]string, duration int64) {
if _, ok := tags["task-id"]; ok {
// Log each value to logging to aid in debugging bad bots.
sklog.Infof("Task: %s %s Value: %d", metric, keyFromTags(tags), duration)
// Remove 'task-id'.
delete(tags, "task-id")
}
byMetric, ok := a.values[metric]
if !ok {
a.values[metric] = map[string]*metricAccum{}
byMetric = a.values[metric]
}
key := keyFromTags(tags)
byTags, ok := byMetric[key]
if !ok {
byMetric[key] = &metricAccum{
tags: tags,
}
byTags = byMetric[key]
}
byTags.total += duration
byTags.num += 1
}
// Report the accumulated metrics and then reset totals and counts.
func (a *Accum) Report() {
for metric, byTags := range a.values {
for _, accum := range byTags {
accum.tags["type"] = "duration"
a.reporter(metric, accum.tags, accum.total)
accum.tags["type"] = "num"
a.reporter(metric, accum.tags, accum.num)
}
}
a.values = map[string]map[string]*metricAccum{}
}