summaryrefslogtreecommitdiff
path: root/lib/gitlab/metrics/concern.rb
blob: d9d69d8026e3dcfad6bb039b9897cb5e3eb9eac6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# rubocop:disable Style/ClassVars

module Gitlab
  module Metrics
    module Concern
      extend ActiveSupport::Concern

      included do
        @@_metric_provider_mutex = Mutex.new
        @_metrics_provider_cache = {}
      end

      class_methods do
        def reload_metric!(name)
          @_metrics_provider_cache.delete(name)
        end

        private

        def define_metric(type, name, opts = {}, &block)
          if respond_to?(name)
            raise ArgumentError, "method #{name} already exists"
          end

          define_singleton_method(name) do
            @_metrics_provider_cache&.[](name) || init_metric(type, name, opts, &block)
          end
        end

        def fetch_metric(type, name, opts = {}, &block)
          @_metrics_provider_cache&.[](name) || init_metric(type, name, opts, &block)
        end

        def init_metric(type, name, opts = {}, &block)
          options = MetricOptions.new(opts)
          options.evaluate(&block)

          if disabled_by_feature(options)
            synchronized_cache_fill(name) { NullMetric.instance }
          else
            synchronized_cache_fill(name) { build_metric!(type, name, options) }
          end
        end

        def synchronized_cache_fill(key)
          @@_metric_provider_mutex.synchronize do
            @_metrics_provider_cache ||= {}
            @_metrics_provider_cache[key] ||= yield
          end
        end

        def disabled_by_feature(options)
          options.with_feature && !Feature.get(options.with_feature).enabled?
        end

        def build_metric!(type, name, options)
          case type
          when :gauge
            Gitlab::Metrics.gauge(name, options.docstring, options.base_labels, options.multiprocess_mode)
          when :counter
            Gitlab::Metrics.counter(name, options.docstring, options.base_labels)
          when :histogram
            Gitlab::Metrics.histogram(name, options.docstring, options.base_labels, options.buckets)
          when :summary
            raise NotImplementedError, "summary metrics are not currently supported"
          else
            raise ArgumentError, "uknown metric type #{type}"
          end
        end

        # Fetch and/or initialize counter metric
        # @param [Symbol] name
        # @param [Hash] opts
        def fetch_counter(name, opts = {}, &block)
          fetch_metric(:counter, name, opts, &block)
        end

        # Fetch and/or initialize gauge metric
        # @param [Symbol] name
        # @param [Hash] opts
        def fetch_gauge(name, opts = {}, &block)
          fetch_metric(:gauge, name, opts, &block)
        end

        # Fetch and/or initialize histogram metric
        # @param [Symbol] name
        # @param [Hash] opts
        def fetch_histogram(name, opts = {}, &block)
          fetch_metric(:histogram, name, opts, &block)
        end

        # Fetch and/or initialize summary metric
        # @param [Symbol] name
        # @param [Hash] opts
        def fetch_summary(name, opts = {}, &block)
          fetch_metric(:summary, name, opts, &block)
        end

        # Define metric accessor method for a Counter
        # @param [Symbol] name
        # @param [Hash] opts
        def define_counter(name, opts = {}, &block)
          define_metric(:counter, name, opts, &block)
        end

        # Define metric accessor method for a Gauge
        # @param [Symbol] name
        # @param [Hash] opts
        def define_gauge(name, opts = {}, &block)
          define_metric(:gauge, name, opts, &block)
        end

        # Define metric accessor method for a Histogram
        # @param [Symbol] name
        # @param [Hash] opts
        def define_histogram(name, opts = {}, &block)
          define_metric(:histogram, name, opts, &block)
        end

        # Define metric accessor method for a Summary
        # @param [Symbol] name
        # @param [Hash] opts
        def define_summary(name, opts = {}, &block)
          define_metric(:summary, name, opts, &block)
        end
      end
    end
  end
end