view.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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/metric/metricdata"
  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 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 to 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. metricDescriptor *metricdata.Descriptor
  110. }
  111. func newViewInternal(v *View) (*viewInternal, error) {
  112. return &viewInternal{
  113. view: v,
  114. collector: &collector{make(map[string]AggregationData), v.Aggregation},
  115. metricDescriptor: viewToMetricDescriptor(v),
  116. }, nil
  117. }
  118. func (v *viewInternal) subscribe() {
  119. atomic.StoreUint32(&v.subscribed, 1)
  120. }
  121. func (v *viewInternal) unsubscribe() {
  122. atomic.StoreUint32(&v.subscribed, 0)
  123. }
  124. // isSubscribed returns true if the view is exporting
  125. // data by subscription.
  126. func (v *viewInternal) isSubscribed() bool {
  127. return atomic.LoadUint32(&v.subscribed) == 1
  128. }
  129. func (v *viewInternal) clearRows() {
  130. v.collector.clearRows()
  131. }
  132. func (v *viewInternal) collectedRows() []*Row {
  133. return v.collector.collectedRows(v.view.TagKeys)
  134. }
  135. func (v *viewInternal) addSample(m *tag.Map, val float64, attachments map[string]interface{}, t time.Time) {
  136. if !v.isSubscribed() {
  137. return
  138. }
  139. sig := string(encodeWithKeys(m, v.view.TagKeys))
  140. v.collector.addSample(sig, val, attachments, t)
  141. }
  142. // A Data is a set of rows about usage of the single measure associated
  143. // with the given view. Each row is specific to a unique set of tags.
  144. type Data struct {
  145. View *View
  146. Start, End time.Time
  147. Rows []*Row
  148. }
  149. // Row is the collected value for a specific set of key value pairs a.k.a tags.
  150. type Row struct {
  151. Tags []tag.Tag
  152. Data AggregationData
  153. }
  154. func (r *Row) String() string {
  155. var buffer bytes.Buffer
  156. buffer.WriteString("{ ")
  157. buffer.WriteString("{ ")
  158. for _, t := range r.Tags {
  159. buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Value))
  160. }
  161. buffer.WriteString(" }")
  162. buffer.WriteString(fmt.Sprintf("%v", r.Data))
  163. buffer.WriteString(" }")
  164. return buffer.String()
  165. }
  166. // Equal returns true if both rows are equal. Tags are expected to be ordered
  167. // by the key name. Even if both rows have the same tags but the tags appear in
  168. // different orders it will return false.
  169. func (r *Row) Equal(other *Row) bool {
  170. if r == other {
  171. return true
  172. }
  173. return reflect.DeepEqual(r.Tags, other.Tags) && r.Data.equal(other.Data)
  174. }
  175. const maxNameLength = 255
  176. // Returns true if the given string contains only printable characters.
  177. func isPrintable(str string) bool {
  178. for _, r := range str {
  179. if !(r >= ' ' && r <= '~') {
  180. return false
  181. }
  182. }
  183. return true
  184. }
  185. func checkViewName(name string) error {
  186. if len(name) > maxNameLength {
  187. return fmt.Errorf("view name cannot be larger than %v", maxNameLength)
  188. }
  189. if !isPrintable(name) {
  190. return fmt.Errorf("view name needs to be an ASCII string")
  191. }
  192. return nil
  193. }