1// Copyright The OpenTelemetry Authors 2// 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 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 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. 14 15package minmaxsumcount // import "go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount" 16 17import ( 18 "context" 19 "sync" 20 21 "go.opentelemetry.io/otel/metric" 22 "go.opentelemetry.io/otel/metric/number" 23 export "go.opentelemetry.io/otel/sdk/export/metric" 24 "go.opentelemetry.io/otel/sdk/export/metric/aggregation" 25 "go.opentelemetry.io/otel/sdk/metric/aggregator" 26) 27 28type ( 29 // Aggregator aggregates events that form a distribution, 30 // keeping only the min, max, sum, and count. 31 Aggregator struct { 32 lock sync.Mutex 33 kind number.Kind 34 state 35 } 36 37 state struct { 38 sum number.Number 39 min number.Number 40 max number.Number 41 count uint64 42 } 43) 44 45var _ export.Aggregator = &Aggregator{} 46var _ aggregation.MinMaxSumCount = &Aggregator{} 47 48// New returns a new aggregator for computing the min, max, sum, and 49// count. 50// 51// This type uses a mutex for Update() and SynchronizedMove() concurrency. 52func New(cnt int, desc *metric.Descriptor) []Aggregator { 53 kind := desc.NumberKind() 54 aggs := make([]Aggregator, cnt) 55 for i := range aggs { 56 aggs[i] = Aggregator{ 57 kind: kind, 58 state: emptyState(kind), 59 } 60 } 61 return aggs 62} 63 64// Aggregation returns an interface for reading the state of this aggregator. 65func (c *Aggregator) Aggregation() aggregation.Aggregation { 66 return c 67} 68 69// Kind returns aggregation.MinMaxSumCountKind. 70func (c *Aggregator) Kind() aggregation.Kind { 71 return aggregation.MinMaxSumCountKind 72} 73 74// Sum returns the sum of values in the checkpoint. 75func (c *Aggregator) Sum() (number.Number, error) { 76 return c.sum, nil 77} 78 79// Count returns the number of values in the checkpoint. 80func (c *Aggregator) Count() (uint64, error) { 81 return c.count, nil 82} 83 84// Min returns the minimum value in the checkpoint. 85// The error value aggregation.ErrNoData will be returned 86// if there were no measurements recorded during the checkpoint. 87func (c *Aggregator) Min() (number.Number, error) { 88 if c.count == 0 { 89 return 0, aggregation.ErrNoData 90 } 91 return c.min, nil 92} 93 94// Max returns the maximum value in the checkpoint. 95// The error value aggregation.ErrNoData will be returned 96// if there were no measurements recorded during the checkpoint. 97func (c *Aggregator) Max() (number.Number, error) { 98 if c.count == 0 { 99 return 0, aggregation.ErrNoData 100 } 101 return c.max, nil 102} 103 104// SynchronizedMove saves the current state into oa and resets the current state to 105// the empty set. 106func (c *Aggregator) SynchronizedMove(oa export.Aggregator, desc *metric.Descriptor) error { 107 o, _ := oa.(*Aggregator) 108 109 if oa != nil && o == nil { 110 return aggregator.NewInconsistentAggregatorError(c, oa) 111 } 112 c.lock.Lock() 113 if o != nil { 114 o.state = c.state 115 } 116 c.state = emptyState(c.kind) 117 c.lock.Unlock() 118 119 return nil 120} 121 122func emptyState(kind number.Kind) state { 123 return state{ 124 count: 0, 125 sum: 0, 126 min: kind.Maximum(), 127 max: kind.Minimum(), 128 } 129} 130 131// Update adds the recorded measurement to the current data set. 132func (c *Aggregator) Update(_ context.Context, number number.Number, desc *metric.Descriptor) error { 133 kind := desc.NumberKind() 134 135 c.lock.Lock() 136 defer c.lock.Unlock() 137 c.count++ 138 c.sum.AddNumber(kind, number) 139 if number.CompareNumber(kind, c.min) < 0 { 140 c.min = number 141 } 142 if number.CompareNumber(kind, c.max) > 0 { 143 c.max = number 144 } 145 return nil 146} 147 148// Merge combines two data sets into one. 149func (c *Aggregator) Merge(oa export.Aggregator, desc *metric.Descriptor) error { 150 o, _ := oa.(*Aggregator) 151 if o == nil { 152 return aggregator.NewInconsistentAggregatorError(c, oa) 153 } 154 155 c.count += o.count 156 c.sum.AddNumber(desc.NumberKind(), o.sum) 157 158 if c.min.CompareNumber(desc.NumberKind(), o.min) > 0 { 159 c.min.SetNumber(o.min) 160 } 161 if c.max.CompareNumber(desc.NumberKind(), o.max) < 0 { 162 c.max.SetNumber(o.max) 163 } 164 return nil 165} 166