debian-mirror-gitlab/lib/gitlab/metrics/transaction.rb

168 lines
4.9 KiB
Ruby
Raw Normal View History

2019-02-15 15:39:39 +05:30
# frozen_string_literal: true
module Gitlab
module Metrics
# Class for storing metrics information of a single transaction.
class Transaction
2018-03-17 18:26:18 +05:30
include Gitlab::Metrics::Methods
2020-10-24 23:57:45 +05:30
# base label keys shared among all transactions
BASE_LABEL_KEYS = %i(controller action feature_category).freeze
2019-07-07 11:18:12 +05:30
# labels that potentially contain sensitive information and will be filtered
2020-10-24 23:57:45 +05:30
FILTERED_LABEL_KEYS = %i(branch path).freeze
2018-03-17 18:26:18 +05:30
THREAD_KEY = :_gitlab_metrics_transaction
2020-10-24 23:57:45 +05:30
SMALL_BUCKETS = [0.1, 0.25, 0.5, 1.0, 2.5, 5.0].freeze
2016-09-13 17:45:13 +05:30
# The series to store events (e.g. Git pushes) in.
2019-12-04 20:38:33 +05:30
EVENT_SERIES = 'events'
2016-09-13 17:45:13 +05:30
2020-06-23 00:09:42 +05:30
attr_reader :method
2020-10-24 23:57:45 +05:30
class << self
def current
Thread.current[THREAD_KEY]
end
def prometheus_metric(name, type, &block)
fetch_metric(type, name) do
# set default metric options
docstring "#{name.to_s.humanize} #{type}"
evaluate(&block)
# always filter sensitive labels and merge with base ones
label_keys BASE_LABEL_KEYS | (label_keys - FILTERED_LABEL_KEYS)
end
end
end
2018-03-17 18:26:18 +05:30
def initialize
2016-06-22 15:30:34 +05:30
@methods = {}
2018-03-17 18:26:18 +05:30
@started_at = nil
@finished_at = nil
end
def duration
2016-08-24 12:49:21 +05:30
@finished_at ? (@finished_at - @started_at) : 0.0
end
2019-12-21 20:55:43 +05:30
def thread_cpu_duration
System.thread_cpu_duration(@thread_cputime_start)
end
def run
Thread.current[THREAD_KEY] = self
2018-03-17 18:26:18 +05:30
@started_at = System.monotonic_time
2019-12-21 20:55:43 +05:30
@thread_cputime_start = System.thread_cpu_time
yield
ensure
2018-03-17 18:26:18 +05:30
@finished_at = System.monotonic_time
2020-10-24 23:57:45 +05:30
observe(:gitlab_transaction_cputime_seconds, thread_cpu_duration) do
buckets SMALL_BUCKETS
end
observe(:gitlab_transaction_duration_seconds, duration) do
buckets SMALL_BUCKETS
end
Thread.current[THREAD_KEY] = nil
end
2016-09-13 17:45:13 +05:30
# Tracks a business level event
2016-06-22 15:30:34 +05:30
#
2016-09-13 17:45:13 +05:30
# Business level events including events such as Git pushes, Emails being
# sent, etc.
2016-06-22 15:30:34 +05:30
#
2016-09-13 17:45:13 +05:30
# event_name - The name of the event (e.g. "git_push").
# tags - A set of tags to attach to the event.
def add_event(event_name, tags = {})
2020-10-24 23:57:45 +05:30
event_name = "gitlab_transaction_event_#{event_name}_total".to_sym
metric = self.class.prometheus_metric(event_name, :counter) do
label_keys tags.keys
end
metric.increment(filter_labels(tags))
2016-09-13 17:45:13 +05:30
end
2016-06-22 15:30:34 +05:30
2016-09-13 17:45:13 +05:30
# Returns a MethodCall object for the given name.
2018-03-17 18:26:18 +05:30
def method_call_for(name, module_name, method_name)
2016-09-13 17:45:13 +05:30
unless method = @methods[name]
2018-03-17 18:26:18 +05:30
@methods[name] = method = MethodCall.new(name, module_name, method_name, self)
2016-06-22 15:30:34 +05:30
end
2016-09-13 17:45:13 +05:30
method
end
2020-10-24 23:57:45 +05:30
# Increment counter metric
#
# It will initialize the metric if metric is not found
#
# block - if provided can be used to initialize metric with custom options (docstring, labels, with_feature)
#
# Example:
# ```
# transaction.increment(:mestric_name, 1, { docstring: 'Custom title', base_labels: {sane: 'yes'} } ) do
#
# transaction.increment(:mestric_name, 1) do
# docstring 'Custom title'
# label_keys %i(sane)
# end
# ```
def increment(name, value = 1, labels = {}, &block)
counter = self.class.prometheus_metric(name, :counter, &block)
2020-10-24 23:57:45 +05:30
counter.increment(filter_labels(labels), value)
end
2018-03-17 18:26:18 +05:30
2020-10-24 23:57:45 +05:30
# Set gauge metric
#
# It will initialize the metric if metric is not found
#
# block - if provided, it can be used to initialize metric with custom options (docstring, labels, with_feature, multiprocess_mode)
# - multiprocess_mode is :all by default
#
# Example:
# ```
# transaction.set(:mestric_name, 1) do
# multiprocess_mode :livesum
# end
# ```
def set(name, value, labels = {}, &block)
gauge = self.class.prometheus_metric(name, :gauge, &block)
2018-03-17 18:26:18 +05:30
2020-10-24 23:57:45 +05:30
gauge.set(filter_labels(labels), value)
2019-12-21 20:55:43 +05:30
end
2020-10-24 23:57:45 +05:30
# Observe histogram metric
#
# It will initialize the metric if metric is not found
#
# block - if provided, it can be used to initialize metric with custom options (docstring, labels, with_feature, buckets)
#
# Example:
# ```
# transaction.observe(:mestric_name, 1) do
# buckets [100, 1000, 10000, 100000, 1000000, 10000000]
# end
# ```
def observe(name, value, labels = {}, &block)
histogram = self.class.prometheus_metric(name, :histogram, &block)
2018-03-17 18:26:18 +05:30
2020-10-24 23:57:45 +05:30
histogram.observe(filter_labels(labels), value)
2018-03-17 18:26:18 +05:30
end
2020-10-24 23:57:45 +05:30
def labels
BASE_LABEL_KEYS.product([nil]).to_h
2018-03-17 18:26:18 +05:30
end
2019-07-07 11:18:12 +05:30
2020-10-24 23:57:45 +05:30
def filter_labels(labels)
labels.empty? ? self.labels : labels.without(*FILTERED_LABEL_KEYS).merge(self.labels)
2019-07-07 11:18:12 +05:30
end
end
end
end