1# frozen_string_literal: true
2
3# rubocop:disable Style/ClassVars
4
5module Gitlab
6  module Metrics
7    module Methods
8      extend ActiveSupport::Concern
9
10      included do
11        @@_metric_provider_mutex ||= Mutex.new
12        @@_metrics_provider_cache = {}
13      end
14
15      class_methods do
16        def reload_metric!(name = nil)
17          if name.nil?
18            @@_metrics_provider_cache = {}
19          else
20            @@_metrics_provider_cache.delete(name)
21          end
22        end
23
24        private
25
26        def define_metric(type, name, opts = {}, &block)
27          if respond_to?(name)
28            raise ArgumentError, "method #{name} already exists"
29          end
30
31          define_singleton_method(name) do
32            # inlining fetch_metric method to avoid method call overhead when instrumenting hot spots
33            @@_metrics_provider_cache[name] || init_metric(type, name, opts, &block)
34          end
35        end
36
37        def fetch_metric(type, name, opts = {}, &block)
38          @@_metrics_provider_cache[name] || init_metric(type, name, opts, &block)
39        end
40
41        def init_metric(type, name, opts = {}, &block)
42          options = ::Gitlab::Metrics::Methods::MetricOptions.new(opts)
43          options.evaluate(&block)
44
45          if disabled_by_feature(options)
46            synchronized_cache_fill(name) { ::Gitlab::Metrics::NullMetric.instance }
47          else
48            synchronized_cache_fill(name) { build_metric!(type, name, options) }
49          end
50        end
51
52        def synchronized_cache_fill(key)
53          @@_metric_provider_mutex.synchronize do
54            @@_metrics_provider_cache[key] ||= yield
55          end
56        end
57
58        def disabled_by_feature(options)
59          options.with_feature && !::Feature.enabled?(options.with_feature, type: :ops)
60        end
61
62        def build_metric!(type, name, options)
63          case type
64          when :gauge
65            ::Gitlab::Metrics.gauge(name, options.docstring, options.base_labels, options.multiprocess_mode)
66          when :counter
67            ::Gitlab::Metrics.counter(name, options.docstring, options.base_labels)
68          when :histogram
69            ::Gitlab::Metrics.histogram(name, options.docstring, options.base_labels, options.buckets)
70          when :summary
71            raise NotImplementedError, "summary metrics are not currently supported"
72          else
73            raise ArgumentError, "uknown metric type #{type}"
74          end
75        end
76      end
77    end
78  end
79end
80