1 // Copyright 2017, OpenCensus Authors
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
22 "go.opencensus.io/metric/metricdata"
25 // AggregationData represents an aggregated value from a collection.
26 // They are reported on the view data during exporting.
27 // Mosts users won't directly access aggregration data.
28 type AggregationData interface {
29 isAggregationData() bool
30 addSample(v float64, attachments map[string]interface{}, t time.Time)
31 clone() AggregationData
32 equal(other AggregationData) bool
33 toPoint(t metricdata.Type, time time.Time) metricdata.Point
38 // CountData is the aggregated data for the Count aggregation.
39 // A count aggregation processes data and counts the recordings.
41 // Most users won't directly access count data.
42 type CountData struct {
46 func (a *CountData) isAggregationData() bool { return true }
48 func (a *CountData) addSample(_ float64, _ map[string]interface{}, _ time.Time) {
52 func (a *CountData) clone() AggregationData {
53 return &CountData{Value: a.Value}
56 func (a *CountData) equal(other AggregationData) bool {
57 a2, ok := other.(*CountData)
62 return a.Value == a2.Value
65 func (a *CountData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
67 case metricdata.TypeCumulativeInt64:
68 return metricdata.NewInt64Point(t, a.Value)
70 panic("unsupported metricdata.Type")
74 // SumData is the aggregated data for the Sum aggregation.
75 // A sum aggregation processes data and sums up the recordings.
77 // Most users won't directly access sum data.
82 func (a *SumData) isAggregationData() bool { return true }
84 func (a *SumData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
88 func (a *SumData) clone() AggregationData {
89 return &SumData{Value: a.Value}
92 func (a *SumData) equal(other AggregationData) bool {
93 a2, ok := other.(*SumData)
97 return math.Pow(a.Value-a2.Value, 2) < epsilon
100 func (a *SumData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
102 case metricdata.TypeCumulativeInt64:
103 return metricdata.NewInt64Point(t, int64(a.Value))
104 case metricdata.TypeCumulativeFloat64:
105 return metricdata.NewFloat64Point(t, a.Value)
107 panic("unsupported metricdata.Type")
111 // DistributionData is the aggregated data for the
112 // Distribution aggregation.
114 // Most users won't directly access distribution data.
116 // For a distribution with N bounds, the associated DistributionData will have
118 type DistributionData struct {
119 Count int64 // number of data points aggregated
120 Min float64 // minimum value in the distribution
121 Max float64 // max value in the distribution
122 Mean float64 // mean of the distribution
123 SumOfSquaredDev float64 // sum of the squared deviation from the mean
124 CountPerBucket []int64 // number of occurrences per bucket
125 // ExemplarsPerBucket is slice the same length as CountPerBucket containing
126 // an exemplar for the associated bucket, or nil.
127 ExemplarsPerBucket []*metricdata.Exemplar
128 bounds []float64 // histogram distribution of the values
131 func newDistributionData(bounds []float64) *DistributionData {
132 bucketCount := len(bounds) + 1
133 return &DistributionData{
134 CountPerBucket: make([]int64, bucketCount),
135 ExemplarsPerBucket: make([]*metricdata.Exemplar, bucketCount),
137 Min: math.MaxFloat64,
138 Max: math.SmallestNonzeroFloat64,
142 // Sum returns the sum of all samples collected.
143 func (a *DistributionData) Sum() float64 { return a.Mean * float64(a.Count) }
145 func (a *DistributionData) variance() float64 {
149 return a.SumOfSquaredDev / float64(a.Count-1)
152 func (a *DistributionData) isAggregationData() bool { return true }
154 // TODO(songy23): support exemplar attachments.
155 func (a *DistributionData) addSample(v float64, attachments map[string]interface{}, t time.Time) {
163 a.addToBucket(v, attachments, t)
171 a.Mean = a.Mean + (v-a.Mean)/float64(a.Count)
172 a.SumOfSquaredDev = a.SumOfSquaredDev + (v-oldMean)*(v-a.Mean)
175 func (a *DistributionData) addToBucket(v float64, attachments map[string]interface{}, t time.Time) {
179 for i, b = range a.bounds {
181 count = &a.CountPerBucket[i]
185 if count == nil { // Last bucket.
187 count = &a.CountPerBucket[i]
190 if exemplar := getExemplar(v, attachments, t); exemplar != nil {
191 a.ExemplarsPerBucket[i] = exemplar
195 func getExemplar(v float64, attachments map[string]interface{}, t time.Time) *metricdata.Exemplar {
196 if len(attachments) == 0 {
199 return &metricdata.Exemplar{
202 Attachments: attachments,
206 func (a *DistributionData) clone() AggregationData {
208 c.CountPerBucket = append([]int64(nil), a.CountPerBucket...)
209 c.ExemplarsPerBucket = append([]*metricdata.Exemplar(nil), a.ExemplarsPerBucket...)
213 func (a *DistributionData) equal(other AggregationData) bool {
214 a2, ok := other.(*DistributionData)
221 if len(a.CountPerBucket) != len(a2.CountPerBucket) {
224 for i := range a.CountPerBucket {
225 if a.CountPerBucket[i] != a2.CountPerBucket[i] {
229 return a.Count == a2.Count && a.Min == a2.Min && a.Max == a2.Max && math.Pow(a.Mean-a2.Mean, 2) < epsilon && math.Pow(a.variance()-a2.variance(), 2) < epsilon
232 func (a *DistributionData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
234 case metricdata.TypeCumulativeDistribution:
235 buckets := []metricdata.Bucket{}
236 for i := 0; i < len(a.CountPerBucket); i++ {
237 buckets = append(buckets, metricdata.Bucket{
238 Count: a.CountPerBucket[i],
239 Exemplar: a.ExemplarsPerBucket[i],
242 bucketOptions := &metricdata.BucketOptions{Bounds: a.bounds}
244 val := &metricdata.Distribution{
247 SumOfSquaredDeviation: a.SumOfSquaredDev,
248 BucketOptions: bucketOptions,
251 return metricdata.NewDistributionPoint(t, val)
254 // TODO: [rghetia] when we have a use case for TypeGaugeDistribution.
255 panic("unsupported metricdata.Type")
259 // LastValueData returns the last value recorded for LastValue aggregation.
260 type LastValueData struct {
264 func (l *LastValueData) isAggregationData() bool {
268 func (l *LastValueData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
272 func (l *LastValueData) clone() AggregationData {
273 return &LastValueData{l.Value}
276 func (l *LastValueData) equal(other AggregationData) bool {
277 a2, ok := other.(*LastValueData)
281 return l.Value == a2.Value
284 func (l *LastValueData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
286 case metricdata.TypeGaugeInt64:
287 return metricdata.NewInt64Point(t, int64(l.Value))
288 case metricdata.TypeGaugeFloat64:
289 return metricdata.NewFloat64Point(t, l.Value)
291 panic("unsupported metricdata.Type")