You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

worker.go 6.8 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. "fmt"
  18. "sync"
  19. "time"
  20. "go.opencensus.io/metric/metricdata"
  21. "go.opencensus.io/metric/metricproducer"
  22. "go.opencensus.io/stats"
  23. "go.opencensus.io/stats/internal"
  24. "go.opencensus.io/tag"
  25. )
  26. func init() {
  27. defaultWorker = newWorker()
  28. go defaultWorker.start()
  29. internal.DefaultRecorder = record
  30. }
  31. type measureRef struct {
  32. measure string
  33. views map[*viewInternal]struct{}
  34. }
  35. type worker struct {
  36. measures map[string]*measureRef
  37. views map[string]*viewInternal
  38. startTimes map[*viewInternal]time.Time
  39. timer *time.Ticker
  40. c chan command
  41. quit, done chan bool
  42. mu sync.RWMutex
  43. }
  44. var defaultWorker *worker
  45. var defaultReportingDuration = 10 * time.Second
  46. // Find returns a registered view associated with this name.
  47. // If no registered view is found, nil is returned.
  48. func Find(name string) (v *View) {
  49. req := &getViewByNameReq{
  50. name: name,
  51. c: make(chan *getViewByNameResp),
  52. }
  53. defaultWorker.c <- req
  54. resp := <-req.c
  55. return resp.v
  56. }
  57. // Register begins collecting data for the given views.
  58. // Once a view is registered, it reports data to the registered exporters.
  59. func Register(views ...*View) error {
  60. req := &registerViewReq{
  61. views: views,
  62. err: make(chan error),
  63. }
  64. defaultWorker.c <- req
  65. return <-req.err
  66. }
  67. // Unregister the given views. Data will not longer be exported for these views
  68. // after Unregister returns.
  69. // It is not necessary to unregister from views you expect to collect for the
  70. // duration of your program execution.
  71. func Unregister(views ...*View) {
  72. names := make([]string, len(views))
  73. for i := range views {
  74. names[i] = views[i].Name
  75. }
  76. req := &unregisterFromViewReq{
  77. views: names,
  78. done: make(chan struct{}),
  79. }
  80. defaultWorker.c <- req
  81. <-req.done
  82. }
  83. // RetrieveData gets a snapshot of the data collected for the the view registered
  84. // with the given name. It is intended for testing only.
  85. func RetrieveData(viewName string) ([]*Row, error) {
  86. req := &retrieveDataReq{
  87. now: time.Now(),
  88. v: viewName,
  89. c: make(chan *retrieveDataResp),
  90. }
  91. defaultWorker.c <- req
  92. resp := <-req.c
  93. return resp.rows, resp.err
  94. }
  95. func record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
  96. req := &recordReq{
  97. tm: tags,
  98. ms: ms.([]stats.Measurement),
  99. attachments: attachments,
  100. t: time.Now(),
  101. }
  102. defaultWorker.c <- req
  103. }
  104. // SetReportingPeriod sets the interval between reporting aggregated views in
  105. // the program. If duration is less than or equal to zero, it enables the
  106. // default behavior.
  107. //
  108. // Note: each exporter makes different promises about what the lowest supported
  109. // duration is. For example, the Stackdriver exporter recommends a value no
  110. // lower than 1 minute. Consult each exporter per your needs.
  111. func SetReportingPeriod(d time.Duration) {
  112. // TODO(acetechnologist): ensure that the duration d is more than a certain
  113. // value. e.g. 1s
  114. req := &setReportingPeriodReq{
  115. d: d,
  116. c: make(chan bool),
  117. }
  118. defaultWorker.c <- req
  119. <-req.c // don't return until the timer is set to the new duration.
  120. }
  121. func newWorker() *worker {
  122. return &worker{
  123. measures: make(map[string]*measureRef),
  124. views: make(map[string]*viewInternal),
  125. startTimes: make(map[*viewInternal]time.Time),
  126. timer: time.NewTicker(defaultReportingDuration),
  127. c: make(chan command, 1024),
  128. quit: make(chan bool),
  129. done: make(chan bool),
  130. }
  131. }
  132. func (w *worker) start() {
  133. prodMgr := metricproducer.GlobalManager()
  134. prodMgr.AddProducer(w)
  135. for {
  136. select {
  137. case cmd := <-w.c:
  138. cmd.handleCommand(w)
  139. case <-w.timer.C:
  140. w.reportUsage(time.Now())
  141. case <-w.quit:
  142. w.timer.Stop()
  143. close(w.c)
  144. w.done <- true
  145. return
  146. }
  147. }
  148. }
  149. func (w *worker) stop() {
  150. prodMgr := metricproducer.GlobalManager()
  151. prodMgr.DeleteProducer(w)
  152. w.quit <- true
  153. <-w.done
  154. }
  155. func (w *worker) getMeasureRef(name string) *measureRef {
  156. if mr, ok := w.measures[name]; ok {
  157. return mr
  158. }
  159. mr := &measureRef{
  160. measure: name,
  161. views: make(map[*viewInternal]struct{}),
  162. }
  163. w.measures[name] = mr
  164. return mr
  165. }
  166. func (w *worker) tryRegisterView(v *View) (*viewInternal, error) {
  167. w.mu.Lock()
  168. defer w.mu.Unlock()
  169. vi, err := newViewInternal(v)
  170. if err != nil {
  171. return nil, err
  172. }
  173. if x, ok := w.views[vi.view.Name]; ok {
  174. if !x.view.same(vi.view) {
  175. return nil, fmt.Errorf("cannot register view %q; a different view with the same name is already registered", v.Name)
  176. }
  177. // the view is already registered so there is nothing to do and the
  178. // command is considered successful.
  179. return x, nil
  180. }
  181. w.views[vi.view.Name] = vi
  182. ref := w.getMeasureRef(vi.view.Measure.Name())
  183. ref.views[vi] = struct{}{}
  184. return vi, nil
  185. }
  186. func (w *worker) unregisterView(viewName string) {
  187. w.mu.Lock()
  188. defer w.mu.Unlock()
  189. delete(w.views, viewName)
  190. }
  191. func (w *worker) reportView(v *viewInternal, now time.Time) {
  192. if !v.isSubscribed() {
  193. return
  194. }
  195. rows := v.collectedRows()
  196. _, ok := w.startTimes[v]
  197. if !ok {
  198. w.startTimes[v] = now
  199. }
  200. viewData := &Data{
  201. View: v.view,
  202. Start: w.startTimes[v],
  203. End: time.Now(),
  204. Rows: rows,
  205. }
  206. exportersMu.Lock()
  207. for e := range exporters {
  208. e.ExportView(viewData)
  209. }
  210. exportersMu.Unlock()
  211. }
  212. func (w *worker) reportUsage(now time.Time) {
  213. w.mu.Lock()
  214. defer w.mu.Unlock()
  215. for _, v := range w.views {
  216. w.reportView(v, now)
  217. }
  218. }
  219. func (w *worker) toMetric(v *viewInternal, now time.Time) *metricdata.Metric {
  220. if !v.isSubscribed() {
  221. return nil
  222. }
  223. _, ok := w.startTimes[v]
  224. if !ok {
  225. w.startTimes[v] = now
  226. }
  227. var startTime time.Time
  228. if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
  229. v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
  230. startTime = time.Time{}
  231. } else {
  232. startTime = w.startTimes[v]
  233. }
  234. return viewToMetric(v, now, startTime)
  235. }
  236. // Read reads all view data and returns them as metrics.
  237. // It is typically invoked by metric reader to export stats in metric format.
  238. func (w *worker) Read() []*metricdata.Metric {
  239. w.mu.Lock()
  240. defer w.mu.Unlock()
  241. now := time.Now()
  242. metrics := make([]*metricdata.Metric, 0, len(w.views))
  243. for _, v := range w.views {
  244. metric := w.toMetric(v, now)
  245. if metric != nil {
  246. metrics = append(metrics, metric)
  247. }
  248. }
  249. return metrics
  250. }