diff --git a/lib/gitlab/metrics/concern.rb b/lib/gitlab/metrics/concern.rb new file mode 100644 index 00000000000..ccc55cf5b76 --- /dev/null +++ b/lib/gitlab/metrics/concern.rb @@ -0,0 +1,84 @@ +module Gitlab + module Metrics + module Concern + extend ActiveSupport::Concern + class_methods do + private + + def metrics_provider(type, name, docstring, options = {}) + @@_metrics_provider_mutex ||= Mutex.new + + if instance_methods(false).include?(name) + raise ArgumentError, "metrics class method #{name} already exists" + end + options[:base_labels] ||= {} + + args = [name.inspect, %{"#{docstring}"}, options[:base_labels].inspect] + + case type + when :gauge + options[:multiprocess_mode] ||= :all + args << options[:multiprocess_mode].inspect + when :histogram + options[:buckets] ||= ::Prometheus::Client::Histogram::DEFAULT_BUCKETS + args << options[:buckets].inspect + end + + metric_fetching_code = %{Gitlab::Metrics::Prometheus.#{type}(#{args.join(', ')})} + + # optionally wrap in feature + metric_fetching_code = if options[:with_feature].is_a?(Symbol) + <<-FETCH.strip_heredoc + if Feature.get(#{options[:with_feature].inspect}).enabled? + #{metric_fetching_code} + else + Gitlab::Metrics::NullMetric.new + end + FETCH + end + + method_code, line = <<-METRIC, __LINE__ + 1 + def #{name} + @@_metric_provider_cached_#{name} if @@_metric_provider_cached_#{name} + + @@_metrics_provider_mutex.synchronize do + @_metric_provider_cached_#{name} ||= #{metric_fetching_code} + end + end + METRIC + + class_eval(method_code, __FILE__, line) + module_eval(method_code, __FILE__, line) + end + + # Declare a Counter + # @param [Symbol] name + # @param [String] docstring + # @param [Hash] opts + def counter(name, docstring, opts = {}) + metrics_provider(:counter, name, docstring, options) + end + + # Declare a Gauge + # @param [Symbol] name + # @param [String] docstring + # @param [Hash] opts + def gauge(name, docstring, opts = {}) + metrics_provider(:counter, name, docstring, opts) + end + + # Declare a Histograam + # @param [Symbol] name + # @param [String] docstring + # @param [Hash] opts + def histogram(name, docstring, opts = {}) + metrics_provider(:histogram, name, docstring, opts) + end + + def summary(*args) + raise NotImplementedError, "summary metrics are not currently supported" + end + end + end + end +end diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index c2f9db56824..2671b612abd 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -4,27 +4,13 @@ module Gitlab module Metrics # Class for tracking timing information about method calls class MethodCall - @@measurement_enabled_cache = Concurrent::AtomicBoolean.new(false) - @@measurement_enabled_cache_expires_at = Concurrent::AtomicReference.new(Time.now.to_i) - MUTEX = Mutex.new + include Gitlab::Metrics::Concern BASE_LABELS = { module: nil, method: nil }.freeze attr_reader :real_time, :cpu_time, :call_count, :labels - def self.call_duration_histogram - return @call_duration_histogram if @call_duration_histogram - - MUTEX.synchronize do - @call_duration_histogram ||= Gitlab::Metrics.histogram( - :gitlab_method_call_duration_seconds, - 'Method calls real duration', - Transaction::BASE_LABELS.merge(BASE_LABELS), - [0.01, 0.05, 0.1, 0.5, 1]) - end - end - - def self.measurement_enabled_cache_expires_at - @@measurement_enabled_cache_expires_at - end + histogram :gitlab_method_call_duration_seconds, 'Method calls real duration', + base_labels: Transaction::BASE_LABELS.merge(BASE_LABELS), + buckets: [0.01, 0.05, 0.1, 0.5, 1] # name - The full name of the method (including namespace) such as # `User#sign_in`. @@ -53,8 +39,8 @@ module Gitlab @cpu_time += cpu_time @call_count += 1 - if call_measurement_enabled? && above_threshold? - self.class.call_duration_histogram.observe(@transaction.labels.merge(labels), real_time) + if above_threshold? + gitlab_method_call_duration_seconds.observe(@transaction.labels.merge(labels), real_time) end retval @@ -78,17 +64,6 @@ module Gitlab def above_threshold? real_time.in_milliseconds >= Metrics.method_call_threshold end - - def call_measurement_enabled? - expires_at = @@measurement_enabled_cache_expires_at.value - if expires_at < Time.now.to_i - if @@measurement_enabled_cache_expires_at.compare_and_set(expires_at, 1.minute.from_now.to_i) - @@measurement_enabled_cache.value = Feature.get(:prometheus_metrics_method_instrumentation).enabled? - end - end - - @@measurement_enabled_cache.value - end end end end diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 3da474fc1ec..5c390a077c2 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -3,6 +3,11 @@ module Gitlab module Subscribers # Class for tracking the rendering timings of views. class ActionView < ActiveSupport::Subscriber + include Gitlab::Metrics::Concern + histogram :gitlab_view_rendering_duration_seconds, 'View rendering time', + base_labels: Transaction::BASE_LABELS.merge({ path: nil }), + buckets: [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + attach_to :action_view SERIES = 'views'.freeze @@ -15,23 +20,11 @@ module Gitlab private - def metric_view_rendering_duration_seconds - @metric_view_rendering_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_view_rendering_duration_seconds, - 'View rendering time', - Transaction::BASE_LABELS.merge({ path: nil }), - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - def track(event) values = values_for(event) tags = tags_for(event) - metric_view_rendering_duration_seconds.observe( - current_transaction.labels.merge(tags), - event.duration - ) + gitlab_view_rendering_duration_seconds.observe(current_transaction.labels.merge(tags), event.duration) current_transaction.increment(:view_duration, event.duration) current_transaction.add_metric(SERIES, values, tags)