| // 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 ( |
| "fmt" |
| "sort" |
| "time" |
| |
| "golang.org/x/tools/internal/event/keys" |
| "golang.org/x/tools/internal/event/label" |
| ) |
| |
| // 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. |
| Groups() [][]label.Label |
| } |
| |
| // 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 |
| |
| groups [][]label.Label |
| key *keys.Int64 |
| } |
| |
| // 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 |
| |
| groups [][]label.Label |
| key *keys.Float64 |
| } |
| |
| // 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 |
| |
| groups [][]label.Label |
| key *keys.Int64 |
| } |
| |
| // 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 |
| |
| groups [][]label.Label |
| key *keys.Float64 |
| } |
| |
| // 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 |
| } |
| |
| func labelListEqual(a, b []label.Label) bool { |
| //TODO: make this more efficient |
| return fmt.Sprint(a) == fmt.Sprint(b) |
| } |
| |
| func labelListLess(a, b []label.Label) bool { |
| //TODO: make this more efficient |
| return fmt.Sprint(a) < fmt.Sprint(b) |
| } |
| |
| func getGroup(lm label.Map, g *[][]label.Label, keys []label.Key) (int, bool) { |
| group := make([]label.Label, len(keys)) |
| for i, key := range keys { |
| l := lm.Find(key) |
| if l.Valid() { |
| group[i] = l |
| } |
| } |
| old := *g |
| index := sort.Search(len(old), func(i int) bool { |
| return !labelListLess(old[i], group) |
| }) |
| if index < len(old) && labelListEqual(group, old[index]) { |
| // not a new group |
| return index, false |
| } |
| *g = make([][]label.Label, len(old)+1) |
| copy(*g, old[:index]) |
| copy((*g)[index+1:], old[index:]) |
| (*g)[index] = group |
| return index, true |
| } |
| |
| func (data *Int64Data) Handle() string { return data.Info.Name } |
| func (data *Int64Data) Groups() [][]label.Label { return data.groups } |
| |
| func (data *Int64Data) modify(at time.Time, lm label.Map, f func(v int64) int64) Data { |
| index, insert := getGroup(lm, &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]) |
| data.EndTime = at |
| frozen := *data |
| return &frozen |
| } |
| |
| func (data *Int64Data) count(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v int64) int64 { |
| return v + 1 |
| }) |
| } |
| |
| func (data *Int64Data) sum(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v int64) int64 { |
| return v + data.key.From(l) |
| }) |
| } |
| |
| func (data *Int64Data) latest(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v int64) int64 { |
| return data.key.From(l) |
| }) |
| } |
| |
| func (data *Float64Data) Handle() string { return data.Info.Name } |
| func (data *Float64Data) Groups() [][]label.Label { return data.groups } |
| |
| func (data *Float64Data) modify(at time.Time, lm label.Map, f func(v float64) float64) Data { |
| index, insert := getGroup(lm, &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]) |
| data.EndTime = at |
| frozen := *data |
| return &frozen |
| } |
| |
| func (data *Float64Data) sum(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v float64) float64 { |
| return v + data.key.From(l) |
| }) |
| } |
| |
| func (data *Float64Data) latest(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v float64) float64 { |
| return data.key.From(l) |
| }) |
| } |
| |
| func (data *HistogramInt64Data) Handle() string { return data.Info.Name } |
| func (data *HistogramInt64Data) Groups() [][]label.Label { return data.groups } |
| |
| func (data *HistogramInt64Data) modify(at time.Time, lm label.Map, f func(v *HistogramInt64Row)) Data { |
| index, insert := getGroup(lm, &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 |
| data.EndTime = at |
| frozen := *data |
| return &frozen |
| } |
| |
| func (data *HistogramInt64Data) record(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v *HistogramInt64Row) { |
| value := data.key.From(l) |
| 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]++ |
| } |
| } |
| }) |
| } |
| |
| func (data *HistogramFloat64Data) Handle() string { return data.Info.Name } |
| func (data *HistogramFloat64Data) Groups() [][]label.Label { return data.groups } |
| |
| func (data *HistogramFloat64Data) modify(at time.Time, lm label.Map, f func(v *HistogramFloat64Row)) Data { |
| index, insert := getGroup(lm, &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 |
| data.EndTime = at |
| frozen := *data |
| return &frozen |
| } |
| |
| func (data *HistogramFloat64Data) record(at time.Time, lm label.Map, l label.Label) Data { |
| return data.modify(at, lm, func(v *HistogramFloat64Row) { |
| value := data.key.From(l) |
| 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]++ |
| } |
| } |
| }) |
| } |