Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

236 linhas
6.0 KiB

  1. // Copyright 2017, OpenCensus 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. //
  15. package view
  16. import (
  17. "math"
  18. "go.opencensus.io/exemplar"
  19. )
  20. // AggregationData represents an aggregated value from a collection.
  21. // They are reported on the view data during exporting.
  22. // Mosts users won't directly access aggregration data.
  23. type AggregationData interface {
  24. isAggregationData() bool
  25. addSample(e *exemplar.Exemplar)
  26. clone() AggregationData
  27. equal(other AggregationData) bool
  28. }
  29. const epsilon = 1e-9
  30. // CountData is the aggregated data for the Count aggregation.
  31. // A count aggregation processes data and counts the recordings.
  32. //
  33. // Most users won't directly access count data.
  34. type CountData struct {
  35. Value int64
  36. }
  37. func (a *CountData) isAggregationData() bool { return true }
  38. func (a *CountData) addSample(_ *exemplar.Exemplar) {
  39. a.Value = a.Value + 1
  40. }
  41. func (a *CountData) clone() AggregationData {
  42. return &CountData{Value: a.Value}
  43. }
  44. func (a *CountData) equal(other AggregationData) bool {
  45. a2, ok := other.(*CountData)
  46. if !ok {
  47. return false
  48. }
  49. return a.Value == a2.Value
  50. }
  51. // SumData is the aggregated data for the Sum aggregation.
  52. // A sum aggregation processes data and sums up the recordings.
  53. //
  54. // Most users won't directly access sum data.
  55. type SumData struct {
  56. Value float64
  57. }
  58. func (a *SumData) isAggregationData() bool { return true }
  59. func (a *SumData) addSample(e *exemplar.Exemplar) {
  60. a.Value += e.Value
  61. }
  62. func (a *SumData) clone() AggregationData {
  63. return &SumData{Value: a.Value}
  64. }
  65. func (a *SumData) equal(other AggregationData) bool {
  66. a2, ok := other.(*SumData)
  67. if !ok {
  68. return false
  69. }
  70. return math.Pow(a.Value-a2.Value, 2) < epsilon
  71. }
  72. // DistributionData is the aggregated data for the
  73. // Distribution aggregation.
  74. //
  75. // Most users won't directly access distribution data.
  76. //
  77. // For a distribution with N bounds, the associated DistributionData will have
  78. // N+1 buckets.
  79. type DistributionData struct {
  80. Count int64 // number of data points aggregated
  81. Min float64 // minimum value in the distribution
  82. Max float64 // max value in the distribution
  83. Mean float64 // mean of the distribution
  84. SumOfSquaredDev float64 // sum of the squared deviation from the mean
  85. CountPerBucket []int64 // number of occurrences per bucket
  86. // ExemplarsPerBucket is slice the same length as CountPerBucket containing
  87. // an exemplar for the associated bucket, or nil.
  88. ExemplarsPerBucket []*exemplar.Exemplar
  89. bounds []float64 // histogram distribution of the values
  90. }
  91. func newDistributionData(bounds []float64) *DistributionData {
  92. bucketCount := len(bounds) + 1
  93. return &DistributionData{
  94. CountPerBucket: make([]int64, bucketCount),
  95. ExemplarsPerBucket: make([]*exemplar.Exemplar, bucketCount),
  96. bounds: bounds,
  97. Min: math.MaxFloat64,
  98. Max: math.SmallestNonzeroFloat64,
  99. }
  100. }
  101. // Sum returns the sum of all samples collected.
  102. func (a *DistributionData) Sum() float64 { return a.Mean * float64(a.Count) }
  103. func (a *DistributionData) variance() float64 {
  104. if a.Count <= 1 {
  105. return 0
  106. }
  107. return a.SumOfSquaredDev / float64(a.Count-1)
  108. }
  109. func (a *DistributionData) isAggregationData() bool { return true }
  110. func (a *DistributionData) addSample(e *exemplar.Exemplar) {
  111. f := e.Value
  112. if f < a.Min {
  113. a.Min = f
  114. }
  115. if f > a.Max {
  116. a.Max = f
  117. }
  118. a.Count++
  119. a.addToBucket(e)
  120. if a.Count == 1 {
  121. a.Mean = f
  122. return
  123. }
  124. oldMean := a.Mean
  125. a.Mean = a.Mean + (f-a.Mean)/float64(a.Count)
  126. a.SumOfSquaredDev = a.SumOfSquaredDev + (f-oldMean)*(f-a.Mean)
  127. }
  128. func (a *DistributionData) addToBucket(e *exemplar.Exemplar) {
  129. var count *int64
  130. var ex **exemplar.Exemplar
  131. for i, b := range a.bounds {
  132. if e.Value < b {
  133. count = &a.CountPerBucket[i]
  134. ex = &a.ExemplarsPerBucket[i]
  135. break
  136. }
  137. }
  138. if count == nil {
  139. count = &a.CountPerBucket[len(a.bounds)]
  140. ex = &a.ExemplarsPerBucket[len(a.bounds)]
  141. }
  142. *count++
  143. *ex = maybeRetainExemplar(*ex, e)
  144. }
  145. func maybeRetainExemplar(old, cur *exemplar.Exemplar) *exemplar.Exemplar {
  146. if old == nil {
  147. return cur
  148. }
  149. // Heuristic to pick the "better" exemplar: first keep the one with a
  150. // sampled trace attachment, if neither have a trace attachment, pick the
  151. // one with more attachments.
  152. _, haveTraceID := cur.Attachments[exemplar.KeyTraceID]
  153. if haveTraceID || len(cur.Attachments) >= len(old.Attachments) {
  154. return cur
  155. }
  156. return old
  157. }
  158. func (a *DistributionData) clone() AggregationData {
  159. c := *a
  160. c.CountPerBucket = append([]int64(nil), a.CountPerBucket...)
  161. c.ExemplarsPerBucket = append([]*exemplar.Exemplar(nil), a.ExemplarsPerBucket...)
  162. return &c
  163. }
  164. func (a *DistributionData) equal(other AggregationData) bool {
  165. a2, ok := other.(*DistributionData)
  166. if !ok {
  167. return false
  168. }
  169. if a2 == nil {
  170. return false
  171. }
  172. if len(a.CountPerBucket) != len(a2.CountPerBucket) {
  173. return false
  174. }
  175. for i := range a.CountPerBucket {
  176. if a.CountPerBucket[i] != a2.CountPerBucket[i] {
  177. return false
  178. }
  179. }
  180. 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
  181. }
  182. // LastValueData returns the last value recorded for LastValue aggregation.
  183. type LastValueData struct {
  184. Value float64
  185. }
  186. func (l *LastValueData) isAggregationData() bool {
  187. return true
  188. }
  189. func (l *LastValueData) addSample(e *exemplar.Exemplar) {
  190. l.Value = e.Value
  191. }
  192. func (l *LastValueData) clone() AggregationData {
  193. return &LastValueData{l.Value}
  194. }
  195. func (l *LastValueData) equal(other AggregationData) bool {
  196. a2, ok := other.(*LastValueData)
  197. if !ok {
  198. return false
  199. }
  200. return l.Value == a2.Value
  201. }