2020-03-17 14:00:16 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package metric
|
|
|
|
|
|
|
|
import (
|
2020-03-20 06:29:48 -06:00
|
|
|
"fmt"
|
2020-03-17 14:00:16 -06:00
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/tools/internal/telemetry/event"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Data represents a single point in the time series of a metric.
|
|
|
|
// This provides the common interface to all metrics no matter their data
|
|
|
|
// format.
|
|
|
|
// To get the actual values for the metric you must type assert to a concrete
|
|
|
|
// metric type.
|
|
|
|
type Data interface {
|
|
|
|
// Handle returns the metric handle this data is for.
|
|
|
|
//TODO: rethink the concept of metric handles
|
|
|
|
Handle() string
|
|
|
|
// Groups reports the rows that currently exist for this metric.
|
2020-03-20 06:29:48 -06:00
|
|
|
Groups() [][]event.Tag
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Int64Data is a concrete implementation of Data for int64 scalar metrics.
|
|
|
|
type Int64Data struct {
|
|
|
|
// Info holds the original construction information.
|
|
|
|
Info *Scalar
|
|
|
|
// IsGauge is true for metrics that track values, rather than increasing over time.
|
|
|
|
IsGauge bool
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []int64
|
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime time.Time
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
groups [][]event.Tag
|
2020-03-24 19:30:31 -06:00
|
|
|
key *event.Int64Key
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Float64Data is a concrete implementation of Data for float64 scalar metrics.
|
|
|
|
type Float64Data struct {
|
|
|
|
// Info holds the original construction information.
|
|
|
|
Info *Scalar
|
|
|
|
// IsGauge is true for metrics that track values, rather than increasing over time.
|
|
|
|
IsGauge bool
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []float64
|
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime time.Time
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
groups [][]event.Tag
|
2020-03-24 19:30:31 -06:00
|
|
|
key *event.Float64Key
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramInt64Data is a concrete implementation of Data for int64 histogram metrics.
|
|
|
|
type HistogramInt64Data struct {
|
|
|
|
// Info holds the original construction information.
|
|
|
|
Info *HistogramInt64
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []*HistogramInt64Row
|
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime time.Time
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
groups [][]event.Tag
|
2020-03-24 19:30:31 -06:00
|
|
|
key *event.Int64Key
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramInt64Row holds the values for a single row of a HistogramInt64Data.
|
|
|
|
type HistogramInt64Row struct {
|
|
|
|
// Values is the counts per bucket.
|
|
|
|
Values []int64
|
|
|
|
// Count is the total count.
|
|
|
|
Count int64
|
|
|
|
// Sum is the sum of all the values recorded.
|
|
|
|
Sum int64
|
|
|
|
// Min is the smallest recorded value.
|
|
|
|
Min int64
|
|
|
|
// Max is the largest recorded value.
|
|
|
|
Max int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramFloat64Data is a concrete implementation of Data for float64 histogram metrics.
|
|
|
|
type HistogramFloat64Data struct {
|
|
|
|
// Info holds the original construction information.
|
|
|
|
Info *HistogramFloat64
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []*HistogramFloat64Row
|
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime time.Time
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
groups [][]event.Tag
|
2020-03-24 19:30:31 -06:00
|
|
|
key *event.Float64Key
|
2020-03-17 14:00:16 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramFloat64Row holds the values for a single row of a HistogramFloat64Data.
|
|
|
|
type HistogramFloat64Row struct {
|
|
|
|
// Values is the counts per bucket.
|
|
|
|
Values []int64
|
|
|
|
// Count is the total count.
|
|
|
|
Count int64
|
|
|
|
// Sum is the sum of all the values recorded.
|
|
|
|
Sum float64
|
|
|
|
// Min is the smallest recorded value.
|
|
|
|
Min float64
|
|
|
|
// Max is the largest recorded value.
|
|
|
|
Max float64
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func tagListEqual(a, b []event.Tag) bool {
|
|
|
|
//TODO: make this more efficient
|
|
|
|
return fmt.Sprint(a) == fmt.Sprint(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tagListLess(a, b []event.Tag) bool {
|
|
|
|
//TODO: make this more efficient
|
|
|
|
return fmt.Sprint(a) < fmt.Sprint(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getGroup(tagMap event.TagMap, g *[][]event.Tag, keys []event.Key) (int, bool) {
|
|
|
|
group := make([]event.Tag, len(keys))
|
|
|
|
for i, key := range keys {
|
|
|
|
tag := tagMap.Find(key)
|
|
|
|
if tag.Valid() {
|
|
|
|
group[i] = tag
|
|
|
|
}
|
|
|
|
}
|
2020-03-17 14:00:16 -06:00
|
|
|
old := *g
|
|
|
|
index := sort.Search(len(old), func(i int) bool {
|
2020-03-20 06:29:48 -06:00
|
|
|
return !tagListLess(old[i], group)
|
2020-03-17 14:00:16 -06:00
|
|
|
})
|
2020-03-20 06:29:48 -06:00
|
|
|
if index < len(old) && tagListEqual(group, old[index]) {
|
2020-03-17 14:00:16 -06:00
|
|
|
// not a new group
|
|
|
|
return index, false
|
|
|
|
}
|
2020-03-20 06:29:48 -06:00
|
|
|
*g = make([][]event.Tag, len(old)+1)
|
2020-03-17 14:00:16 -06:00
|
|
|
copy(*g, old[:index])
|
|
|
|
copy((*g)[index+1:], old[index:])
|
|
|
|
(*g)[index] = group
|
|
|
|
return index, true
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Int64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *Int64Data) Groups() [][]event.Tag { return data.groups }
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Int64Data) modify(at time.Time, tagMap event.TagMap, f func(v int64) int64) Data {
|
|
|
|
index, insert := getGroup(tagMap, &data.groups, data.Info.Keys)
|
2020-03-17 14:00:16 -06:00
|
|
|
old := data.Rows
|
|
|
|
if insert {
|
|
|
|
data.Rows = make([]int64, len(old)+1)
|
|
|
|
copy(data.Rows, old[:index])
|
|
|
|
copy(data.Rows[index+1:], old[index:])
|
|
|
|
} else {
|
|
|
|
data.Rows = make([]int64, len(old))
|
|
|
|
copy(data.Rows, old)
|
|
|
|
}
|
|
|
|
data.Rows[index] = f(data.Rows[index])
|
2020-03-20 06:29:48 -06:00
|
|
|
data.EndTime = at
|
2020-03-17 14:00:16 -06:00
|
|
|
frozen := *data
|
|
|
|
return &frozen
|
|
|
|
}
|
|
|
|
|
2020-03-24 19:30:31 -06:00
|
|
|
func (data *Int64Data) count(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
2020-03-20 06:29:48 -06:00
|
|
|
return data.modify(at, tagMap, func(v int64) int64 {
|
2020-03-17 14:00:16 -06:00
|
|
|
return v + 1
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Int64Data) sum(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v int64) int64 {
|
2020-03-24 19:30:31 -06:00
|
|
|
return v + data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Int64Data) latest(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v int64) int64 {
|
2020-03-24 19:30:31 -06:00
|
|
|
return data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Float64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *Float64Data) Groups() [][]event.Tag { return data.groups }
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Float64Data) modify(at time.Time, tagMap event.TagMap, f func(v float64) float64) Data {
|
|
|
|
index, insert := getGroup(tagMap, &data.groups, data.Info.Keys)
|
2020-03-17 14:00:16 -06:00
|
|
|
old := data.Rows
|
|
|
|
if insert {
|
|
|
|
data.Rows = make([]float64, len(old)+1)
|
|
|
|
copy(data.Rows, old[:index])
|
|
|
|
copy(data.Rows[index+1:], old[index:])
|
|
|
|
} else {
|
|
|
|
data.Rows = make([]float64, len(old))
|
|
|
|
copy(data.Rows, old)
|
|
|
|
}
|
|
|
|
data.Rows[index] = f(data.Rows[index])
|
2020-03-20 06:29:48 -06:00
|
|
|
data.EndTime = at
|
2020-03-17 14:00:16 -06:00
|
|
|
frozen := *data
|
|
|
|
return &frozen
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Float64Data) sum(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v float64) float64 {
|
2020-03-24 19:30:31 -06:00
|
|
|
return v + data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *Float64Data) latest(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v float64) float64 {
|
2020-03-24 19:30:31 -06:00
|
|
|
return data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramInt64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *HistogramInt64Data) Groups() [][]event.Tag { return data.groups }
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramInt64Data) modify(at time.Time, tagMap event.TagMap, f func(v *HistogramInt64Row)) Data {
|
|
|
|
index, insert := getGroup(tagMap, &data.groups, data.Info.Keys)
|
2020-03-17 14:00:16 -06:00
|
|
|
old := data.Rows
|
|
|
|
var v HistogramInt64Row
|
|
|
|
if insert {
|
|
|
|
data.Rows = make([]*HistogramInt64Row, len(old)+1)
|
|
|
|
copy(data.Rows, old[:index])
|
|
|
|
copy(data.Rows[index+1:], old[index:])
|
|
|
|
} else {
|
|
|
|
data.Rows = make([]*HistogramInt64Row, len(old))
|
|
|
|
copy(data.Rows, old)
|
|
|
|
v = *data.Rows[index]
|
|
|
|
}
|
|
|
|
oldValues := v.Values
|
|
|
|
v.Values = make([]int64, len(data.Info.Buckets))
|
|
|
|
copy(v.Values, oldValues)
|
|
|
|
f(&v)
|
|
|
|
data.Rows[index] = &v
|
2020-03-20 06:29:48 -06:00
|
|
|
data.EndTime = at
|
2020-03-17 14:00:16 -06:00
|
|
|
frozen := *data
|
|
|
|
return &frozen
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramInt64Data) record(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v *HistogramInt64Row) {
|
2020-03-24 19:30:31 -06:00
|
|
|
value := data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
v.Sum += value
|
|
|
|
if v.Min > value || v.Count == 0 {
|
|
|
|
v.Min = value
|
|
|
|
}
|
|
|
|
if v.Max < value || v.Count == 0 {
|
|
|
|
v.Max = value
|
|
|
|
}
|
|
|
|
v.Count++
|
|
|
|
for i, b := range data.Info.Buckets {
|
|
|
|
if value <= b {
|
|
|
|
v.Values[i]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramFloat64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *HistogramFloat64Data) Groups() [][]event.Tag { return data.groups }
|
2020-03-17 14:00:16 -06:00
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramFloat64Data) modify(at time.Time, tagMap event.TagMap, f func(v *HistogramFloat64Row)) Data {
|
|
|
|
index, insert := getGroup(tagMap, &data.groups, data.Info.Keys)
|
2020-03-17 14:00:16 -06:00
|
|
|
old := data.Rows
|
|
|
|
var v HistogramFloat64Row
|
|
|
|
if insert {
|
|
|
|
data.Rows = make([]*HistogramFloat64Row, len(old)+1)
|
|
|
|
copy(data.Rows, old[:index])
|
|
|
|
copy(data.Rows[index+1:], old[index:])
|
|
|
|
} else {
|
|
|
|
data.Rows = make([]*HistogramFloat64Row, len(old))
|
|
|
|
copy(data.Rows, old)
|
|
|
|
v = *data.Rows[index]
|
|
|
|
}
|
|
|
|
oldValues := v.Values
|
|
|
|
v.Values = make([]int64, len(data.Info.Buckets))
|
|
|
|
copy(v.Values, oldValues)
|
|
|
|
f(&v)
|
|
|
|
data.Rows[index] = &v
|
2020-03-20 06:29:48 -06:00
|
|
|
data.EndTime = at
|
2020-03-17 14:00:16 -06:00
|
|
|
frozen := *data
|
|
|
|
return &frozen
|
|
|
|
}
|
|
|
|
|
2020-03-20 06:29:48 -06:00
|
|
|
func (data *HistogramFloat64Data) record(at time.Time, tagMap event.TagMap, tag event.Tag) Data {
|
|
|
|
return data.modify(at, tagMap, func(v *HistogramFloat64Row) {
|
2020-03-24 19:30:31 -06:00
|
|
|
value := data.key.From(tag)
|
2020-03-17 14:00:16 -06:00
|
|
|
v.Sum += value
|
|
|
|
if v.Min > value || v.Count == 0 {
|
|
|
|
v.Min = value
|
|
|
|
}
|
|
|
|
if v.Max < value || v.Count == 0 {
|
|
|
|
v.Max = value
|
|
|
|
}
|
|
|
|
v.Count++
|
|
|
|
for i, b := range data.Info.Buckets {
|
|
|
|
if value <= b {
|
|
|
|
v.Values[i]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|