summaryrefslogtreecommitdiff
path: root/lib/gitlab/metrics/methods.rb
blob: 447d03bfca48c911ca31c7c91bcdfd1520b31a7f (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
130
131
# frozen_string_literal: true

# rubocop:disable Style/ClassVars

module Gitlab
  module Metrics
    module Methods
      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
            # inlining fetch_metric method to avoid method call overhead when instrumenting hot spots
            @@_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[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