metrics.go 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  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. package metrics
  15. import (
  16. "fmt"
  17. "reflect"
  18. "strings"
  19. )
  20. // Init initializes the passed in metrics and initializes its fields using the passed in factory.
  21. func Init(metrics interface{}, factory Factory, globalTags map[string]string) {
  22. if err := initMetrics(metrics, factory, globalTags); err != nil {
  23. panic(err.Error())
  24. }
  25. }
  26. // initMetrics uses reflection to initialize a struct containing metrics fields
  27. // by assigning new Counter/Gauge/Timer values with the metric name retrieved
  28. // from the `metric` tag and stats tags retrieved from the `tags` tag.
  29. //
  30. // Note: all fields of the struct must be exported, have a `metric` tag, and be
  31. // of type Counter or Gauge or Timer.
  32. func initMetrics(m interface{}, factory Factory, globalTags map[string]string) error {
  33. // Allow user to opt out of reporting metrics by passing in nil.
  34. if factory == nil {
  35. factory = NullFactory
  36. }
  37. counterPtrType := reflect.TypeOf((*Counter)(nil)).Elem()
  38. gaugePtrType := reflect.TypeOf((*Gauge)(nil)).Elem()
  39. timerPtrType := reflect.TypeOf((*Timer)(nil)).Elem()
  40. v := reflect.ValueOf(m).Elem()
  41. t := v.Type()
  42. for i := 0; i < t.NumField(); i++ {
  43. tags := make(map[string]string)
  44. for k, v := range globalTags {
  45. tags[k] = v
  46. }
  47. field := t.Field(i)
  48. metric := field.Tag.Get("metric")
  49. if metric == "" {
  50. return fmt.Errorf("Field %s is missing a tag 'metric'", field.Name)
  51. }
  52. if tagString := field.Tag.Get("tags"); tagString != "" {
  53. tagPairs := strings.Split(tagString, ",")
  54. for _, tagPair := range tagPairs {
  55. tag := strings.Split(tagPair, "=")
  56. if len(tag) != 2 {
  57. return fmt.Errorf(
  58. "Field [%s]: Tag [%s] is not of the form key=value in 'tags' string [%s]",
  59. field.Name, tagPair, tagString)
  60. }
  61. tags[tag[0]] = tag[1]
  62. }
  63. }
  64. var obj interface{}
  65. if field.Type.AssignableTo(counterPtrType) {
  66. obj = factory.Counter(metric, tags)
  67. } else if field.Type.AssignableTo(gaugePtrType) {
  68. obj = factory.Gauge(metric, tags)
  69. } else if field.Type.AssignableTo(timerPtrType) {
  70. obj = factory.Timer(metric, tags)
  71. } else {
  72. return fmt.Errorf(
  73. "Field %s is not a pointer to timer, gauge, or counter",
  74. field.Name)
  75. }
  76. v.Field(i).Set(reflect.ValueOf(obj))
  77. }
  78. return nil
  79. }