registry.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package prometheus
  2. import (
  3. "sync"
  4. "github.com/prometheus/client_golang/prometheus"
  5. dto "github.com/prometheus/client_model/go"
  6. "github.com/prometheus/common/expfmt"
  7. "github.com/ydb-platform/ydb/library/go/core/metrics"
  8. "github.com/ydb-platform/ydb/library/go/core/metrics/internal/pkg/metricsutil"
  9. "github.com/ydb-platform/ydb/library/go/core/metrics/internal/pkg/registryutil"
  10. "github.com/ydb-platform/ydb/library/go/core/xerrors"
  11. )
  12. var _ metrics.Registry = (*Registry)(nil)
  13. var _ metrics.MetricsStreamer = (*Registry)(nil)
  14. type Registry struct {
  15. rg PrometheusRegistry
  16. m *sync.Mutex
  17. subregistries map[string]*Registry
  18. tags map[string]string
  19. prefix string
  20. nameSanitizer func(string) string
  21. streamFormat expfmt.Format
  22. }
  23. // NewRegistry creates new Prometheus backed registry.
  24. func NewRegistry(opts *RegistryOpts) *Registry {
  25. r := &Registry{
  26. rg: prometheus.NewRegistry(),
  27. m: new(sync.Mutex),
  28. subregistries: make(map[string]*Registry),
  29. tags: make(map[string]string),
  30. streamFormat: StreamCompact,
  31. }
  32. if opts != nil {
  33. r.prefix = opts.Prefix
  34. r.tags = opts.Tags
  35. r.streamFormat = opts.StreamFormat
  36. if opts.rg != nil {
  37. r.rg = opts.rg
  38. }
  39. for _, collector := range opts.Collectors {
  40. collector(r)
  41. }
  42. if opts.NameSanitizer != nil {
  43. r.nameSanitizer = opts.NameSanitizer
  44. }
  45. }
  46. return r
  47. }
  48. // WithTags creates new sub-scope, where each metric has tags attached to it.
  49. func (r Registry) WithTags(tags map[string]string) metrics.Registry {
  50. return r.newSubregistry(r.prefix, registryutil.MergeTags(r.tags, tags))
  51. }
  52. // WithPrefix creates new sub-scope, where each metric has prefix added to it name.
  53. func (r Registry) WithPrefix(prefix string) metrics.Registry {
  54. return r.newSubregistry(registryutil.BuildFQName("_", r.prefix, prefix), r.tags)
  55. }
  56. // ComposeName builds FQ name with appropriate separator.
  57. func (r Registry) ComposeName(parts ...string) string {
  58. return registryutil.BuildFQName("_", parts...)
  59. }
  60. func (r Registry) Counter(name string) metrics.Counter {
  61. name = r.sanitizeName(name)
  62. cnt := prometheus.NewCounter(prometheus.CounterOpts{
  63. Namespace: r.prefix,
  64. Name: name,
  65. ConstLabels: r.tags,
  66. })
  67. if err := r.rg.Register(cnt); err != nil {
  68. var existErr prometheus.AlreadyRegisteredError
  69. if xerrors.As(err, &existErr) {
  70. return &Counter{cnt: existErr.ExistingCollector.(prometheus.Counter)}
  71. }
  72. panic(err)
  73. }
  74. return &Counter{cnt: cnt}
  75. }
  76. func (r Registry) FuncCounter(name string, function func() int64) metrics.FuncCounter {
  77. name = r.sanitizeName(name)
  78. cnt := prometheus.NewCounterFunc(prometheus.CounterOpts{
  79. Namespace: r.prefix,
  80. Name: name,
  81. ConstLabels: r.tags,
  82. }, func() float64 {
  83. return float64(function())
  84. })
  85. if err := r.rg.Register(cnt); err != nil {
  86. panic(err)
  87. }
  88. return &FuncCounter{
  89. cnt: cnt,
  90. function: function,
  91. }
  92. }
  93. func (r Registry) Gauge(name string) metrics.Gauge {
  94. name = r.sanitizeName(name)
  95. gg := prometheus.NewGauge(prometheus.GaugeOpts{
  96. Namespace: r.prefix,
  97. Name: name,
  98. ConstLabels: r.tags,
  99. })
  100. if err := r.rg.Register(gg); err != nil {
  101. var existErr prometheus.AlreadyRegisteredError
  102. if xerrors.As(err, &existErr) {
  103. return &Gauge{gg: existErr.ExistingCollector.(prometheus.Gauge)}
  104. }
  105. panic(err)
  106. }
  107. return &Gauge{gg: gg}
  108. }
  109. func (r Registry) FuncGauge(name string, function func() float64) metrics.FuncGauge {
  110. name = r.sanitizeName(name)
  111. ff := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
  112. Namespace: r.prefix,
  113. Name: name,
  114. ConstLabels: r.tags,
  115. }, function)
  116. if err := r.rg.Register(ff); err != nil {
  117. panic(err)
  118. }
  119. return &FuncGauge{
  120. ff: ff,
  121. function: function,
  122. }
  123. }
  124. func (r Registry) IntGauge(name string) metrics.IntGauge {
  125. return &IntGauge{Gauge: r.Gauge(name)}
  126. }
  127. func (r Registry) FuncIntGauge(name string, function func() int64) metrics.FuncIntGauge {
  128. name = r.sanitizeName(name)
  129. ff := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
  130. Namespace: r.prefix,
  131. Name: name,
  132. ConstLabels: r.tags,
  133. }, func() float64 { return float64(function()) })
  134. if err := r.rg.Register(ff); err != nil {
  135. panic(err)
  136. }
  137. return &FuncIntGauge{
  138. ff: ff,
  139. function: function,
  140. }
  141. }
  142. func (r Registry) Timer(name string) metrics.Timer {
  143. name = r.sanitizeName(name)
  144. gg := prometheus.NewGauge(prometheus.GaugeOpts{
  145. Namespace: r.prefix,
  146. Name: name,
  147. ConstLabels: r.tags,
  148. })
  149. if err := r.rg.Register(gg); err != nil {
  150. var existErr prometheus.AlreadyRegisteredError
  151. if xerrors.As(err, &existErr) {
  152. return &Timer{gg: existErr.ExistingCollector.(prometheus.Gauge)}
  153. }
  154. panic(err)
  155. }
  156. return &Timer{gg: gg}
  157. }
  158. func (r Registry) Histogram(name string, buckets metrics.Buckets) metrics.Histogram {
  159. name = r.sanitizeName(name)
  160. hm := prometheus.NewHistogram(prometheus.HistogramOpts{
  161. Namespace: r.prefix,
  162. Name: name,
  163. ConstLabels: r.tags,
  164. Buckets: metricsutil.BucketsBounds(buckets),
  165. })
  166. if err := r.rg.Register(hm); err != nil {
  167. var existErr prometheus.AlreadyRegisteredError
  168. if xerrors.As(err, &existErr) {
  169. return &Histogram{hm: existErr.ExistingCollector.(prometheus.Observer)}
  170. }
  171. panic(err)
  172. }
  173. return &Histogram{hm: hm}
  174. }
  175. func (r Registry) DurationHistogram(name string, buckets metrics.DurationBuckets) metrics.Timer {
  176. name = r.sanitizeName(name)
  177. hm := prometheus.NewHistogram(prometheus.HistogramOpts{
  178. Namespace: r.prefix,
  179. Name: name,
  180. ConstLabels: r.tags,
  181. Buckets: metricsutil.DurationBucketsBounds(buckets),
  182. })
  183. if err := r.rg.Register(hm); err != nil {
  184. var existErr prometheus.AlreadyRegisteredError
  185. if xerrors.As(err, &existErr) {
  186. return &Histogram{hm: existErr.ExistingCollector.(prometheus.Histogram)}
  187. }
  188. panic(err)
  189. }
  190. return &Histogram{hm: hm}
  191. }
  192. // Gather returns raw collected Prometheus metrics.
  193. func (r Registry) Gather() ([]*dto.MetricFamily, error) {
  194. return r.rg.Gather()
  195. }
  196. func (r *Registry) newSubregistry(prefix string, tags map[string]string) *Registry {
  197. registryKey := registryutil.BuildRegistryKey(prefix, tags)
  198. r.m.Lock()
  199. defer r.m.Unlock()
  200. if old, ok := r.subregistries[registryKey]; ok {
  201. return old
  202. }
  203. subregistry := &Registry{
  204. rg: r.rg,
  205. m: r.m,
  206. subregistries: r.subregistries,
  207. tags: tags,
  208. prefix: prefix,
  209. nameSanitizer: r.nameSanitizer,
  210. }
  211. r.subregistries[registryKey] = subregistry
  212. return subregistry
  213. }
  214. func (r *Registry) sanitizeName(name string) string {
  215. if r.nameSanitizer == nil {
  216. return name
  217. }
  218. return r.nameSanitizer(name)
  219. }