Commit 39100c59 authored by nmilojevic1's avatar nmilojevic1

Introduce label_keys as metric options

- Fix specs for metric options
- Add labels as a param to increment, set and observe
parent f291599e
--- ---
title: Unify Prometheus metric initialization by always using inline transaction metrics. title: Unify Prometheus metric initialization by always using inline transaction metrics
merge_request: 32980 merge_request: 32980
author: author:
type: other type: other
...@@ -34,9 +34,10 @@ module Gitlab ...@@ -34,9 +34,10 @@ module Gitlab
@call_count += 1 @call_count += 1
if above_threshold? && transaction if above_threshold? && transaction
transaction.observe(:gitlab_method_call_duration_seconds, real_time) do label_keys = labels.keys
transaction.observe(:gitlab_method_call_duration_seconds, real_time, labels) do
docstring 'Method calls real duration' docstring 'Method calls real duration'
base_labels @labels label_keys label_keys
buckets [0.01, 0.05, 0.1, 0.5, 1] buckets [0.01, 0.05, 0.1, 0.5, 1]
with_feature :prometheus_metrics_method_instrumentation with_feature :prometheus_metrics_method_instrumentation
end end
......
...@@ -4,14 +4,13 @@ module Gitlab ...@@ -4,14 +4,13 @@ module Gitlab
module Metrics module Metrics
module Methods module Methods
class MetricOptions class MetricOptions
SMALL_NETWORK_BUCKETS = [0.005, 0.01, 0.1, 1, 10].freeze
def initialize(options = {}) def initialize(options = {})
@multiprocess_mode = options[:multiprocess_mode] || :all @multiprocess_mode = options[:multiprocess_mode] || :all
@buckets = options[:buckets] || SMALL_NETWORK_BUCKETS @buckets = options[:buckets] || ::Prometheus::Client::Histogram::DEFAULT_BUCKETS
@base_labels = options[:base_labels] || {} @base_labels = options[:base_labels] || {}
@docstring = options[:docstring] @docstring = options[:docstring]
@with_feature = options[:with_feature] @with_feature = options[:with_feature]
@label_keys = options[:label_keys] || []
end end
# Documentation describing metric in metrics endpoint '/-/metrics' # Documentation describing metric in metrics endpoint '/-/metrics'
...@@ -40,12 +39,24 @@ module Gitlab ...@@ -40,12 +39,24 @@ module Gitlab
end end
# Base labels are merged with per metric labels # Base labels are merged with per metric labels
def base_labels(base_labels = nil) def base_labels
@base_labels = base_labels unless base_labels.nil? seed_labels if @base_labels.empty?
@base_labels @base_labels
end end
def label_keys(label_keys = nil)
@label_keys = label_keys unless label_keys.nil?
@label_keys
end
def seed_labels
@label_keys.each do |key|
@base_labels[key] = nil
end
end
# Use feature toggle to control whether certain metric is enabled/disabled # Use feature toggle to control whether certain metric is enabled/disabled
def with_feature(name = nil) def with_feature(name = nil)
@with_feature = name unless name.nil? @with_feature = name unless name.nil?
......
...@@ -19,9 +19,9 @@ module Gitlab ...@@ -19,9 +19,9 @@ module Gitlab
def track(event) def track(event)
tags = tags_for(event) tags = tags_for(event)
current_transaction.observe(:gitlab_view_rendering_duration_seconds, event.duration) do current_transaction.observe(:gitlab_view_rendering_duration_seconds, event.duration, tags) do
docstring 'View rendering time' docstring 'View rendering time'
base_labels tags label_keys %i(view)
buckets [0.001, 0.01, 0.1, 1, 10.0] buckets [0.001, 0.01, 0.1, 1, 10.0]
with_feature :prometheus_metrics_view_instrumentation with_feature :prometheus_metrics_view_instrumentation
end end
......
...@@ -53,15 +53,15 @@ module Gitlab ...@@ -53,15 +53,15 @@ module Gitlab
return unless current_transaction return unless current_transaction
labels = { operation: key } labels = { operation: key }
current_transaction.increment(:gitlab_cache_operations_total, 1) do current_transaction.increment(:gitlab_cache_operations_total, 1, labels) do
docstring 'Cache operations' docstring 'Cache operations'
base_labels labels label_keys labels.keys
end end
current_transaction.observe(:gitlab_cache_operation_duration_seconds, duration / 1000.0) do current_transaction.observe(:gitlab_cache_operation_duration_seconds, duration / 1000.0, labels) do
docstring 'Cache access time' docstring 'Cache access time'
buckets [0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0] buckets [0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0]
base_labels labels label_keys labels.keys
end end
end end
......
...@@ -6,10 +6,10 @@ module Gitlab ...@@ -6,10 +6,10 @@ module Gitlab
class Transaction class Transaction
include Gitlab::Metrics::Methods include Gitlab::Metrics::Methods
# base labels shared among all transactions # base label keys shared among all transactions
BASE_LABELS = { controller: nil, action: nil, feature_category: nil }.freeze BASE_LABEL_KEYS = %i(controller action feature_category).freeze
# labels that potentially contain sensitive information and will be filtered # labels that potentially contain sensitive information and will be filtered
FILTERED_LABELS = [:branch, :path].freeze FILTERED_LABELS = %i(branch path).freeze
THREAD_KEY = :_gitlab_metrics_transaction THREAD_KEY = :_gitlab_metrics_transaction
...@@ -31,11 +31,10 @@ module Gitlab ...@@ -31,11 +31,10 @@ module Gitlab
# set default metric options # set default metric options
docstring "#{name.to_s.humanize} #{type}" docstring "#{name.to_s.humanize} #{type}"
multiprocess_mode :livesum if type == :gauge multiprocess_mode :livesum if type == :gauge
buckets ::Prometheus::Client::Histogram::DEFAULT_BUCKETS if type == :histogram
evaluate(&block) evaluate(&block)
# always filter sensitive labels and merge with base ones # always filter sensitive labels and merge with base ones
base_labels base_labels.without(*FILTERED_LABELS).merge(BASE_LABELS) label_keys BASE_LABEL_KEYS | (label_keys - FILTERED_LABELS)
end end
end end
end end
...@@ -97,10 +96,10 @@ module Gitlab ...@@ -97,10 +96,10 @@ module Gitlab
def add_event(event_name, tags = {}) def add_event(event_name, tags = {})
event_name = "gitlab_transaction_event_#{event_name}_total".to_sym event_name = "gitlab_transaction_event_#{event_name}_total".to_sym
metric = self.class.prometheus_metric(event_name, :counter) do metric = self.class.prometheus_metric(event_name, :counter) do
base_labels tags label_keys tags.keys
end end
metric.increment(tags.without(*FILTERED_LABELS).merge(labels)) metric.increment(filter_labels(tags))
end end
# Returns a MethodCall object for the given name. # Returns a MethodCall object for the given name.
...@@ -124,13 +123,13 @@ module Gitlab ...@@ -124,13 +123,13 @@ module Gitlab
# #
# transaction.increment(:mestric_name, 1) do # transaction.increment(:mestric_name, 1) do
# docstring 'Custom title' # docstring 'Custom title'
# base_labels { sane: 'yes' } # label_keys %i(sane)
# end # end
# ``` # ```
def increment(name, value = 1, &block) def increment(name, value = 1, labels = {}, &block)
counter = self.class.prometheus_metric(name, :counter, &block) counter = self.class.prometheus_metric(name, :counter, &block)
counter.increment(counter.base_labels&.merge(labels), value) counter.increment(filter_labels(labels), value)
end end
# Set gauge metric # Set gauge metric
...@@ -145,10 +144,10 @@ module Gitlab ...@@ -145,10 +144,10 @@ module Gitlab
# multiprocess_mode :all # multiprocess_mode :all
# end # end
# ``` # ```
def set(name, value, &block) def set(name, value, labels = {}, &block)
gauge = self.class.prometheus_metric(name, :gauge, &block) gauge = self.class.prometheus_metric(name, :gauge, &block)
gauge.set(gauge.base_labels&.merge(labels), value) gauge.set(filter_labels(labels), value)
end end
# Observe histogram metric # Observe histogram metric
...@@ -163,14 +162,20 @@ module Gitlab ...@@ -163,14 +162,20 @@ module Gitlab
# buckets [100, 1000, 10000, 100000, 1000000, 10000000] # buckets [100, 1000, 10000, 100000, 1000000, 10000000]
# end # end
# ``` # ```
def observe(name, value, &block) def observe(name, value, labels = {}, &block)
histogram = self.class.prometheus_metric(name, :histogram, &block) histogram = self.class.prometheus_metric(name, :histogram, &block)
histogram.observe(histogram.base_labels&.merge(labels), value) histogram.observe(filter_labels(labels), value)
end end
def labels def labels
BASE_LABELS BASE_LABEL_KEYS.each_with_object( {} ) do |key, hash|
hash[key] = nil
end
end
def filter_labels(labels)
labels.empty? ? self.labels : labels.without(*FILTERED_LABELS).merge(self.labels)
end end
end end
end end
......
...@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Metrics::BackgroundTransaction do ...@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Metrics::BackgroundTransaction do
end end
it 'contains only the labels defined for metrics' do it 'contains only the labels defined for metrics' do
expect(subject.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABELS.keys) expect(subject.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABEL_KEYS)
end end
it 'includes the feature category if there is one' do it 'includes the feature category if there is one' do
......
...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do ...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do
it 'observes the performance of the supplied block' do it 'observes the performance of the supplied block' do
expect(transaction) expect(transaction)
.to receive(:observe).with(:gitlab_method_call_duration_seconds, be_a_kind_of(Numeric)) .to receive(:observe).with(:gitlab_method_call_duration_seconds, be_a_kind_of(Numeric), { method: "#bar", module: :Foo })
method_call.measure { 'foo' } method_call.measure { 'foo' }
end end
...@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do ...@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Metrics::MethodCall do
it 'observes the performance of the supplied block' do it 'observes the performance of the supplied block' do
expect(transaction) expect(transaction)
.to receive(:observe).with(:gitlab_method_call_duration_seconds, be_a_kind_of(Numeric)) .to receive(:observe).with(:gitlab_method_call_duration_seconds, be_a_kind_of(Numeric), { method: "#bar", module: :Foo })
method_call.measure { 'foo' } method_call.measure { 'foo' }
end end
......
...@@ -135,5 +135,5 @@ RSpec.describe Gitlab::Metrics::Methods do ...@@ -135,5 +135,5 @@ RSpec.describe Gitlab::Metrics::Methods do
include_examples 'metric', :counter, {} include_examples 'metric', :counter, {}
include_examples 'metric', :gauge, {}, :all include_examples 'metric', :gauge, {}, :all
include_examples 'metric', :histogram, {}, [0.005, 0.01, 0.1, 1, 10] include_examples 'metric', :histogram, {}, ::Prometheus::Client::Histogram::DEFAULT_BUCKETS
end end
...@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do ...@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do
expect_next_instance_of(Gitlab::Metrics::BackgroundTransaction) do |transaction| expect_next_instance_of(Gitlab::Metrics::BackgroundTransaction) do |transaction|
expect(transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float)) expect(transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float))
expect(transaction).to receive(:increment).with(:db_count, 1) expect(transaction).to receive(:increment).with(:gitlab_transaction_db_count_total, 1)
end end
middleware.call(worker, message, :test) do middleware.call(worker, message, :test) do
......
...@@ -30,7 +30,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionView do ...@@ -30,7 +30,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionView do
it 'observes view rendering time' do it 'observes view rendering time' do
expect(transaction) expect(transaction)
.to receive(:observe) .to receive(:observe)
.with(:gitlab_view_rendering_duration_seconds, 2.1) .with(:gitlab_view_rendering_duration_seconds, 2.1, { view: "app/views/x.html.haml" })
subscriber.render_template(event) subscriber.render_template(event)
end end
......
...@@ -158,7 +158,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -158,7 +158,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
it 'observes cache metric' do it 'observes cache metric' do
expect(transaction) expect(transaction)
.to receive(:observe) .to receive(:observe)
.with(:gitlab_cache_operation_duration_seconds, event.duration / 1000.0) .with(:gitlab_cache_operation_duration_seconds, event.duration / 1000.0, { operation: :delete })
subscriber.observe(:delete, event.duration) subscriber.observe(:delete, event.duration)
end end
...@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do ...@@ -166,7 +166,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RailsCache do
it 'increments the operations total' do it 'increments the operations total' do
expect(transaction) expect(transaction)
.to receive(:increment) .to receive(:increment)
.with(:gitlab_cache_operations_total, 1) .with(:gitlab_cache_operations_total, 1, { operation: :delete })
subscriber.observe(:delete, event.duration) subscriber.observe(:delete, event.duration)
end end
......
...@@ -116,17 +116,17 @@ RSpec.describe Gitlab::Metrics::Transaction do ...@@ -116,17 +116,17 @@ RSpec.describe Gitlab::Metrics::Transaction do
expect(::Gitlab::Metrics).to receive(:counter).with(:block_labels, 'Block labels counter', hash_including(:controller, :action, :sane)).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:counter).with(:block_labels, 'Block labels counter', hash_including(:controller, :action, :sane)).and_return(prometheus_metric)
labels = { sane: 'yes' } labels = { sane: 'yes' }
transaction.increment(:block_labels, 1) do transaction.increment(:block_labels, 1, labels) do
base_labels labels label_keys %i(sane)
end end
end end
it 'filters sensitive tags' do it 'filters sensitive tags' do
expect(::Gitlab::Metrics).to receive(:counter).with(:metric_with_sensitive_block, 'Metric with sensitive block counter', hash_excluding(sensitive_tags)).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:counter).with(:metric_with_sensitive_block, 'Metric with sensitive block counter', hash_excluding(sensitive_tags)).and_return(prometheus_metric)
labels = sensitive_tags labels_keys = sensitive_tags.keys
transaction.increment(:metric_with_sensitive_block, 1) do transaction.increment(:metric_with_sensitive_block, 1, sensitive_tags) do
base_labels labels label_keys labels_keys
end end
end end
end end
...@@ -155,17 +155,17 @@ RSpec.describe Gitlab::Metrics::Transaction do ...@@ -155,17 +155,17 @@ RSpec.describe Gitlab::Metrics::Transaction do
expect(::Gitlab::Metrics).to receive(:gauge).with(:block_labels_set, 'Block labels set gauge', hash_including(:controller, :action, :sane), :livesum).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:gauge).with(:block_labels_set, 'Block labels set gauge', hash_including(:controller, :action, :sane), :livesum).and_return(prometheus_metric)
labels = { sane: 'yes' } labels = { sane: 'yes' }
transaction.set(:block_labels_set, 1) do transaction.set(:block_labels_set, 1, labels) do
base_labels labels label_keys %i(sane)
end end
end end
it 'filters sensitive tags' do it 'filters sensitive tags' do
expect(::Gitlab::Metrics).to receive(:gauge).with(:metric_set_with_sensitive_block, 'Metric set with sensitive block gauge', hash_excluding(sensitive_tags), :livesum).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:gauge).with(:metric_set_with_sensitive_block, 'Metric set with sensitive block gauge', hash_excluding(sensitive_tags), :livesum).and_return(prometheus_metric)
labels = sensitive_tags label_keys = sensitive_tags.keys
transaction.set(:metric_set_with_sensitive_block, 1) do transaction.set(:metric_set_with_sensitive_block, 1, sensitive_tags) do
base_labels labels label_keys label_keys
end end
end end
end end
...@@ -194,17 +194,17 @@ RSpec.describe Gitlab::Metrics::Transaction do ...@@ -194,17 +194,17 @@ RSpec.describe Gitlab::Metrics::Transaction do
expect(::Gitlab::Metrics).to receive(:histogram).with(:block_labels_observe, 'Block labels observe histogram', hash_including(:controller, :action, :sane), kind_of(Array)).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:histogram).with(:block_labels_observe, 'Block labels observe histogram', hash_including(:controller, :action, :sane), kind_of(Array)).and_return(prometheus_metric)
labels = { sane: 'yes' } labels = { sane: 'yes' }
transaction.observe(:block_labels_observe, 1) do transaction.observe(:block_labels_observe, 1, labels) do
base_labels labels label_keys %i(sane)
end end
end end
it 'filters sensitive tags' do it 'filters sensitive tags' do
expect(::Gitlab::Metrics).to receive(:histogram).with(:metric_observe_with_sensitive_block, 'Metric observe with sensitive block histogram', hash_excluding(sensitive_tags), kind_of(Array)).and_return(prometheus_metric) expect(::Gitlab::Metrics).to receive(:histogram).with(:metric_observe_with_sensitive_block, 'Metric observe with sensitive block histogram', hash_excluding(sensitive_tags), kind_of(Array)).and_return(prometheus_metric)
labels = sensitive_tags label_keys = sensitive_tags.keys
transaction.observe(:metric_observe_with_sensitive_block, 1) do transaction.observe(:metric_observe_with_sensitive_block, 1, sensitive_tags) do
base_labels labels label_keys label_keys
end end
end end
end end
......
...@@ -100,7 +100,7 @@ RSpec.describe Gitlab::Metrics::WebTransaction do ...@@ -100,7 +100,7 @@ RSpec.describe Gitlab::Metrics::WebTransaction do
end end
it 'contains only the labels defined for transactions' do it 'contains only the labels defined for transactions' do
expect(transaction.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABELS.keys) expect(transaction.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABEL_KEYS)
end end
it 'does not provide labels if route infos are missing' do it 'does not provide labels if route infos are missing' do
...@@ -121,7 +121,7 @@ RSpec.describe Gitlab::Metrics::WebTransaction do ...@@ -121,7 +121,7 @@ RSpec.describe Gitlab::Metrics::WebTransaction do
end end
it 'contains only the labels defined for transactions' do it 'contains only the labels defined for transactions' do
expect(transaction.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABELS.keys) expect(transaction.labels.keys).to contain_exactly(*described_class.superclass::BASE_LABEL_KEYS)
end end
context 'when the request content type is not :html' do context 'when the request content type is not :html' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment