2019-07-08 19:22:37 -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 aggregates stats into metrics that can be exported.
|
|
|
|
package metric
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sort"
|
2019-10-06 17:49:04 -06:00
|
|
|
"time"
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry"
|
|
|
|
"golang.org/x/tools/internal/telemetry/export"
|
2019-08-13 13:07:39 -06:00
|
|
|
"golang.org/x/tools/internal/telemetry/stats"
|
|
|
|
"golang.org/x/tools/internal/telemetry/tag"
|
2019-07-08 19:22:37 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// Scalar represents the construction information for a scalar metric.
|
|
|
|
type Scalar struct {
|
|
|
|
// Name is the unique name of this metric.
|
|
|
|
Name string
|
|
|
|
// Description can be used by observers to describe the metric to users.
|
|
|
|
Description string
|
|
|
|
// Keys is the set of tags that collectively describe rows of the metric.
|
|
|
|
Keys []interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramInt64 represents the construction information for an int64 histogram metric.
|
|
|
|
type HistogramInt64 struct {
|
|
|
|
// Name is the unique name of this metric.
|
|
|
|
Name string
|
|
|
|
// Description can be used by observers to describe the metric to users.
|
|
|
|
Description string
|
|
|
|
// Keys is the set of tags that collectively describe rows of the metric.
|
|
|
|
Keys []interface{}
|
|
|
|
// Buckets holds the inclusive upper bound of each bucket in the histogram.
|
|
|
|
Buckets []int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramFloat64 represents the construction information for an float64 histogram metric.
|
|
|
|
type HistogramFloat64 struct {
|
|
|
|
// Name is the unique name of this metric.
|
|
|
|
Name string
|
|
|
|
// Description can be used by observers to describe the metric to users.
|
|
|
|
Description string
|
|
|
|
// Keys is the set of tags that collectively describe rows of the metric.
|
|
|
|
Keys []interface{}
|
|
|
|
// Buckets holds the inclusive upper bound of each bucket in the histogram.
|
|
|
|
Buckets []float64
|
|
|
|
}
|
|
|
|
|
|
|
|
// CountInt64 creates a new metric based on the Scalar information that counts
|
|
|
|
// the number of times the supplied int64 measure is set.
|
|
|
|
// Metrics of this type will use Int64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) CountInt64(measure *stats.Int64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Int64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.countInt64)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// SumInt64 creates a new metric based on the Scalar information that sums all
|
|
|
|
// the values recorded on the int64 measure.
|
|
|
|
// Metrics of this type will use Int64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) SumInt64(measure *stats.Int64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Int64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.sum)
|
|
|
|
_ = data
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// LatestInt64 creates a new metric based on the Scalar information that tracks
|
|
|
|
// the most recent value recorded on the int64 measure.
|
|
|
|
// Metrics of this type will use Int64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) LatestInt64(measure *stats.Int64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Int64Data{Info: &info, IsGauge: true}
|
|
|
|
measure.Subscribe(data.latest)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// CountFloat64 creates a new metric based on the Scalar information that counts
|
|
|
|
// the number of times the supplied float64 measure is set.
|
|
|
|
// Metrics of this type will use Int64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) CountFloat64(measure *stats.Float64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Int64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.countFloat64)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// SumFloat64 creates a new metric based on the Scalar information that sums all
|
|
|
|
// the values recorded on the float64 measure.
|
|
|
|
// Metrics of this type will use Float64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) SumFloat64(measure *stats.Float64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Float64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.sum)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// LatestFloat64 creates a new metric based on the Scalar information that tracks
|
|
|
|
// the most recent value recorded on the float64 measure.
|
|
|
|
// Metrics of this type will use Float64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info Scalar) LatestFloat64(measure *stats.Float64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &Float64Data{Info: &info, IsGauge: true}
|
|
|
|
measure.Subscribe(data.latest)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Record creates a new metric based on the HistogramInt64 information that
|
|
|
|
// tracks the bucketized counts of values recorded on the int64 measure.
|
|
|
|
// Metrics of this type will use HistogramInt64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info HistogramInt64) Record(measure *stats.Int64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &HistogramInt64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.record)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Record creates a new metric based on the HistogramFloat64 information that
|
|
|
|
// tracks the bucketized counts of values recorded on the float64 measure.
|
|
|
|
// Metrics of this type will use HistogramFloat64Data.
|
2019-08-14 10:51:42 -06:00
|
|
|
func (info HistogramFloat64) Record(measure *stats.Float64Measure) string {
|
2019-07-08 19:22:37 -06:00
|
|
|
data := &HistogramFloat64Data{Info: &info}
|
|
|
|
measure.Subscribe(data.record)
|
2019-08-14 10:51:42 -06:00
|
|
|
return info.Name
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Int64Data is a concrete implementation of Data for int64 scalar metrics.
|
|
|
|
type Int64Data struct {
|
|
|
|
// Info holds the original consruction 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
|
2019-10-23 18:34:23 -06:00
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime *time.Time
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
groups []telemetry.TagList
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Float64Data is a concrete implementation of Data for float64 scalar metrics.
|
|
|
|
type Float64Data struct {
|
|
|
|
// Info holds the original consruction 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
|
2019-10-23 18:34:23 -06:00
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime *time.Time
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
groups []telemetry.TagList
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// HistogramInt64Data is a concrete implementation of Data for int64 histogram metrics.
|
|
|
|
type HistogramInt64Data struct {
|
|
|
|
// Info holds the original consruction information.
|
|
|
|
Info *HistogramInt64
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []*HistogramInt64Row
|
2019-10-23 18:34:23 -06:00
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime *time.Time
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
groups []telemetry.TagList
|
2019-07-08 19:22:37 -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 consruction information.
|
|
|
|
Info *HistogramFloat64
|
|
|
|
// Rows holds the per group values for the metric.
|
|
|
|
Rows []*HistogramFloat64Row
|
2019-10-23 18:34:23 -06:00
|
|
|
// End is the last time this metric was updated.
|
|
|
|
EndTime *time.Time
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
groups []telemetry.TagList
|
2019-07-08 19:22:37 -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
|
|
|
|
}
|
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
func getGroup(ctx context.Context, g *[]telemetry.TagList, keys []interface{}) (int, bool) {
|
2019-07-08 19:22:37 -06:00
|
|
|
group := tag.Get(ctx, keys...)
|
|
|
|
old := *g
|
|
|
|
index := sort.Search(len(old), func(i int) bool {
|
|
|
|
return !old[i].Less(group)
|
|
|
|
})
|
|
|
|
if index < len(old) && group.Equal(old[index]) {
|
|
|
|
// not a new group
|
|
|
|
return index, false
|
|
|
|
}
|
2019-08-14 10:51:42 -06:00
|
|
|
*g = make([]telemetry.TagList, len(old)+1)
|
2019-07-08 19:22:37 -06:00
|
|
|
copy(*g, old[:index])
|
|
|
|
copy((*g)[index+1:], old[index:])
|
|
|
|
(*g)[index] = group
|
|
|
|
return index, true
|
|
|
|
}
|
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
func (data *Int64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *Int64Data) Groups() []telemetry.TagList { return data.groups }
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-10-23 18:34:23 -06:00
|
|
|
func (data *Int64Data) modify(ctx context.Context, at time.Time, f func(v int64) int64) {
|
2019-08-17 21:26:28 -06:00
|
|
|
index, insert := getGroup(ctx, &data.groups, data.Info.Keys)
|
|
|
|
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])
|
2019-10-23 18:34:23 -06:00
|
|
|
data.EndTime = &at
|
2019-08-17 21:26:28 -06:00
|
|
|
frozen := *data
|
|
|
|
export.Metric(ctx, &frozen)
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Int64Data) countInt64(ctx context.Context, measure *stats.Int64Measure, value int64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v int64) int64 { return v + 1 })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Int64Data) countFloat64(ctx context.Context, measure *stats.Float64Measure, value float64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v int64) int64 { return v + 1 })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Int64Data) sum(ctx context.Context, measure *stats.Int64Measure, value int64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v int64) int64 { return v + value })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Int64Data) latest(ctx context.Context, measure *stats.Int64Measure, value int64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v int64) int64 { return value })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
func (data *Float64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *Float64Data) Groups() []telemetry.TagList { return data.groups }
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-10-23 18:34:23 -06:00
|
|
|
func (data *Float64Data) modify(ctx context.Context, at time.Time, f func(v float64) float64) {
|
2019-08-17 21:26:28 -06:00
|
|
|
index, insert := getGroup(ctx, &data.groups, data.Info.Keys)
|
|
|
|
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])
|
2019-10-23 18:34:23 -06:00
|
|
|
data.EndTime = &at
|
2019-08-17 21:26:28 -06:00
|
|
|
frozen := *data
|
|
|
|
export.Metric(ctx, &frozen)
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Float64Data) sum(ctx context.Context, measure *stats.Float64Measure, value float64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v float64) float64 { return v + value })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *Float64Data) latest(ctx context.Context, measure *stats.Float64Measure, value float64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v float64) float64 { return value })
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
func (data *HistogramInt64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *HistogramInt64Data) Groups() []telemetry.TagList { return data.groups }
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-10-23 18:34:23 -06:00
|
|
|
func (data *HistogramInt64Data) modify(ctx context.Context, at time.Time, f func(v *HistogramInt64Row)) {
|
2019-08-17 21:26:28 -06:00
|
|
|
index, insert := getGroup(ctx, &data.groups, data.Info.Keys)
|
|
|
|
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
|
2019-10-23 18:34:23 -06:00
|
|
|
data.EndTime = &at
|
2019-08-17 21:26:28 -06:00
|
|
|
frozen := *data
|
|
|
|
export.Metric(ctx, &frozen)
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *HistogramInt64Data) record(ctx context.Context, measure *stats.Int64Measure, value int64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v *HistogramInt64Row) {
|
2019-07-08 19:22:37 -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]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-08-14 10:51:42 -06:00
|
|
|
func (data *HistogramFloat64Data) Handle() string { return data.Info.Name }
|
|
|
|
func (data *HistogramFloat64Data) Groups() []telemetry.TagList { return data.groups }
|
2019-07-08 19:22:37 -06:00
|
|
|
|
2019-10-23 18:34:23 -06:00
|
|
|
func (data *HistogramFloat64Data) modify(ctx context.Context, at time.Time, f func(v *HistogramFloat64Row)) {
|
2019-08-17 21:26:28 -06:00
|
|
|
index, insert := getGroup(ctx, &data.groups, data.Info.Keys)
|
|
|
|
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
|
2019-10-23 18:34:23 -06:00
|
|
|
data.EndTime = &at
|
2019-08-17 21:26:28 -06:00
|
|
|
frozen := *data
|
|
|
|
export.Metric(ctx, &frozen)
|
2019-07-08 19:22:37 -06:00
|
|
|
}
|
|
|
|
|
2019-10-06 17:49:04 -06:00
|
|
|
func (data *HistogramFloat64Data) record(ctx context.Context, measure *stats.Float64Measure, value float64, at time.Time) {
|
2019-10-23 18:34:23 -06:00
|
|
|
data.modify(ctx, at, func(v *HistogramFloat64Row) {
|
2019-07-08 19:22:37 -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]++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|