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.
 
 
 

221 linhas
5.9 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. "bytes"
  18. "errors"
  19. "fmt"
  20. "reflect"
  21. "sort"
  22. "sync/atomic"
  23. "time"
  24. "go.opencensus.io/exemplar"
  25. "go.opencensus.io/stats"
  26. "go.opencensus.io/tag"
  27. )
  28. // View allows users to aggregate the recorded stats.Measurements.
  29. // Views need to be passed to the Register function to be before data will be
  30. // collected and sent to Exporters.
  31. type View struct {
  32. Name string // Name of View. Must be unique. If unset, will default to the name of the Measure.
  33. Description string // Description is a human-readable description for this view.
  34. // TagKeys are the tag keys describing the grouping of this view.
  35. // A single Row will be produced for each combination of associated tag values.
  36. TagKeys []tag.Key
  37. // Measure is a stats.Measure to aggregate in this view.
  38. Measure stats.Measure
  39. // Aggregation is the aggregation function tp apply to the set of Measurements.
  40. Aggregation *Aggregation
  41. }
  42. // WithName returns a copy of the View with a new name. This is useful for
  43. // renaming views to cope with limitations placed on metric names by various
  44. // backends.
  45. func (v *View) WithName(name string) *View {
  46. vNew := *v
  47. vNew.Name = name
  48. return &vNew
  49. }
  50. // same compares two views and returns true if they represent the same aggregation.
  51. func (v *View) same(other *View) bool {
  52. if v == other {
  53. return true
  54. }
  55. if v == nil {
  56. return false
  57. }
  58. return reflect.DeepEqual(v.Aggregation, other.Aggregation) &&
  59. v.Measure.Name() == other.Measure.Name()
  60. }
  61. // ErrNegativeBucketBounds error returned if histogram contains negative bounds.
  62. //
  63. // Deprecated: this should not be public.
  64. var ErrNegativeBucketBounds = errors.New("negative bucket bounds not supported")
  65. // canonicalize canonicalizes v by setting explicit
  66. // defaults for Name and Description and sorting the TagKeys
  67. func (v *View) canonicalize() error {
  68. if v.Measure == nil {
  69. return fmt.Errorf("cannot register view %q: measure not set", v.Name)
  70. }
  71. if v.Aggregation == nil {
  72. return fmt.Errorf("cannot register view %q: aggregation not set", v.Name)
  73. }
  74. if v.Name == "" {
  75. v.Name = v.Measure.Name()
  76. }
  77. if v.Description == "" {
  78. v.Description = v.Measure.Description()
  79. }
  80. if err := checkViewName(v.Name); err != nil {
  81. return err
  82. }
  83. sort.Slice(v.TagKeys, func(i, j int) bool {
  84. return v.TagKeys[i].Name() < v.TagKeys[j].Name()
  85. })
  86. sort.Float64s(v.Aggregation.Buckets)
  87. for _, b := range v.Aggregation.Buckets {
  88. if b < 0 {
  89. return ErrNegativeBucketBounds
  90. }
  91. }
  92. // drop 0 bucket silently.
  93. v.Aggregation.Buckets = dropZeroBounds(v.Aggregation.Buckets...)
  94. return nil
  95. }
  96. func dropZeroBounds(bounds ...float64) []float64 {
  97. for i, bound := range bounds {
  98. if bound > 0 {
  99. return bounds[i:]
  100. }
  101. }
  102. return []float64{}
  103. }
  104. // viewInternal is the internal representation of a View.
  105. type viewInternal struct {
  106. view *View // view is the canonicalized View definition associated with this view.
  107. subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access
  108. collector *collector
  109. }
  110. func newViewInternal(v *View) (*viewInternal, error) {
  111. return &viewInternal{
  112. view: v,
  113. collector: &collector{make(map[string]AggregationData), v.Aggregation},
  114. }, nil
  115. }
  116. func (v *viewInternal) subscribe() {
  117. atomic.StoreUint32(&v.subscribed, 1)
  118. }
  119. func (v *viewInternal) unsubscribe() {
  120. atomic.StoreUint32(&v.subscribed, 0)
  121. }
  122. // isSubscribed returns true if the view is exporting
  123. // data by subscription.
  124. func (v *viewInternal) isSubscribed() bool {
  125. return atomic.LoadUint32(&v.subscribed) == 1
  126. }
  127. func (v *viewInternal) clearRows() {
  128. v.collector.clearRows()
  129. }
  130. func (v *viewInternal) collectedRows() []*Row {
  131. return v.collector.collectedRows(v.view.TagKeys)
  132. }
  133. func (v *viewInternal) addSample(m *tag.Map, e *exemplar.Exemplar) {
  134. if !v.isSubscribed() {
  135. return
  136. }
  137. sig := string(encodeWithKeys(m, v.view.TagKeys))
  138. v.collector.addSample(sig, e)
  139. }
  140. // A Data is a set of rows about usage of the single measure associated
  141. // with the given view. Each row is specific to a unique set of tags.
  142. type Data struct {
  143. View *View
  144. Start, End time.Time
  145. Rows []*Row
  146. }
  147. // Row is the collected value for a specific set of key value pairs a.k.a tags.
  148. type Row struct {
  149. Tags []tag.Tag
  150. Data AggregationData
  151. }
  152. func (r *Row) String() string {
  153. var buffer bytes.Buffer
  154. buffer.WriteString("{ ")
  155. buffer.WriteString("{ ")
  156. for _, t := range r.Tags {
  157. buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Value))
  158. }
  159. buffer.WriteString(" }")
  160. buffer.WriteString(fmt.Sprintf("%v", r.Data))
  161. buffer.WriteString(" }")
  162. return buffer.String()
  163. }
  164. // Equal returns true if both rows are equal. Tags are expected to be ordered
  165. // by the key name. Even both rows have the same tags but the tags appear in
  166. // different orders it will return false.
  167. func (r *Row) Equal(other *Row) bool {
  168. if r == other {
  169. return true
  170. }
  171. return reflect.DeepEqual(r.Tags, other.Tags) && r.Data.equal(other.Data)
  172. }
  173. const maxNameLength = 255
  174. // Returns true if the given string contains only printable characters.
  175. func isPrintable(str string) bool {
  176. for _, r := range str {
  177. if !(r >= ' ' && r <= '~') {
  178. return false
  179. }
  180. }
  181. return true
  182. }
  183. func checkViewName(name string) error {
  184. if len(name) > maxNameLength {
  185. return fmt.Errorf("view name cannot be larger than %v", maxNameLength)
  186. }
  187. if !isPrintable(name) {
  188. return fmt.Errorf("view name needs to be an ASCII string")
  189. }
  190. return nil
  191. }