record.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2018, 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 stats
  16. import (
  17. "context"
  18. "go.opencensus.io/metric/metricdata"
  19. "go.opencensus.io/stats/internal"
  20. "go.opencensus.io/tag"
  21. )
  22. func init() {
  23. internal.SubscriptionReporter = func(measure string) {
  24. mu.Lock()
  25. measures[measure].subscribe()
  26. mu.Unlock()
  27. }
  28. }
  29. // Recorder provides an interface for exporting measurement information from
  30. // the static Record method by using the WithRecorder option.
  31. type Recorder interface {
  32. // Record records a set of measurements associated with the given tags and attachments.
  33. // The second argument is a `[]Measurement`.
  34. Record(*tag.Map, interface{}, map[string]interface{})
  35. }
  36. type recordOptions struct {
  37. attachments metricdata.Attachments
  38. mutators []tag.Mutator
  39. measurements []Measurement
  40. recorder Recorder
  41. }
  42. // WithAttachments applies provided exemplar attachments.
  43. func WithAttachments(attachments metricdata.Attachments) Options {
  44. return func(ro *recordOptions) {
  45. ro.attachments = attachments
  46. }
  47. }
  48. // WithTags applies provided tag mutators.
  49. func WithTags(mutators ...tag.Mutator) Options {
  50. return func(ro *recordOptions) {
  51. ro.mutators = mutators
  52. }
  53. }
  54. // WithMeasurements applies provided measurements.
  55. func WithMeasurements(measurements ...Measurement) Options {
  56. return func(ro *recordOptions) {
  57. ro.measurements = measurements
  58. }
  59. }
  60. // WithRecorder records the measurements to the specified `Recorder`, rather
  61. // than to the global metrics recorder.
  62. func WithRecorder(meter Recorder) Options {
  63. return func(ro *recordOptions) {
  64. ro.recorder = meter
  65. }
  66. }
  67. // Options apply changes to recordOptions.
  68. type Options func(*recordOptions)
  69. func createRecordOption(ros ...Options) *recordOptions {
  70. o := &recordOptions{}
  71. for _, ro := range ros {
  72. ro(o)
  73. }
  74. return o
  75. }
  76. type measurementRecorder = func(tags *tag.Map, measurement []Measurement, attachments map[string]interface{})
  77. // Record records one or multiple measurements with the same context at once.
  78. // If there are any tags in the context, measurements will be tagged with them.
  79. func Record(ctx context.Context, ms ...Measurement) {
  80. // Record behaves the same as RecordWithOptions, but because we do not have to handle generic functionality
  81. // (RecordOptions) we can reduce some allocations to speed up this hot path
  82. if len(ms) == 0 {
  83. return
  84. }
  85. recorder := internal.MeasurementRecorder.(measurementRecorder)
  86. record := false
  87. for _, m := range ms {
  88. if m.desc.subscribed() {
  89. record = true
  90. break
  91. }
  92. }
  93. if !record {
  94. return
  95. }
  96. recorder(tag.FromContext(ctx), ms, nil)
  97. return
  98. }
  99. // RecordWithTags records one or multiple measurements at once.
  100. //
  101. // Measurements will be tagged with the tags in the context mutated by the mutators.
  102. // RecordWithTags is useful if you want to record with tag mutations but don't want
  103. // to propagate the mutations in the context.
  104. func RecordWithTags(ctx context.Context, mutators []tag.Mutator, ms ...Measurement) error {
  105. return RecordWithOptions(ctx, WithTags(mutators...), WithMeasurements(ms...))
  106. }
  107. // RecordWithOptions records measurements from the given options (if any) against context
  108. // and tags and attachments in the options (if any).
  109. // If there are any tags in the context, measurements will be tagged with them.
  110. func RecordWithOptions(ctx context.Context, ros ...Options) error {
  111. o := createRecordOption(ros...)
  112. if len(o.measurements) == 0 {
  113. return nil
  114. }
  115. recorder := internal.DefaultRecorder
  116. if o.recorder != nil {
  117. recorder = o.recorder.Record
  118. }
  119. if recorder == nil {
  120. return nil
  121. }
  122. record := false
  123. for _, m := range o.measurements {
  124. if m.desc.subscribed() {
  125. record = true
  126. break
  127. }
  128. }
  129. if !record {
  130. return nil
  131. }
  132. if len(o.mutators) > 0 {
  133. var err error
  134. if ctx, err = tag.New(ctx, o.mutators...); err != nil {
  135. return err
  136. }
  137. }
  138. recorder(tag.FromContext(ctx), o.measurements, o.attachments)
  139. return nil
  140. }