From 03b38a4a8dd5ba01043264df31b7f5faf6217e1b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 10 Aug 2017 16:18:37 +0200 Subject: Add GC sampler and small refactor of samplers --- lib/gitlab/metrics/base_sampler.rb | 63 --------------- lib/gitlab/metrics/influx_sampler.rb | 101 ------------------------ lib/gitlab/metrics/samplers/base_sampler.rb | 96 +++++++++++++++++++++++ lib/gitlab/metrics/samplers/influx_sampler.rb | 103 +++++++++++++++++++++++++ lib/gitlab/metrics/samplers/ruby_sampler.rb | 89 +++++++++++++++++++++ lib/gitlab/metrics/samplers/unicorn_sampler.rb | 50 ++++++++++++ lib/gitlab/metrics/unicorn_sampler.rb | 48 ------------ 7 files changed, 338 insertions(+), 212 deletions(-) delete mode 100644 lib/gitlab/metrics/base_sampler.rb delete mode 100644 lib/gitlab/metrics/influx_sampler.rb create mode 100644 lib/gitlab/metrics/samplers/base_sampler.rb create mode 100644 lib/gitlab/metrics/samplers/influx_sampler.rb create mode 100644 lib/gitlab/metrics/samplers/ruby_sampler.rb create mode 100644 lib/gitlab/metrics/samplers/unicorn_sampler.rb delete mode 100644 lib/gitlab/metrics/unicorn_sampler.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/base_sampler.rb b/lib/gitlab/metrics/base_sampler.rb deleted file mode 100644 index 716d20bb91a..00000000000 --- a/lib/gitlab/metrics/base_sampler.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'logger' -module Gitlab - module Metrics - class BaseSampler < Daemon - # interval - The sampling interval in seconds. - def initialize(interval) - interval_half = interval.to_f / 2 - - @interval = interval - @interval_steps = (-interval_half..interval_half).step(0.1).to_a - - super() - end - - def safe_sample - sample - rescue => e - Rails.logger.warn("#{self.class}: #{e}, stopping") - stop - end - - def sample - raise NotImplementedError - end - - # Returns the sleep interval with a random adjustment. - # - # The random adjustment is put in place to ensure we: - # - # 1. Don't generate samples at the exact same interval every time (thus - # potentially missing anything that happens in between samples). - # 2. Don't sample data at the same interval two times in a row. - def sleep_interval - while (step = @interval_steps.sample) - if step != @last_step - @last_step = step - - return @interval + @last_step - end - end - end - - private - - attr_reader :running - - def start_working - @running = true - sleep(sleep_interval) - - while running - safe_sample - - sleep(sleep_interval) - end - end - - def stop_working - @running = false - end - end - end -end diff --git a/lib/gitlab/metrics/influx_sampler.rb b/lib/gitlab/metrics/influx_sampler.rb deleted file mode 100644 index 6db1dd755b7..00000000000 --- a/lib/gitlab/metrics/influx_sampler.rb +++ /dev/null @@ -1,101 +0,0 @@ -module Gitlab - module Metrics - # Class that sends certain metrics to InfluxDB at a specific interval. - # - # This class is used to gather statistics that can't be directly associated - # with a transaction such as system memory usage, garbage collection - # statistics, etc. - class InfluxSampler < BaseSampler - # interval - The sampling interval in seconds. - def initialize(interval = Metrics.settings[:sample_interval]) - super(interval) - @last_step = nil - - @metrics = [] - - @last_minor_gc = Delta.new(GC.stat[:minor_gc_count]) - @last_major_gc = Delta.new(GC.stat[:major_gc_count]) - - if Gitlab::Metrics.mri? - require 'allocations' - - Allocations.start - end - end - - def sample - sample_memory_usage - sample_file_descriptors - sample_objects - sample_gc - - flush - ensure - GC::Profiler.clear - @metrics.clear - end - - def flush - Metrics.submit_metrics(@metrics.map(&:to_hash)) - end - - def sample_memory_usage - add_metric('memory_usage', value: System.memory_usage) - end - - def sample_file_descriptors - add_metric('file_descriptors', value: System.file_descriptor_count) - end - - if Metrics.mri? - def sample_objects - sample = Allocations.to_hash - counts = sample.each_with_object({}) do |(klass, count), hash| - name = klass.name - - next unless name - - hash[name] = count - end - - # Symbols aren't allocated so we'll need to add those manually. - counts['Symbol'] = Symbol.all_symbols.length - - counts.each do |name, count| - add_metric('object_counts', { count: count }, type: name) - end - end - else - def sample_objects - end - end - - def sample_gc - time = GC::Profiler.total_time * 1000.0 - stats = GC.stat.merge(total_time: time) - - # We want the difference of GC runs compared to the last sample, not the - # total amount since the process started. - stats[:minor_gc_count] = - @last_minor_gc.compared_with(stats[:minor_gc_count]) - - stats[:major_gc_count] = - @last_major_gc.compared_with(stats[:major_gc_count]) - - stats[:count] = stats[:minor_gc_count] + stats[:major_gc_count] - - add_metric('gc_statistics', stats) - end - - def add_metric(series, values, tags = {}) - prefix = sidekiq? ? 'sidekiq_' : 'rails_' - - @metrics << Metric.new("#{prefix}#{series}", values, tags) - end - - def sidekiq? - Sidekiq.server? - end - end - end -end diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb new file mode 100644 index 00000000000..f911df3d170 --- /dev/null +++ b/lib/gitlab/metrics/samplers/base_sampler.rb @@ -0,0 +1,96 @@ +require 'logger' +module Gitlab + module Metrics + module Samplers + class BaseSampler + def self.initialize_instance(*args) + raise "#{name} singleton instance already initialized" if @instance + @instance = new(*args) + at_exit(&@instance.method(:stop)) + @instance + end + + def self.instance + @instance + end + + attr_reader :running + + # interval - The sampling interval in seconds. + def initialize(interval) + interval_half = interval.to_f / 2 + + @interval = interval + @interval_steps = (-interval_half..interval_half).step(0.1).to_a + + @mutex = Mutex.new + end + + def enabled? + true + end + + def start + return unless enabled? + + @mutex.synchronize do + return if running + @running = true + + @thread = Thread.new do + sleep(sleep_interval) + + while running + safe_sample + + sleep(sleep_interval) + end + end + end + end + + def stop + @mutex.synchronize do + return unless running + + @running = false + + if @thread + @thread.wakeup if @thread.alive? + @thread.join + @thread = nil + end + end + end + + def safe_sample + sample + rescue => e + Rails.logger.warn("#{self.class}: #{e}, stopping") + stop + end + + def sample + raise NotImplementedError + end + + # Returns the sleep interval with a random adjustment. + # + # The random adjustment is put in place to ensure we: + # + # 1. Don't generate samples at the exact same interval every time (thus + # potentially missing anything that happens in between samples). + # 2. Don't sample data at the same interval two times in a row. + def sleep_interval + while step = @interval_steps.sample + if step != @last_step + @last_step = step + + return @interval + @last_step + end + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/gitlab/metrics/samplers/influx_sampler.rb b/lib/gitlab/metrics/samplers/influx_sampler.rb new file mode 100644 index 00000000000..66adc47dcc3 --- /dev/null +++ b/lib/gitlab/metrics/samplers/influx_sampler.rb @@ -0,0 +1,103 @@ +module Gitlab + module Metrics + module Samplers + # Class that sends certain metrics to InfluxDB at a specific interval. + # + # This class is used to gather statistics that can't be directly associated + # with a transaction such as system memory usage, garbage collection + # statistics, etc. + class InfluxSampler < BaseSampler + # interval - The sampling interval in seconds. + def initialize(interval = Metrics.settings[:sample_interval]) + super(interval) + @last_step = nil + + @metrics = [] + + @last_minor_gc = Delta.new(GC.stat[:minor_gc_count]) + @last_major_gc = Delta.new(GC.stat[:major_gc_count]) + + if Gitlab::Metrics.mri? + require 'allocations' + + Allocations.start + end + end + + def sample + sample_memory_usage + sample_file_descriptors + sample_objects + sample_gc + + flush + ensure + GC::Profiler.clear + @metrics.clear + end + + def flush + Metrics.submit_metrics(@metrics.map(&:to_hash)) + end + + def sample_memory_usage + add_metric('memory_usage', value: System.memory_usage) + end + + def sample_file_descriptors + add_metric('file_descriptors', value: System.file_descriptor_count) + end + + if Metrics.mri? + def sample_objects + sample = Allocations.to_hash + counts = sample.each_with_object({}) do |(klass, count), hash| + name = klass.name + + next unless name + + hash[name] = count + end + + # Symbols aren't allocated so we'll need to add those manually. + counts['Symbol'] = Symbol.all_symbols.length + + counts.each do |name, count| + add_metric('object_counts', { count: count }, type: name) + end + end + else + def sample_objects + end + end + + def sample_gc + time = GC::Profiler.total_time * 1000.0 + stats = GC.stat.merge(total_time: time) + + # We want the difference of GC runs compared to the last sample, not the + # total amount since the process started. + stats[:minor_gc_count] = + @last_minor_gc.compared_with(stats[:minor_gc_count]) + + stats[:major_gc_count] = + @last_major_gc.compared_with(stats[:major_gc_count]) + + stats[:count] = stats[:minor_gc_count] + stats[:major_gc_count] + + add_metric('gc_statistics', stats) + end + + def add_metric(series, values, tags = {}) + prefix = sidekiq? ? 'sidekiq_' : 'rails_' + + @metrics << Metric.new("#{prefix}#{series}", values, tags) + end + + def sidekiq? + Sidekiq.server? + end + end + end + end +end \ No newline at end of file diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb new file mode 100644 index 00000000000..455085731a8 --- /dev/null +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -0,0 +1,89 @@ +module Gitlab + module Metrics + module Samplers + class RubySampler < BaseSampler + + COUNTS = [:count, :minor_gc_count, :major_gc_count] + + def metrics + @metrics ||= init_metrics + end + + def with_prefix(name) + "ruby_gc_#{name}".to_sym + end + + def to_doc_string(name) + name.to_s.humanize + end + + def labels + worker_label.merge(source_label) + end + + def initialize(interval) + super(interval) + GC::Profiler.enable + Rails.logger.info("123") + + init_metrics + end + + def init_metrics + metrics = {} + metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:total_time), to_doc_string(:total_time), labels, :livesum) + GC.stat.keys.each do |key| + metrics[key] = Gitlab::Metrics.gauge(with_prefix(key), to_doc_string(key), labels, :livesum) + end + metrics + end + + def sample + metrics[:total_time].set(labels, GC::Profiler.total_time * 1000) + + GC.stat.each do |key, value| + metrics[key].set(labels, value) + end + end + + def source_label + if Sidekiq.server? + { source: 'sidekiq' } + else + { source: 'rails' } + end + end + + def worker_label + return {} unless defined?(Unicorn::Worker) + worker = if defined?(Unicorn::Worker) + ObjectSpace.each_object(Unicorn::Worker)&.first + end + if worker + { unicorn: worker.nr } + else + { unicorn: 'master' } + end + end + + def sample_gc + time = GC::Profiler.total_time * 1000.0 + stats = GC.stat.merge(total_time: time) + + # We want the difference of GC runs compared to the last sample, not the + # total amount since the process started. + + stats[:minor_gc_count] = + @last_minor_gc.compared_with(stats[:minor_gc_count]) + + stats[:major_gc_count] = + @last_major_gc.compared_with(stats[:major_gc_count]) + + stats[:count] = stats[:minor_gc_count] + stats[:major_gc_count] + + add_metric('gc_statistics', stats) + end + end + end + end +end \ No newline at end of file diff --git a/lib/gitlab/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb new file mode 100644 index 00000000000..686489a07a8 --- /dev/null +++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb @@ -0,0 +1,50 @@ +module Gitlab + module Metrics + module Samplers + class UnicornSampler < BaseSampler + def initialize(interval) + super(interval) + end + + def unicorn_active_connections + @unicorn_active_connections ||= Gitlab::Metrics.gauge(:unicorn_active_connections, 'Unicorn active connections', {}, :max) + end + + def unicorn_queued_connections + @unicorn_queued_connections ||= Gitlab::Metrics.gauge(:unicorn_queued_connections, 'Unicorn queued connections', {}, :max) + end + + def enabled? + # Raindrops::Linux.tcp_listener_stats is only present on Linux + unicorn_with_listeners? && Raindrops::Linux.respond_to?(:tcp_listener_stats) + end + + def sample + Raindrops::Linux.tcp_listener_stats(tcp_listeners).each do |addr, stats| + unicorn_active_connections.set({ type: 'tcp', address: addr }, stats.active) + unicorn_queued_connections.set({ type: 'tcp', address: addr }, stats.queued) + end + + Raindrops::Linux.unix_listener_stats(unix_listeners).each do |addr, stats| + unicorn_active_connections.set({ type: 'unix', address: addr }, stats.active) + unicorn_queued_connections.set({ type: 'unix', address: addr }, stats.queued) + end + end + + private + + def tcp_listeners + @tcp_listeners ||= Unicorn.listener_names.grep(%r{\A[^/]+:\d+\z}) + end + + def unix_listeners + @unix_listeners ||= Unicorn.listener_names - tcp_listeners + end + + def unicorn_with_listeners? + defined?(Unicorn) && Unicorn.listener_names.any? + end + end + end + end +end \ No newline at end of file diff --git a/lib/gitlab/metrics/unicorn_sampler.rb b/lib/gitlab/metrics/unicorn_sampler.rb deleted file mode 100644 index f6987252039..00000000000 --- a/lib/gitlab/metrics/unicorn_sampler.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Gitlab - module Metrics - class UnicornSampler < BaseSampler - def initialize(interval) - super(interval) - end - - def unicorn_active_connections - @unicorn_active_connections ||= Gitlab::Metrics.gauge(:unicorn_active_connections, 'Unicorn active connections', {}, :max) - end - - def unicorn_queued_connections - @unicorn_queued_connections ||= Gitlab::Metrics.gauge(:unicorn_queued_connections, 'Unicorn queued connections', {}, :max) - end - - def enabled? - # Raindrops::Linux.tcp_listener_stats is only present on Linux - unicorn_with_listeners? && Raindrops::Linux.respond_to?(:tcp_listener_stats) - end - - def sample - Raindrops::Linux.tcp_listener_stats(tcp_listeners).each do |addr, stats| - unicorn_active_connections.set({ type: 'tcp', address: addr }, stats.active) - unicorn_queued_connections.set({ type: 'tcp', address: addr }, stats.queued) - end - - Raindrops::Linux.unix_listener_stats(unix_listeners).each do |addr, stats| - unicorn_active_connections.set({ type: 'unix', address: addr }, stats.active) - unicorn_queued_connections.set({ type: 'unix', address: addr }, stats.queued) - end - end - - private - - def tcp_listeners - @tcp_listeners ||= Unicorn.listener_names.grep(%r{\A[^/]+:\d+\z}) - end - - def unix_listeners - @unix_listeners ||= Unicorn.listener_names - tcp_listeners - end - - def unicorn_with_listeners? - defined?(Unicorn) && Unicorn.listener_names.any? - end - end - end -end -- cgit v1.2.3 From 0bfe79019d1743407c7df59d1f44cc470dd48e4b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 11 Aug 2017 22:00:06 +0200 Subject: Add samples total and cleanup --- lib/gitlab/metrics/samplers/ruby_sampler.rb | 79 +++++++++++++++++++---------- 1 file changed, 53 insertions(+), 26 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index 455085731a8..fc7870eb3f8 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -2,15 +2,14 @@ module Gitlab module Metrics module Samplers class RubySampler < BaseSampler - COUNTS = [:count, :minor_gc_count, :major_gc_count] def metrics @metrics ||= init_metrics end - def with_prefix(name) - "ruby_gc_#{name}".to_sym + def with_prefix(prefix, name) + "ruby_#{prefix}_#{name}".to_sym end def to_doc_string(name) @@ -23,22 +22,40 @@ module Gitlab def initialize(interval) super(interval) - GC::Profiler.enable - Rails.logger.info("123") - init_metrics + if Gitlab::Metrics.mri? + require 'allocations' + + Allocations.start + end end def init_metrics metrics = {} - metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:total_time), to_doc_string(:total_time), labels, :livesum) + metrics[:samples_total] = Gitlab::Metrics.counter(with_prefix(:sampler, :total), 'Total count of samples') + metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum) GC.stat.keys.each do |key| - metrics[key] = Gitlab::Metrics.gauge(with_prefix(key), to_doc_string(key), labels, :livesum) + metrics[key] = Gitlab::Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum) end + + metrics[:objects_total] = Gitlab::Metrics.gauge(with_prefix(:objects, :total), 'Objects total', labels.merge(class: nil), :livesum) + metrics end def sample + + metrics[:samples_total].increment(labels) + sample_gc + sample_objects + rescue => ex + puts ex + + end + + private + + def sample_gc metrics[:total_time].set(labels, GC::Profiler.total_time * 1000) GC.stat.each do |key, value| @@ -46,6 +63,34 @@ module Gitlab end end + def sample_objects + ss_objects.each do |name, count| + metrics[:objects_total].set(labels.merge(class: name), count) + end + end + + if Metrics.mri? + def ss_objects + sample = Allocations.to_hash + counts = sample.each_with_object({}) do |(klass, count), hash| + name = klass.name + + next unless name + + hash[name] = count + end + + # Symbols aren't allocated so we'll need to add those manually. + counts['Symbol'] = Symbol.all_symbols.length + counts + end + else + def ss_objects + + end + end + + def source_label if Sidekiq.server? { source: 'sidekiq' } @@ -65,24 +110,6 @@ module Gitlab { unicorn: 'master' } end end - - def sample_gc - time = GC::Profiler.total_time * 1000.0 - stats = GC.stat.merge(total_time: time) - - # We want the difference of GC runs compared to the last sample, not the - # total amount since the process started. - - stats[:minor_gc_count] = - @last_minor_gc.compared_with(stats[:minor_gc_count]) - - stats[:major_gc_count] = - @last_major_gc.compared_with(stats[:major_gc_count]) - - stats[:count] = stats[:minor_gc_count] + stats[:major_gc_count] - - add_metric('gc_statistics', stats) - end end end end -- cgit v1.2.3 From 4c04444e22f79cbe73d4fa4a6a41c5884c26855f Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 16 Aug 2017 13:44:21 +0200 Subject: Finished Ruby Sampler + Cleanup sampler, use latest Prometheus gem --- lib/gitlab/metrics/samplers/ruby_sampler.rb | 38 +++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index fc7870eb3f8..e61356bb460 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -1,9 +1,9 @@ +require 'prometheus/client/support/unicorn' + module Gitlab module Metrics module Samplers class RubySampler < BaseSampler - COUNTS = [:count, :minor_gc_count, :major_gc_count] - def metrics @metrics ||= init_metrics end @@ -32,25 +32,30 @@ module Gitlab def init_metrics metrics = {} - metrics[:samples_total] = Gitlab::Metrics.counter(with_prefix(:sampler, :total), 'Total count of samples') + metrics[:sampler_duration] = Gitlab::Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', source_label) metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum) GC.stat.keys.each do |key| metrics[key] = Gitlab::Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum) end metrics[:objects_total] = Gitlab::Metrics.gauge(with_prefix(:objects, :total), 'Objects total', labels.merge(class: nil), :livesum) + metrics[:memory_usage] = Gitlab::Metrics.gauge(with_prefix(:memory, :usage_total), 'Memory used total', labels, :livesum) + metrics[:file_descriptors] = Gitlab::Metrics.gauge(with_prefix(:file, :descriptors_total), 'File descriptors total', labels, :livesum) metrics end def sample - - metrics[:samples_total].increment(labels) + start_time = System.monotonic_time sample_gc sample_objects - rescue => ex - puts ex + metrics[:memory_usage].set(labels, System.memory_usage) + metrics[:file_descriptors].set(labels, System.file_descriptor_count) + + metrics[:sampler_duration].observe(source_label, (System.monotonic_time - start_time) / 1000.0) + ensure + GC::Profiler.clear end private @@ -64,13 +69,13 @@ module Gitlab end def sample_objects - ss_objects.each do |name, count| + list_objects.each do |name, count| metrics[:objects_total].set(labels.merge(class: name), count) end end if Metrics.mri? - def ss_objects + def list_objects sample = Allocations.to_hash counts = sample.each_with_object({}) do |(klass, count), hash| name = klass.name @@ -85,12 +90,10 @@ module Gitlab counts end else - def ss_objects - + def list_objects end end - def source_label if Sidekiq.server? { source: 'sidekiq' } @@ -101,11 +104,10 @@ module Gitlab def worker_label return {} unless defined?(Unicorn::Worker) - worker = if defined?(Unicorn::Worker) - ObjectSpace.each_object(Unicorn::Worker)&.first - end - if worker - { unicorn: worker.nr } + worker_no = ::Prometheus::Client::Support::Unicorn.worker_id + + if worker_no + { unicorn: worker_no } else { unicorn: 'master' } end @@ -113,4 +115,4 @@ module Gitlab end end end -end \ No newline at end of file +end -- cgit v1.2.3 From 3b1464803bd9b72eb00ca9e70623a647550794cf Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 22 Aug 2017 16:13:51 +0200 Subject: Transaction and method instrumentation --- lib/gitlab/metrics/method_call.rb | 44 +++++++++++++++++++++++++---- lib/gitlab/metrics/samplers/ruby_sampler.rb | 12 ++------ lib/gitlab/metrics/transaction.rb | 28 +++++++++++------- 3 files changed, 58 insertions(+), 26 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index d3465e5ec19..2c650a987f1 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -7,13 +7,28 @@ module Gitlab # name - The full name of the method (including namespace) such as # `User#sign_in`. # - # series - The series to use for storing the data. - def initialize(name, series) + def self.call_real_duration_histogram + @call_real_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_real_duration_milliseconds, + 'Method calls real duration', + {}, + [1, 2, 5, 10, 20, 50, 100, 1000]) + + end + + def self.call_cpu_duration_histogram + @call_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_cpu_duration_milliseconds, + 'Method calls cpu duration', + {}, + [1, 2, 5, 10, 20, 50, 100, 1000]) + end + + + def initialize(name, tags = {}) @name = name - @series = series @real_time = 0 @cpu_time = 0 @call_count = 0 + @tags = tags end # Measures the real and CPU execution time of the supplied block. @@ -26,17 +41,34 @@ module Gitlab @cpu_time += System.cpu_time - start_cpu @call_count += 1 + if above_threshold? + self.class.call_real_duration_histogram.observe(labels, @real_time) + self.class.call_cpu_duration_histogram.observe(labels, @cpu_time) + end + retval end + def labels + @labels ||= @tags.merge(source_label).merge({ call_name: @name }) + end + + def source_label + if Sidekiq.server? + { source: 'sidekiq' } + else + { source: 'rails' } + end + end + # Returns a Metric instance of the current method call. def to_metric Metric.new( - @series, + Instrumentation.series, { - duration: real_time, + duration: real_time, cpu_duration: cpu_time, - call_count: call_count + call_count: call_count }, method: @name ) diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index e61356bb460..61eb6e7b541 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -17,7 +17,7 @@ module Gitlab end def labels - worker_label.merge(source_label) + {} end def initialize(interval) @@ -53,7 +53,7 @@ module Gitlab metrics[:memory_usage].set(labels, System.memory_usage) metrics[:file_descriptors].set(labels, System.file_descriptor_count) - metrics[:sampler_duration].observe(source_label, (System.monotonic_time - start_time) / 1000.0) + metrics[:sampler_duration].observe(labels.merge(worker_label), (System.monotonic_time - start_time) / 1000.0) ensure GC::Profiler.clear end @@ -94,14 +94,6 @@ module Gitlab end end - def source_label - if Sidekiq.server? - { source: 'sidekiq' } - else - { source: 'rails' } - end - end - def worker_label return {} unless defined?(Unicorn::Worker) worker_no = ::Prometheus::Client::Support::Unicorn.worker_id diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 4f9fb1c7853..2a4578aa6e4 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -21,15 +21,15 @@ module Gitlab @metrics = [] @methods = {} - @started_at = nil + @started_at = nil @finished_at = nil @values = Hash.new(0) - @tags = {} + @tags = {} @action = action @memory_before = 0 - @memory_after = 0 + @memory_after = 0 end def duration @@ -44,12 +44,17 @@ module Gitlab Thread.current[THREAD_KEY] = self @memory_before = System.memory_usage - @started_at = System.monotonic_time + @started_at = System.monotonic_time yield ensure @memory_after = System.memory_usage - @finished_at = System.monotonic_time + @finished_at = System.monotonic_time + + Gitlab::Metrics.histogram("gitlab_method_duration_seconds".to_sym, "Method duration seconds", @tags).observe({}, ) + + self.class.prometheus_gauge(:duration).set(@tags, duration) + self.class.prometheus_gauge(:allocated_memory).set(@tags, allocated_memory) Thread.current[THREAD_KEY] = nil end @@ -66,16 +71,14 @@ module Gitlab # 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 = {}) - @metrics << Metric.new(EVENT_SERIES, - { count: 1 }, - { event: event_name }.merge(tags), - :event) + Gitlab::Metrics.counter("gitlab_event_#{event_name}".to_sym, "Event #{event_name}", tags).increment({}) + @metrics << Metric.new(EVENT_SERIES, { count: 1 }, labels, :event) end # Returns a MethodCall object for the given name. def method_call_for(name) unless method = @methods[name] - @methods[name] = method = MethodCall.new(name, Instrumentation.series) + @methods[name] = method = MethodCall.new(name) end method @@ -103,11 +106,16 @@ module Gitlab @values.each do |name, value| values[name] = value + self.class.prometheus_gauge(name).set(@tags, value) end add_metric('transactions', values, @tags) end + def self.prometheus_gauge(name) + Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Gitlab Transaction #{name}") + end + def submit submit = @metrics.dup -- cgit v1.2.3 From f464adaaf6e463e148a1b74d8f1f8280a49594c3 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 29 Aug 2017 14:01:37 +0200 Subject: Remove transaction tags and map transaction metrics to prometheus + clean transaction metrics + Gemfile.lock file update --- lib/gitlab/metrics/influx_db.rb | 10 ---------- lib/gitlab/metrics/transaction.rb | 39 ++++++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 25 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index 7b06bb953aa..1e3db4eb8b4 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -109,16 +109,6 @@ module Gitlab retval end - # Adds a tag to the current transaction (if any) - # - # name - The name of the tag to add. - # value - The value of the tag. - def tag_transaction(name, value) - trans = current_transaction - - trans&.add_tag(name, value) - end - # Sets the action of the current transaction (if any) # # action - The name of the action. diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 2a4578aa6e4..da1f44b1eab 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -40,6 +40,24 @@ module Gitlab @memory_after - @memory_before end + def self.metric_transaction_duration_milliseconds + @metrics_transaction_duration_milliseconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_milliseconds, + 'Method duration milliseconds', + {}, + [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000] + ) + end + + def self.metric_transaction_allocated_memory_bytes + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, + 'Method duration milliseconds', + {}, + [1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 5000000] + ) + end + def run Thread.current[THREAD_KEY] = self @@ -51,10 +69,8 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - Gitlab::Metrics.histogram("gitlab_method_duration_seconds".to_sym, "Method duration seconds", @tags).observe({}, ) - - self.class.prometheus_gauge(:duration).set(@tags, duration) - self.class.prometheus_gauge(:allocated_memory).set(@tags, allocated_memory) + self.class.metric_transaction_duration_milliseconds.observe({}, duration) + self.class.metric_transaction_allocated_memory_bytes.observe({}, allocated_memory) Thread.current[THREAD_KEY] = nil end @@ -71,8 +87,8 @@ module Gitlab # 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 = {}) - Gitlab::Metrics.counter("gitlab_event_#{event_name}".to_sym, "Event #{event_name}", tags).increment({}) - @metrics << Metric.new(EVENT_SERIES, { count: 1 }, labels, :event) + Gitlab::Metrics.counter("gitlab_transaction_event_#{event_name}_total".to_sym, "Transaction event #{event_name}", tags).increment({}) + @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags, :event) end # Returns a MethodCall object for the given name. @@ -85,17 +101,15 @@ module Gitlab end def increment(name, value) + Gitlab::Metrics.counter("gitlab_transaction_#{name}_total".to_sym, "Transaction counter #{name}", {}).increment({}, value) @values[name] += value end def set(name, value) + Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", {}, :livesum).set({}, value) @values[name] = value end - def add_tag(key, value) - @tags[key] = value - end - def finish track_self submit @@ -106,16 +120,11 @@ module Gitlab @values.each do |name, value| values[name] = value - self.class.prometheus_gauge(name).set(@tags, value) end add_metric('transactions', values, @tags) end - def self.prometheus_gauge(name) - Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Gitlab Transaction #{name}") - end - def submit submit = @metrics.dup -- cgit v1.2.3 From b6d75b29551e250f853b1a85919c677ecd85ac73 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 4 Sep 2017 22:14:54 +0200 Subject: remove common Base Sampler code --- lib/gitlab/metrics/samplers/base_sampler.rb | 71 ++++++++--------------------- 1 file changed, 18 insertions(+), 53 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb index f911df3d170..778cf304e49 100644 --- a/lib/gitlab/metrics/samplers/base_sampler.rb +++ b/lib/gitlab/metrics/samplers/base_sampler.rb @@ -2,20 +2,7 @@ require 'logger' module Gitlab module Metrics module Samplers - class BaseSampler - def self.initialize_instance(*args) - raise "#{name} singleton instance already initialized" if @instance - @instance = new(*args) - at_exit(&@instance.method(:stop)) - @instance - end - - def self.instance - @instance - end - - attr_reader :running - + class BaseSampler < Daemon # interval - The sampling interval in seconds. def initialize(interval) interval_half = interval.to_f / 2 @@ -23,44 +10,7 @@ module Gitlab @interval = interval @interval_steps = (-interval_half..interval_half).step(0.1).to_a - @mutex = Mutex.new - end - - def enabled? - true - end - - def start - return unless enabled? - - @mutex.synchronize do - return if running - @running = true - - @thread = Thread.new do - sleep(sleep_interval) - - while running - safe_sample - - sleep(sleep_interval) - end - end - end - end - - def stop - @mutex.synchronize do - return unless running - - @running = false - - if @thread - @thread.wakeup if @thread.alive? - @thread.join - @thread = nil - end - end + super() end def safe_sample @@ -90,7 +40,22 @@ module Gitlab end end end + + private + + def start_working + @running = true + sleep(sleep_interval) + while running + safe_sample + end + end + + def stop_working + @running = false + end end end end -end \ No newline at end of file +end + -- cgit v1.2.3 From 3cc28601f37c11e444362495f27d39aee3d7aaca Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 5 Sep 2017 01:34:28 +0200 Subject: Cleanup sampling code and fix bug with samplers running without sleep --- lib/gitlab/metrics/method_call.rb | 23 +++++------------------ lib/gitlab/metrics/samplers/base_sampler.rb | 4 ++++ lib/gitlab/metrics/samplers/ruby_sampler.rb | 2 +- lib/gitlab/metrics/transaction.rb | 18 +++++++++--------- 4 files changed, 19 insertions(+), 28 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 2c650a987f1..fb652cbeb4f 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -10,7 +10,7 @@ module Gitlab def self.call_real_duration_histogram @call_real_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_real_duration_milliseconds, 'Method calls real duration', - {}, + {call_name: nil}, [1, 2, 5, 10, 20, 50, 100, 1000]) end @@ -18,17 +18,16 @@ module Gitlab def self.call_cpu_duration_histogram @call_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_cpu_duration_milliseconds, 'Method calls cpu duration', - {}, + {call_name: nil}, [1, 2, 5, 10, 20, 50, 100, 1000]) end - def initialize(name, tags = {}) + def initialize(name) @name = name @real_time = 0 @cpu_time = 0 @call_count = 0 - @tags = tags end # Measures the real and CPU execution time of the supplied block. @@ -42,25 +41,13 @@ module Gitlab @call_count += 1 if above_threshold? - self.class.call_real_duration_histogram.observe(labels, @real_time) - self.class.call_cpu_duration_histogram.observe(labels, @cpu_time) + self.class.call_real_duration_histogram.observe({ call_name: @name }, @real_time) + self.class.call_cpu_duration_histogram.observe({ call_name: @name }, @cpu_time) end retval end - def labels - @labels ||= @tags.merge(source_label).merge({ call_name: @name }) - end - - def source_label - if Sidekiq.server? - { source: 'sidekiq' } - else - { source: 'rails' } - end - end - # Returns a Metric instance of the current method call. def to_metric Metric.new( diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb index 778cf304e49..50441b514b2 100644 --- a/lib/gitlab/metrics/samplers/base_sampler.rb +++ b/lib/gitlab/metrics/samplers/base_sampler.rb @@ -1,4 +1,5 @@ require 'logger' + module Gitlab module Metrics module Samplers @@ -43,11 +44,14 @@ module Gitlab private + attr_reader :running + def start_working @running = true sleep(sleep_interval) while running safe_sample + sleep(sleep_interval) end end diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index 61eb6e7b541..9897635960a 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -32,7 +32,7 @@ module Gitlab def init_metrics metrics = {} - metrics[:sampler_duration] = Gitlab::Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', source_label) + metrics[:sampler_duration] = Gitlab::Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', {}) metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum) GC.stat.keys.each do |key| metrics[key] = Gitlab::Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index da1f44b1eab..92be76c48f4 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -43,18 +43,18 @@ module Gitlab def self.metric_transaction_duration_milliseconds @metrics_transaction_duration_milliseconds ||= Gitlab::Metrics.histogram( :gitlab_transaction_duration_milliseconds, - 'Method duration milliseconds', + 'Transaction duration milliseconds', {}, - [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000] + [1, 2, 5, 10, 20, 50, 100, 500, 10000] ) end - def self.metric_transaction_allocated_memory_bytes - @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_bytes, - 'Method duration milliseconds', + def self.metric_transaction_allocated_memory_megabytes + @metric_transaction_allocated_memory_megabytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_megabytes, + 'Transaction allocated memory bytes', {}, - [1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 5000000] + [1, 2, 5, 10, 20, 100] ) end @@ -69,8 +69,8 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - self.class.metric_transaction_duration_milliseconds.observe({}, duration) - self.class.metric_transaction_allocated_memory_bytes.observe({}, allocated_memory) + Transaction.metric_transaction_duration_milliseconds.observe({}, duration) + Transaction.metric_transaction_allocated_memory_megabytes.observe({}, allocated_memory) Thread.current[THREAD_KEY] = nil end -- cgit v1.2.3 From c97dc61a9e3e203bc7a3315dd6828eda320b4323 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 01:26:31 +0200 Subject: Cleanup transaction metrics --- lib/gitlab/metrics/influx_db.rb | 9 ++++--- lib/gitlab/metrics/subscribers/active_record.rb | 14 +++++++++-- lib/gitlab/metrics/transaction.rb | 32 +++++++++++++------------ 3 files changed, 35 insertions(+), 20 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index 1e3db4eb8b4..d819ce1ed35 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -102,9 +102,12 @@ module Gitlab real_time = (real_stop - real_start) * 1000.0 cpu_time = cpu_stop - cpu_start - trans.increment("#{name}_real_time", real_time) - trans.increment("#{name}_cpu_time", cpu_time) - trans.increment("#{name}_call_count", 1) + Gitlab::Metrics.histogram("gitlab_#{name}_real_duration".to_sym, "Measure #{name}", {}, [1, 2, 5, 10, 20, 50, 100, 1000]).observe({}, real_time) + Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration".to_sym, "Measure #{name}", {}, [1, 2, 5, 10, 20, 50, 100, 1000]).observe({}, cpu_time) + + trans.increment("#{name}_real_time", real_time, false) + trans.increment("#{name}_cpu_time", cpu_time, false) + trans.increment("#{name}_call_count", 1, false) retval end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 96cad941d5c..15892c88e86 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -5,11 +5,21 @@ module Gitlab class ActiveRecord < ActiveSupport::Subscriber attach_to :active_record + def self.metric_sql_duration_seconds + @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_sql_duration_seconds, + 'SQL duration seconds', + {}, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + def sql(event) + self.class.metric_sql_duration_secodnds.observe({}, event.duration/1000.0) return unless current_transaction - current_transaction.increment(:sql_duration, event.duration) - current_transaction.increment(:sql_count, 1) + current_transaction.increment(:sql_duration, event.duration, false) + current_transaction.increment(:sql_count, 1, false) end private diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 92be76c48f4..bf196bb17cc 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -40,21 +40,21 @@ module Gitlab @memory_after - @memory_before end - def self.metric_transaction_duration_milliseconds - @metrics_transaction_duration_milliseconds ||= Gitlab::Metrics.histogram( - :gitlab_transaction_duration_milliseconds, - 'Transaction duration milliseconds', + def self.metric_transaction_duration_seconds + @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_seconds, + 'Transaction duration seconds', {}, - [1, 2, 5, 10, 20, 50, 100, 500, 10000] + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end - def self.metric_transaction_allocated_memory_megabytes - @metric_transaction_allocated_memory_megabytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_megabytes, + def self.metric_transaction_allocated_memory_bytes + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, 'Transaction allocated memory bytes', {}, - [1, 2, 5, 10, 20, 100] + [500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] ) end @@ -69,8 +69,8 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - Transaction.metric_transaction_duration_milliseconds.observe({}, duration) - Transaction.metric_transaction_allocated_memory_megabytes.observe({}, allocated_memory) + Transaction.metric_transaction_duration_seconds.observe({}, duration * 1000) + Transaction.metric_transaction_allocated_memory_bytes.observe({}, allocated_memory / 2 ^ 20) Thread.current[THREAD_KEY] = nil end @@ -100,13 +100,15 @@ module Gitlab method end - def increment(name, value) - Gitlab::Metrics.counter("gitlab_transaction_#{name}_total".to_sym, "Transaction counter #{name}", {}).increment({}, value) + def increment(name, value, compat = true) + Gitlab::Metrics.counter("gitlab_transaction_#{name}_total".to_sym, "Transaction counter #{name}", {}) + .increment({}, value) if compat @values[name] += value end - def set(name, value) - Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", {}, :livesum).set({}, value) + def set(name, value, compat = true) + Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", {}, :livesum) + .set({}, value) if compat @values[name] = value end -- cgit v1.2.3 From 6db3151fa164d65d6176896301166c9883df1fe4 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 03:02:08 +0200 Subject: Introduce missing Action concept --- lib/gitlab/metrics/method_call.rb | 35 +++++++++++--------- lib/gitlab/metrics/subscribers/active_record.rb | 4 +-- lib/gitlab/metrics/subscribers/rails_cache.rb | 44 ++++++++++++++++++++----- lib/gitlab/metrics/transaction.rb | 12 +++---- 4 files changed, 62 insertions(+), 33 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index fb652cbeb4f..dba4b0cdc8e 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -4,26 +4,29 @@ module Gitlab class MethodCall attr_reader :real_time, :cpu_time, :call_count - # name - The full name of the method (including namespace) such as - # `User#sign_in`. - # def self.call_real_duration_histogram - @call_real_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_real_duration_milliseconds, - 'Method calls real duration', - {call_name: nil}, - [1, 2, 5, 10, 20, 50, 100, 1000]) - + @call_real_duration_histogram ||= Gitlab::Metrics.histogram( + :gitlab_method_call_real_duration_seconds, + 'Method calls real duration', + { action: nil, call_name: nil }, + [1000, 2000, 5000, 10000, 20000, 50000, 100000, 1000000] + ) end def self.call_cpu_duration_histogram - @call_duration_histogram ||= Gitlab::Metrics.histogram(:gitlab_method_call_cpu_duration_milliseconds, - 'Method calls cpu duration', - {call_name: nil}, - [1, 2, 5, 10, 20, 50, 100, 1000]) + @call_duration_histogram ||= Gitlab::Metrics.histogram( + :gitlab_method_call_cpu_duration_seconds, + 'Method calls cpu duration', + { action: nil, call_name: nil }, + [1000, 2000, 5000, 10000, 20000, 50000, 100000, 1000000] + ) end - - def initialize(name) + # name - The full name of the method (including namespace) such as + # `User#sign_in`. + # + def initialize(name, action) + @action = action @name = name @real_time = 0 @cpu_time = 0 @@ -41,8 +44,8 @@ module Gitlab @call_count += 1 if above_threshold? - self.class.call_real_duration_histogram.observe({ call_name: @name }, @real_time) - self.class.call_cpu_duration_histogram.observe({ call_name: @name }, @cpu_time) + self.class.call_real_duration_histogram.observe({ call_name: @name, action: @action }, @real_time) + self.class.call_cpu_duration_histogram.observe({ call_name: @name, action: @action }, @cpu_time) end retval diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 15892c88e86..411f49456a6 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -8,14 +8,14 @@ module Gitlab def self.metric_sql_duration_seconds @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_sql_duration_seconds, - 'SQL duration seconds', + 'SQL time', {}, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end def sql(event) - self.class.metric_sql_duration_secodnds.observe({}, event.duration/1000.0) + self.class.metric_sql_duration_secodnds.observe({}, event.duration / 1000.0) return unless current_transaction current_transaction.increment(:sql_duration, event.duration, false) diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index aaed2184f44..8eadb40a739 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -6,54 +6,80 @@ module Gitlab class RailsCache < ActiveSupport::Subscriber attach_to :active_support + def self.metric_cache_duration_seconds + @metric_cache_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_cache_duration_seconds, + 'Cache access time', + { action: nil, operation: nil }, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + + def self.metric_cache_read_hit_total + @metric_cache_read_hit_total ||= Gitlab::Metrics.counter(:gitlab_cache_read_hit_total, 'Cache read hit', { action: nil }) + end + + def self.metric_cache_read_miss_total + @metric_cache_read_miss_total ||= Gitlab::Metrics.counter(:gitlab_cache_read_miss_total, 'Cache read miss', { action: nil }) + end + def cache_read(event) - increment(:cache_read, event.duration) + observe(:read, event.duration) return unless current_transaction return if event.payload[:super_operation] == :fetch if event.payload[:hit] + self.class.metric_cache_read_hit_total.increment({ action: action }) current_transaction.increment(:cache_read_hit_count, 1) else + self.class.metric_cache_read_miss_total.increment({ action: action }) current_transaction.increment(:cache_read_miss_count, 1) end end def cache_write(event) - increment(:cache_write, event.duration) + observe(:write, event.duration) end def cache_delete(event) - increment(:cache_delete, event.duration) + observe(:delete, event.duration) end def cache_exist?(event) - increment(:cache_exists, event.duration) + observe(:exists, event.duration) end def cache_fetch_hit(event) return unless current_transaction + self.class.metric_cache_read_hit_total.increment({ action: action }) current_transaction.increment(:cache_read_hit_count, 1) end def cache_generate(event) return unless current_transaction + self.class.metric_cache_read_miss_total.increment({ action: action }) current_transaction.increment(:cache_read_miss_count, 1) end - def increment(key, duration) + def observe(key, duration) return unless current_transaction - current_transaction.increment(:cache_duration, duration) - current_transaction.increment(:cache_count, 1) - current_transaction.increment("#{key}_duration".to_sym, duration) - current_transaction.increment("#{key}_count".to_sym, 1) + metric_cache_duration_seconds.observe({ operation: key, action: action }, duration / 1000.1) + current_transaction.increment(:cache_duration, duration, false) + current_transaction.increment(:cache_count, 1, false) + current_transaction.increment("#{key}_duration".to_sym, duration, false) + current_transaction.increment("#{key}_count".to_sym, 1, false) end private + def action + current_transaction&.action + end + def current_transaction Transaction.current end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index bf196bb17cc..c4d364a08c2 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -43,8 +43,8 @@ module Gitlab def self.metric_transaction_duration_seconds @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_transaction_duration_seconds, - 'Transaction duration seconds', - {}, + 'Transaction duration', + { action: nil }, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end @@ -53,7 +53,7 @@ module Gitlab @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( :gitlab_transaction_allocated_memory_bytes, 'Transaction allocated memory bytes', - {}, + { action: nil }, [500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] ) end @@ -69,8 +69,8 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - Transaction.metric_transaction_duration_seconds.observe({}, duration * 1000) - Transaction.metric_transaction_allocated_memory_bytes.observe({}, allocated_memory / 2 ^ 20) + Transaction.metric_transaction_duration_seconds.observe({ action: action }, duration * 1000) + Transaction.metric_transaction_allocated_memory_bytes.observe({ action: action }, allocated_memory / 2 ^ 20) Thread.current[THREAD_KEY] = nil end @@ -94,7 +94,7 @@ module Gitlab # Returns a MethodCall object for the given name. def method_call_for(name) unless method = @methods[name] - @methods[name] = method = MethodCall.new(name) + @methods[name] = method = MethodCall.new(name, action) end method -- cgit v1.2.3 From aa25586afea1ef8f909e9ffd7c01d5c9e339b9bc Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 03:19:39 +0200 Subject: Add action tag to more metrics --- lib/gitlab/metrics/subscribers/active_record.rb | 4 +-- lib/gitlab/metrics/transaction.rb | 33 +++++++++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 411f49456a6..db6bc8c90ff 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -9,14 +9,14 @@ module Gitlab @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_sql_duration_seconds, 'SQL time', - {}, + { action: nil }, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end def sql(event) - self.class.metric_sql_duration_secodnds.observe({}, event.duration / 1000.0) return unless current_transaction + self.class.metric_sql_duration_seconds.observe({ action: current_transaction.action }, event.duration / 1000.0) current_transaction.increment(:sql_duration, event.duration, false) current_transaction.increment(:sql_count, 1, false) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index c4d364a08c2..ec298990e5a 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -87,7 +87,7 @@ module Gitlab # 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 = {}) - Gitlab::Metrics.counter("gitlab_transaction_event_#{event_name}_total".to_sym, "Transaction event #{event_name}", tags).increment({}) + self.class.metric_event_counter(event_name, tags).increment(tags.merge({ action: action })) @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags, :event) end @@ -101,14 +101,12 @@ module Gitlab end def increment(name, value, compat = true) - Gitlab::Metrics.counter("gitlab_transaction_#{name}_total".to_sym, "Transaction counter #{name}", {}) - .increment({}, value) if compat + self.class.metric_transaction_counter(name).increment({ action: action }, value) if compat @values[name] += value end def set(name, value, compat = true) - Gitlab::Metrics.gauge("gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", {}, :livesum) - .set({}, value) if compat + self.class.metric_transaction_gauge(name).set({ action: action }, value) if compat @values[name] = value end @@ -144,6 +142,31 @@ module Gitlab Metrics.submit_metrics(submit_hashes) end + + private + + def self.metric_event_counter(event_name, tags) + @metric_event_counters ||= {} + @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_event_#{event_name}_total".to_sym, + "Transaction event #{event_name} counter", + tags.merge({ action: nil }) + ) + end + + def self.metric_transaction_counter(name) + @metric_transaction_counters ||= {} + @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", action: nil + ) + end + + def self.metric_transaction_gauge(name) + @metric_transaction_gauges ||= {} + @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( + "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", { action: nil }, :livesum + ) + end end end end -- cgit v1.2.3 From 29a1ad16467123c5d6a11e5c7bf0ebacc4e79438 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 04:36:54 +0200 Subject: Tune bucket sizes an action labels --- lib/gitlab/metrics/method_call.rb | 8 ++++---- lib/gitlab/metrics/rack_middleware.rb | 16 ++++++++-------- lib/gitlab/metrics/subscribers/rails_cache.rb | 2 +- lib/gitlab/metrics/transaction.rb | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index dba4b0cdc8e..76fced601e5 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -9,7 +9,7 @@ module Gitlab :gitlab_method_call_real_duration_seconds, 'Method calls real duration', { action: nil, call_name: nil }, - [1000, 2000, 5000, 10000, 20000, 50000, 100000, 1000000] + [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -18,7 +18,7 @@ module Gitlab :gitlab_method_call_cpu_duration_seconds, 'Method calls cpu duration', { action: nil, call_name: nil }, - [1000, 2000, 5000, 10000, 20000, 50000, 100000, 1000000] + [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -44,8 +44,8 @@ module Gitlab @call_count += 1 if above_threshold? - self.class.call_real_duration_histogram.observe({ call_name: @name, action: @action }, @real_time) - self.class.call_cpu_duration_histogram.observe({ call_name: @name, action: @action }, @cpu_time) + self.class.call_real_duration_histogram.observe({ call_name: @name, action: @action }, @real_time / 1000.0) + self.class.call_cpu_duration_histogram.observe({ call_name: @name, action: @action }, @cpu_time / 1000.0) end retval diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index adc0db1a874..fcfaeb83b4c 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -35,12 +35,6 @@ module Gitlab # Even in the event of an error we want to submit any metrics we # might've gathered up to this point. ensure - if env[CONTROLLER_KEY] - tag_controller(trans, env) - elsif env[ENDPOINT_KEY] - tag_endpoint(trans, env) - end - trans.finish end @@ -50,8 +44,14 @@ module Gitlab def transaction_from_env(env) trans = Transaction.new - trans.set(:request_uri, filtered_path(env)) - trans.set(:request_method, env['REQUEST_METHOD']) + trans.set(:request_uri, filtered_path(env), false) + trans.set(:request_method, env['REQUEST_METHOD'], false) + + if env[CONTROLLER_KEY] + tag_controller(trans, env) + elsif env[ENDPOINT_KEY] + tag_endpoint(trans, env) + end trans end diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index 8eadb40a739..d7eaef3304f 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -67,7 +67,7 @@ module Gitlab def observe(key, duration) return unless current_transaction - metric_cache_duration_seconds.observe({ operation: key, action: action }, duration / 1000.1) + self.class.metric_cache_duration_seconds.observe({ operation: key, action: action }, duration / 1000.0) current_transaction.increment(:cache_duration, duration, false) current_transaction.increment(:cache_count, 1, false) current_transaction.increment("#{key}_duration".to_sym, duration, false) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index ec298990e5a..322111b63dc 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -54,7 +54,7 @@ module Gitlab :gitlab_transaction_allocated_memory_bytes, 'Transaction allocated memory bytes', { action: nil }, - [500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] + [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] ) end @@ -70,7 +70,7 @@ module Gitlab @finished_at = System.monotonic_time Transaction.metric_transaction_duration_seconds.observe({ action: action }, duration * 1000) - Transaction.metric_transaction_allocated_memory_bytes.observe({ action: action }, allocated_memory / 2 ^ 20) + Transaction.metric_transaction_allocated_memory_bytes.observe({ action: action }, allocated_memory * 1024.0) Thread.current[THREAD_KEY] = nil end @@ -100,13 +100,13 @@ module Gitlab method end - def increment(name, value, compat = true) - self.class.metric_transaction_counter(name).increment({ action: action }, value) if compat + def increment(name, value, use_prometheus = true) + self.class.metric_transaction_counter(name).increment({ action: action }, value) if use_prometheus @values[name] += value end - def set(name, value, compat = true) - self.class.metric_transaction_gauge(name).set({ action: action }, value) if compat + def set(name, value, use_prometheus = true) + self.class.metric_transaction_gauge(name).set({ action: action }, value) if use_prometheus @values[name] = value end -- cgit v1.2.3 From a8a5c337c1d6914a0f9bdd17fb9fe1453c21a067 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 11:36:29 +0200 Subject: Transaction needs to be able to describe controller action by itself --- lib/gitlab/metrics/rack_middleware.rb | 51 +------------------ lib/gitlab/metrics/transaction.rb | 93 +++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 71 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index fcfaeb83b4c..11af1fb6322 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -2,20 +2,6 @@ module Gitlab module Metrics # Rack middleware for tracking Rails and Grape requests. class RackMiddleware - CONTROLLER_KEY = 'action_controller.instance'.freeze - ENDPOINT_KEY = 'api.endpoint'.freeze - CONTENT_TYPES = { - 'text/html' => :html, - 'text/plain' => :txt, - 'application/json' => :json, - 'text/js' => :js, - 'application/atom+xml' => :atom, - 'image/png' => :png, - 'image/jpeg' => :jpeg, - 'image/gif' => :gif, - 'image/svg+xml' => :svg - }.freeze - def initialize(app) @app = app end @@ -42,49 +28,14 @@ module Gitlab end def transaction_from_env(env) - trans = Transaction.new + trans = Transaction.new(env) trans.set(:request_uri, filtered_path(env), false) trans.set(:request_method, env['REQUEST_METHOD'], false) - if env[CONTROLLER_KEY] - tag_controller(trans, env) - elsif env[ENDPOINT_KEY] - tag_endpoint(trans, env) - end - trans end - def tag_controller(trans, env) - controller = env[CONTROLLER_KEY] - action = "#{controller.class.name}##{controller.action_name}" - suffix = CONTENT_TYPES[controller.content_type] - - if suffix && suffix != :html - action += ".#{suffix}" - end - - trans.action = action - end - - def tag_endpoint(trans, env) - endpoint = env[ENDPOINT_KEY] - - begin - route = endpoint.route - rescue - # endpoint.route is calling env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info] - # but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response - # so we're rescuing exceptions and bailing out - end - - if route - path = endpoint_paths_cache[route.request_method][route.path] - trans.action = "Grape##{route.request_method} #{path}" - end - end - private def filtered_path(env) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 322111b63dc..0b15d73a237 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -2,6 +2,21 @@ module Gitlab module Metrics # Class for storing metrics information of a single transaction. class Transaction + CONTROLLER_KEY = 'action_controller.instance'.freeze + ENDPOINT_KEY = 'api.endpoint'.freeze + + CONTENT_TYPES = { + 'text/html' => :html, + 'text/plain' => :txt, + 'application/json' => :json, + 'text/js' => :js, + 'application/atom+xml' => :atom, + 'image/png' => :png, + 'image/jpeg' => :jpeg, + 'image/gif' => :gif, + 'image/svg+xml' => :svg + }.freeze + THREAD_KEY = :_gitlab_metrics_transaction # The series to store events (e.g. Git pushes) in. @@ -9,15 +24,13 @@ module Gitlab attr_reader :tags, :values, :method, :metrics - attr_accessor :action - def self.current Thread.current[THREAD_KEY] end # action - A String describing the action performed, usually the class # plus method name. - def initialize(action = nil) + def initialize(env) @metrics = [] @methods = {} @@ -26,7 +39,7 @@ module Gitlab @values = Hash.new(0) @tags = {} - @action = action + @env = env @memory_before = 0 @memory_after = 0 @@ -40,22 +53,12 @@ module Gitlab @memory_after - @memory_before end - def self.metric_transaction_duration_seconds - @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_transaction_duration_seconds, - 'Transaction duration', - { action: nil }, - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - - def self.metric_transaction_allocated_memory_bytes - @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_bytes, - 'Transaction allocated memory bytes', - { action: nil }, - [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] - ) + def action + @action ||= if @env[CONTROLLER_KEY] + action_from_controller(@env) || '' + elsif @env[ENDPOINT_KEY] + action_from_endpoint(@env) || '' + end end def run @@ -135,7 +138,7 @@ module Gitlab submit_hashes = submit.map do |metric| hash = metric.to_hash - hash[:tags][:action] ||= @action if @action && !metric.event? + hash[:tags][:action] ||= action if action && !metric.event? hash end @@ -145,6 +148,24 @@ module Gitlab private + def self.metric_transaction_duration_seconds + @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_seconds, + 'Transaction duration', + { action: nil }, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + + def self.metric_transaction_allocated_memory_bytes + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, + 'Transaction allocated memory bytes', + { action: nil }, + [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] + ) + end + def self.metric_event_counter(event_name, tags) @metric_event_counters ||= {} @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( @@ -167,6 +188,36 @@ module Gitlab "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", { action: nil }, :livesum ) end + + def action_from_controller(env) + controller = env[CONTROLLER_KEY] + + action = "#{controller.class.name}##{controller.action_name}" + suffix = CONTENT_TYPES[controller.content_type] + + if suffix && suffix != :html + action += ".#{suffix}" + end + + action + end + + def action_from_endpoint(env) + endpoint = env[ENDPOINT_KEY] + + begin + route = endpoint.route + rescue + # endpoint.route is calling env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info] + # but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response + # so we're rescuing exceptions and bailing out + end + + if route + path = endpoint_paths_cache[route.request_method][route.path] + "Grape##{route.request_method} #{path}" + end + end end end end -- cgit v1.2.3 From 40e9fbb2922b0135a0564cbe6c07cec0c3a50182 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 12:11:53 +0200 Subject: Rails cache metrics name alignment --- lib/gitlab/metrics/subscribers/rails_cache.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index d7eaef3304f..e17401b5f10 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -6,9 +6,9 @@ module Gitlab class RailsCache < ActiveSupport::Subscriber attach_to :active_support - def self.metric_cache_duration_seconds - @metric_cache_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_cache_duration_seconds, + def self.metric_cache_operation_duration_seconds + @metric_cache_operation_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_cache_operation_duration_seconds, 'Cache access time', { action: nil, operation: nil }, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] @@ -31,10 +31,10 @@ module Gitlab if event.payload[:hit] self.class.metric_cache_read_hit_total.increment({ action: action }) - current_transaction.increment(:cache_read_hit_count, 1) + current_transaction.increment(:cache_read_hit_count, 1, false) else self.class.metric_cache_read_miss_total.increment({ action: action }) - current_transaction.increment(:cache_read_miss_count, 1) + current_transaction.increment(:cache_read_miss_count, 1, false) end end @@ -67,11 +67,11 @@ module Gitlab def observe(key, duration) return unless current_transaction - self.class.metric_cache_duration_seconds.observe({ operation: key, action: action }, duration / 1000.0) + self.class.metric_cache_operation_duration_seconds.observe({ operation: key, action: action }, duration / 1000.0) current_transaction.increment(:cache_duration, duration, false) current_transaction.increment(:cache_count, 1, false) - current_transaction.increment("#{key}_duration".to_sym, duration, false) - current_transaction.increment("#{key}_count".to_sym, 1, false) + current_transaction.increment("cache_#{key}_duration".to_sym, duration, false) + current_transaction.increment("cache_#{key}_count".to_sym, 1, false) end private -- cgit v1.2.3 From 6cd912ba16a23458d9f765c84373903790ee191b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 12:26:12 +0200 Subject: Fix measure codde to work with seconds --- lib/gitlab/metrics/influx_db.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index d819ce1ed35..04dd42e22e2 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -102,8 +102,8 @@ module Gitlab real_time = (real_stop - real_start) * 1000.0 cpu_time = cpu_stop - cpu_start - Gitlab::Metrics.histogram("gitlab_#{name}_real_duration".to_sym, "Measure #{name}", {}, [1, 2, 5, 10, 20, 50, 100, 1000]).observe({}, real_time) - Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration".to_sym, "Measure #{name}", {}, [1, 2, 5, 10, 20, 50, 100, 1000]).observe({}, cpu_time) + Gitlab::Metrics.histogram("gitlab_#{name}_real_duration_seconds".to_sym, "Measure #{name}", {}, [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]).observe({}, real_time / 1000.0) + Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration_seconds".to_sym, "Measure #{name}", {}, [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]).observe({}, cpu_time / 1000.0) trans.increment("#{name}_real_time", real_time, false) trans.increment("#{name}_cpu_time", cpu_time, false) -- cgit v1.2.3 From 43a9777e5e33bcc226c7dcac2f758e9cd87cf5af Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 13:23:34 +0200 Subject: Make transaction labels more readable --- lib/gitlab/metrics/subscribers/active_record.rb | 4 +- lib/gitlab/metrics/subscribers/rails_cache.rb | 28 ++++++++------ lib/gitlab/metrics/transaction.rb | 50 +++++++++++++++---------- 3 files changed, 48 insertions(+), 34 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index db6bc8c90ff..842a4cfaf55 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -9,14 +9,14 @@ module Gitlab @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_sql_duration_seconds, 'SQL time', - { action: nil }, + Transaction::BASE_LABELS, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end def sql(event) return unless current_transaction - self.class.metric_sql_duration_seconds.observe({ action: current_transaction.action }, event.duration / 1000.0) + self.class.metric_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0) current_transaction.increment(:sql_duration, event.duration, false) current_transaction.increment(:sql_count, 1, false) diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index e17401b5f10..17814f6b845 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -10,17 +10,25 @@ module Gitlab @metric_cache_operation_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_cache_operation_duration_seconds, 'Cache access time', - { action: nil, operation: nil }, + Transaction::BASE_LABELS.merge({ action: nil }), [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end def self.metric_cache_read_hit_total - @metric_cache_read_hit_total ||= Gitlab::Metrics.counter(:gitlab_cache_read_hit_total, 'Cache read hit', { action: nil }) + @metric_cache_read_hit_total ||= Gitlab::Metrics.counter( + :gitlab_cache_read_hit_total, + 'Cache read hit', + Transaction::BASE_LABELS + ) end def self.metric_cache_read_miss_total - @metric_cache_read_miss_total ||= Gitlab::Metrics.counter(:gitlab_cache_read_miss_total, 'Cache read miss', { action: nil }) + @metric_cache_read_miss_total ||= Gitlab::Metrics.counter( + :gitlab_cache_read_miss_total, + 'Cache read miss', + Transaction::BASE_LABELS + ) end def cache_read(event) @@ -30,10 +38,10 @@ module Gitlab return if event.payload[:super_operation] == :fetch if event.payload[:hit] - self.class.metric_cache_read_hit_total.increment({ action: action }) + self.class.metric_cache_read_hit_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_hit_count, 1, false) else - self.class.metric_cache_read_miss_total.increment({ action: action }) + self.class.metric_cache_read_miss_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1, false) end end @@ -53,21 +61,21 @@ module Gitlab def cache_fetch_hit(event) return unless current_transaction - self.class.metric_cache_read_hit_total.increment({ action: action }) + self.class.metric_cache_read_hit_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_hit_count, 1) end def cache_generate(event) return unless current_transaction - self.class.metric_cache_read_miss_total.increment({ action: action }) + self.class.metric_cache_read_miss_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1) end def observe(key, duration) return unless current_transaction - self.class.metric_cache_operation_duration_seconds.observe({ operation: key, action: action }, duration / 1000.0) + self.class.metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0) current_transaction.increment(:cache_duration, duration, false) current_transaction.increment(:cache_count, 1, false) current_transaction.increment("cache_#{key}_duration".to_sym, duration, false) @@ -76,10 +84,6 @@ module Gitlab private - def action - current_transaction&.action - end - def current_transaction Transaction.current end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 0b15d73a237..2c9ecbb03a5 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -2,6 +2,7 @@ module Gitlab module Metrics # Class for storing metrics information of a single transaction. class Transaction + BASE_LABELS = { controller: nil, action: nil }.freeze CONTROLLER_KEY = 'action_controller.instance'.freeze ENDPOINT_KEY = 'api.endpoint'.freeze @@ -54,11 +55,20 @@ module Gitlab end def action - @action ||= if @env[CONTROLLER_KEY] - action_from_controller(@env) || '' - elsif @env[ENDPOINT_KEY] - action_from_endpoint(@env) || '' - end + "#{labels[:controller]}##{labels[:action]}" if labels + end + + def labels + return @labels if @labels + + # memoize transaction labels only source env variables were present + @labels = if @env[CONTROLLER_KEY] + labels_from_controller(@env) || {} + elsif @env[ENDPOINT_KEY] + labels_from_endpoint(@env) || {} + end + + @labels || {} end def run @@ -72,8 +82,8 @@ module Gitlab @memory_after = System.memory_usage @finished_at = System.monotonic_time - Transaction.metric_transaction_duration_seconds.observe({ action: action }, duration * 1000) - Transaction.metric_transaction_allocated_memory_bytes.observe({ action: action }, allocated_memory * 1024.0) + Transaction.metric_transaction_duration_seconds.observe(labels, duration * 1000) + Transaction.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) Thread.current[THREAD_KEY] = nil end @@ -90,7 +100,7 @@ module Gitlab # 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 = {}) - self.class.metric_event_counter(event_name, tags).increment(tags.merge({ action: action })) + self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags, :event) end @@ -104,12 +114,12 @@ module Gitlab end def increment(name, value, use_prometheus = true) - self.class.metric_transaction_counter(name).increment({ action: action }, value) if use_prometheus + self.class.metric_transaction_counter(name).increment(labels, value) if use_prometheus @values[name] += value end def set(name, value, use_prometheus = true) - self.class.metric_transaction_gauge(name).set({ action: action }, value) if use_prometheus + self.class.metric_transaction_gauge(name).set(labels, value) if use_prometheus @values[name] = value end @@ -152,7 +162,7 @@ module Gitlab @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_transaction_duration_seconds, 'Transaction duration', - { action: nil }, + BASE_LABELS, [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] ) end @@ -161,7 +171,7 @@ module Gitlab @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( :gitlab_transaction_allocated_memory_bytes, 'Transaction allocated memory bytes', - { action: nil }, + BASE_LABELS, [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] ) end @@ -171,38 +181,38 @@ module Gitlab @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( "gitlab_transaction_event_#{event_name}_total".to_sym, "Transaction event #{event_name} counter", - tags.merge({ action: nil }) + tags.merge(BASE_LABELS) ) end def self.metric_transaction_counter(name) @metric_transaction_counters ||= {} @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", action: nil + "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS ) end def self.metric_transaction_gauge(name) @metric_transaction_gauges ||= {} @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( - "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", { action: nil }, :livesum + "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum ) end - def action_from_controller(env) + def labels_from_controller(env) controller = env[CONTROLLER_KEY] - action = "#{controller.class.name}##{controller.action_name}" + action = "#{controller.action_name}" suffix = CONTENT_TYPES[controller.content_type] if suffix && suffix != :html action += ".#{suffix}" end - action + { controller: controller.class.name, action: action } end - def action_from_endpoint(env) + def labels_from_endpoint(env) endpoint = env[ENDPOINT_KEY] begin @@ -215,7 +225,7 @@ module Gitlab if route path = endpoint_paths_cache[route.request_method][route.path] - "Grape##{route.request_method} #{path}" + { controller: 'Grape', action: "#{route.request_method} #{path}" } end end end -- cgit v1.2.3 From cc7997d8d00e159eb1fd9a4cb5f31d68df4681b9 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 13:35:47 +0200 Subject: More parsable labels in method performance measurements --- lib/gitlab/metrics/method_call.rb | 12 ++++++------ lib/gitlab/metrics/transaction.rb | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 76fced601e5..d7d86e3e89f 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -8,7 +8,7 @@ module Gitlab @call_real_duration_histogram ||= Gitlab::Metrics.histogram( :gitlab_method_call_real_duration_seconds, 'Method calls real duration', - { action: nil, call_name: nil }, + Transaction::BASE_LABELS.merge({ call_name: nil }), [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -17,7 +17,7 @@ module Gitlab @call_duration_histogram ||= Gitlab::Metrics.histogram( :gitlab_method_call_cpu_duration_seconds, 'Method calls cpu duration', - { action: nil, call_name: nil }, + Transaction::BASE_LABELS.merge({ call_name: nil }), [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -25,8 +25,8 @@ module Gitlab # name - The full name of the method (including namespace) such as # `User#sign_in`. # - def initialize(name, action) - @action = action + def initialize(name, transaction) + @transaction = transaction @name = name @real_time = 0 @cpu_time = 0 @@ -44,8 +44,8 @@ module Gitlab @call_count += 1 if above_threshold? - self.class.call_real_duration_histogram.observe({ call_name: @name, action: @action }, @real_time / 1000.0) - self.class.call_cpu_duration_histogram.observe({ call_name: @name, action: @action }, @cpu_time / 1000.0) + self.class.call_real_duration_histogram.observe(@transaction.labels.merge({ call_name: @name }), @real_time / 1000.0) + self.class.call_cpu_duration_histogram.observe(@transaction.labels.merge({ call_name: @name }), @cpu_time / 1000.0) end retval diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 2c9ecbb03a5..28d3247c3e0 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -107,7 +107,7 @@ module Gitlab # Returns a MethodCall object for the given name. def method_call_for(name) unless method = @methods[name] - @methods[name] = method = MethodCall.new(name, action) + @methods[name] = method = MethodCall.new(name, transaction) end method -- cgit v1.2.3 From 815b8db1b9b01a994867948bb4b8b4476e83ff47 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 13:59:21 +0200 Subject: Split call name to module and method name --- lib/gitlab/metrics/influx_db.rb | 13 +++++++++++-- lib/gitlab/metrics/instrumentation.rb | 3 ++- lib/gitlab/metrics/method_call.rb | 17 ++++++++++++----- lib/gitlab/metrics/transaction.rb | 4 ++-- 4 files changed, 27 insertions(+), 10 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index 04dd42e22e2..adbc5397257 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -102,8 +102,17 @@ module Gitlab real_time = (real_stop - real_start) * 1000.0 cpu_time = cpu_stop - cpu_start - Gitlab::Metrics.histogram("gitlab_#{name}_real_duration_seconds".to_sym, "Measure #{name}", {}, [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]).observe({}, real_time / 1000.0) - Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration_seconds".to_sym, "Measure #{name}", {}, [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]).observe({}, cpu_time / 1000.0) + Gitlab::Metrics.histogram("gitlab_#{name}_real_duration_seconds".to_sym, + "Measure #{name}", + Transaction::BASE_LABELS, + [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]) + .observe(trans.labels, real_time / 1000.0) + + Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration_seconds".to_sym, + "Measure #{name}", + Transaction::BASE_LABELS, + [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]) + .observe(trans.labels, cpu_time / 1000.0) trans.increment("#{name}_real_time", real_time, false) trans.increment("#{name}_cpu_time", cpu_time, false) diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb index 6aa38542cb4..1b80fef1212 100644 --- a/lib/gitlab/metrics/instrumentation.rb +++ b/lib/gitlab/metrics/instrumentation.rb @@ -153,7 +153,8 @@ module Gitlab proxy_module.class_eval <<-EOF, __FILE__, __LINE__ + 1 def #{name}(#{args_signature}) if trans = Gitlab::Metrics::Instrumentation.transaction - trans.method_call_for(#{label.to_sym.inspect}).measure { super } + trans.method_call_for(#{label.to_sym.inspect}, #{mod.name.to_sym.inspect}, #{name.to_sym.inspect}) + .measure { super } else super end diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index d7d86e3e89f..0f7ab84a9b4 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -2,13 +2,14 @@ module Gitlab module Metrics # Class for tracking timing information about method calls class MethodCall + BASE_LABELS = { module: nil, method: nil } attr_reader :real_time, :cpu_time, :call_count def self.call_real_duration_histogram @call_real_duration_histogram ||= Gitlab::Metrics.histogram( :gitlab_method_call_real_duration_seconds, 'Method calls real duration', - Transaction::BASE_LABELS.merge({ call_name: nil }), + Transaction::BASE_LABELS.merge(BASE_LABELS), [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -17,7 +18,7 @@ module Gitlab @call_duration_histogram ||= Gitlab::Metrics.histogram( :gitlab_method_call_cpu_duration_seconds, 'Method calls cpu duration', - Transaction::BASE_LABELS.merge({ call_name: nil }), + Transaction::BASE_LABELS.merge(BASE_LABELS), [0.1, 0.2, 0.5, 1, 2, 5, 10] ) end @@ -25,7 +26,9 @@ module Gitlab # name - The full name of the method (including namespace) such as # `User#sign_in`. # - def initialize(name, transaction) + def initialize(name, module_name, method_name, transaction) + @module_name = module_name + @method_name = method_name @transaction = transaction @name = name @real_time = 0 @@ -44,13 +47,17 @@ module Gitlab @call_count += 1 if above_threshold? - self.class.call_real_duration_histogram.observe(@transaction.labels.merge({ call_name: @name }), @real_time / 1000.0) - self.class.call_cpu_duration_histogram.observe(@transaction.labels.merge({ call_name: @name }), @cpu_time / 1000.0) + self.class.call_real_duration_histogram.observe(@transaction.labels.merge(labels), @real_time / 1000.0) + self.class.call_cpu_duration_histogram.observe(@transaction.labels.merge(labels), @cpu_time / 1000.0) end retval end + def labels + @labels ||= { module: @module_name, method: @method_name } + end + # Returns a Metric instance of the current method call. def to_metric Metric.new( diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 28d3247c3e0..f4fd26b1547 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -105,9 +105,9 @@ module Gitlab end # Returns a MethodCall object for the given name. - def method_call_for(name) + def method_call_for(name, module_name, method_name) unless method = @methods[name] - @methods[name] = method = MethodCall.new(name, transaction) + @methods[name] = method = MethodCall.new(name, module_name, method_name, self) end method -- cgit v1.2.3 From 4cf6be2be8afdac58a24edbc7d9ae942ed3dca51 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 14:02:42 +0200 Subject: Differentiatie between class and instance methods --- lib/gitlab/metrics/instrumentation.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb index 1b80fef1212..023e9963493 100644 --- a/lib/gitlab/metrics/instrumentation.rb +++ b/lib/gitlab/metrics/instrumentation.rb @@ -118,19 +118,21 @@ module Gitlab def self.instrument(type, mod, name) return unless Metrics.enabled? - name = name.to_sym + name = name.to_sym target = type == :instance ? mod : mod.singleton_class if type == :instance target = mod - label = "#{mod.name}##{name}" + method_name = "##{name}" method = mod.instance_method(name) else target = mod.singleton_class - label = "#{mod.name}.#{name}" + method_name = ".#{name}" method = mod.method(name) end + label = "#{mod.name}#{method_name}" + unless instrumented?(target) target.instance_variable_set(PROXY_IVAR, Module.new) end @@ -153,7 +155,7 @@ module Gitlab proxy_module.class_eval <<-EOF, __FILE__, __LINE__ + 1 def #{name}(#{args_signature}) if trans = Gitlab::Metrics::Instrumentation.transaction - trans.method_call_for(#{label.to_sym.inspect}, #{mod.name.to_sym.inspect}, #{name.to_sym.inspect}) + trans.method_call_for(#{label.to_sym.inspect}, #{mod.name.inspect}, "#{method_name}") .measure { super } else super -- cgit v1.2.3 From 547dd817a59f79516d7d25c5918de22a3d2e0e12 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 15:40:41 +0200 Subject: Fix rubocop warnings --- lib/gitlab/metrics/method_call.rb | 2 +- lib/gitlab/metrics/samplers/base_sampler.rb | 1 - lib/gitlab/metrics/samplers/influx_sampler.rb | 2 +- lib/gitlab/metrics/samplers/unicorn_sampler.rb | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 0f7ab84a9b4..6d6ecae309b 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -2,7 +2,7 @@ module Gitlab module Metrics # Class for tracking timing information about method calls class MethodCall - BASE_LABELS = { module: nil, method: nil } + BASE_LABELS = { module: nil, method: nil }.freeze attr_reader :real_time, :cpu_time, :call_count def self.call_real_duration_histogram diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb index 50441b514b2..37f90c4673d 100644 --- a/lib/gitlab/metrics/samplers/base_sampler.rb +++ b/lib/gitlab/metrics/samplers/base_sampler.rb @@ -62,4 +62,3 @@ module Gitlab end end end - diff --git a/lib/gitlab/metrics/samplers/influx_sampler.rb b/lib/gitlab/metrics/samplers/influx_sampler.rb index 66adc47dcc3..f4f9b5ca792 100644 --- a/lib/gitlab/metrics/samplers/influx_sampler.rb +++ b/lib/gitlab/metrics/samplers/influx_sampler.rb @@ -100,4 +100,4 @@ module Gitlab end end end -end \ No newline at end of file +end diff --git a/lib/gitlab/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb index 686489a07a8..ea325651fbb 100644 --- a/lib/gitlab/metrics/samplers/unicorn_sampler.rb +++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb @@ -47,4 +47,4 @@ module Gitlab end end end -end \ No newline at end of file +end -- cgit v1.2.3 From 39ac6acbccff05d0379e908345043d07b5ab489b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 18:43:06 +0200 Subject: Fix Active record and transaction specs --- lib/gitlab/metrics/transaction.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index f4fd26b1547..91e9628606b 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -101,7 +101,7 @@ module Gitlab # tags - A set of tags to attach to the event. def add_event(event_name, tags = {}) self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) - @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags, :event) + @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) end # Returns a MethodCall object for the given name. -- cgit v1.2.3 From b90bf88b141aa9efa0979111b8820b8cc876b32d Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Wed, 6 Sep 2017 20:36:25 +0200 Subject: Fix rspec errors, and add more tests to MethodCall and ActionView --- lib/gitlab/metrics/method_call.rb | 12 +++++++----- lib/gitlab/metrics/subscribers/action_view.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 6d6ecae309b..f83d397fd78 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -42,14 +42,16 @@ module Gitlab start_cpu = System.cpu_time retval = yield - @real_time += System.monotonic_time - start_real + real_time = System.monotonic_time - start_real + cpu_time = System.cpu_time - start_cpu + + @real_time += real_time + @cpu_time += System.cpu_time - start_cpu @call_count += 1 - if above_threshold? - self.class.call_real_duration_histogram.observe(@transaction.labels.merge(labels), @real_time / 1000.0) - self.class.call_cpu_duration_histogram.observe(@transaction.labels.merge(labels), @cpu_time / 1000.0) - end + self.class.call_real_duration_histogram.observe(@transaction.labels.merge(labels), real_time / 1000.0) + self.class.call_cpu_duration_histogram.observe(@transaction.labels.merge(labels), cpu_time / 1000.0) retval end diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index d435a33e9c7..f7d5f8f0279 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -15,10 +15,24 @@ module Gitlab private + def self.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) + self.class.metric_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) end -- cgit v1.2.3 From f64085e6932ff128facdc361c340cb951214c1c6 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 11:15:27 +0200 Subject: Move labels tests from Metrics rack spec to Transaction spec --- lib/gitlab/metrics/rack_middleware.rb | 12 ------------ lib/gitlab/metrics/transaction.rb | 26 +++++++++++++++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index 11af1fb6322..a5aa0c49551 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -41,18 +41,6 @@ module Gitlab def filtered_path(env) ActionDispatch::Request.new(env).filtered_path.presence || env['REQUEST_URI'] end - - def endpoint_paths_cache - @endpoint_paths_cache ||= Hash.new do |hash, http_method| - hash[http_method] = Hash.new do |inner_hash, raw_path| - inner_hash[raw_path] = endpoint_instrumentable_path(raw_path) - end - end - end - - def endpoint_instrumentable_path(raw_path) - raw_path.sub('(.:format)', '').sub('/:version', '') - end end end end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 91e9628606b..5bf8580d91f 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -55,7 +55,7 @@ module Gitlab end def action - "#{labels[:controller]}##{labels[:action]}" if labels + "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? end def labels @@ -63,9 +63,9 @@ module Gitlab # memoize transaction labels only source env variables were present @labels = if @env[CONTROLLER_KEY] - labels_from_controller(@env) || {} + labels_from_controller || {} elsif @env[ENDPOINT_KEY] - labels_from_endpoint(@env) || {} + labels_from_endpoint || {} end @labels || {} @@ -199,8 +199,8 @@ module Gitlab ) end - def labels_from_controller(env) - controller = env[CONTROLLER_KEY] + def labels_from_controller + controller = @env[CONTROLLER_KEY] action = "#{controller.action_name}" suffix = CONTENT_TYPES[controller.content_type] @@ -212,8 +212,8 @@ module Gitlab { controller: controller.class.name, action: action } end - def labels_from_endpoint(env) - endpoint = env[ENDPOINT_KEY] + def labels_from_endpoint + endpoint = @env[ENDPOINT_KEY] begin route = endpoint.route @@ -228,6 +228,18 @@ module Gitlab { controller: 'Grape', action: "#{route.request_method} #{path}" } end end + + def endpoint_paths_cache + @endpoint_paths_cache ||= Hash.new do |hash, http_method| + hash[http_method] = Hash.new do |inner_hash, raw_path| + inner_hash[raw_path] = endpoint_instrumentable_path(raw_path) + end + end + end + + def endpoint_instrumentable_path(raw_path) + raw_path.sub('(.:format)', '').sub('/:version', '') + end end end end -- cgit v1.2.3 From b4dbc30616db11fcfa7775f4fe1c040651f70aa0 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 20:22:01 +0200 Subject: Guard metrics creation with a mutex --- lib/gitlab/metrics/prometheus.rb | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb index 460dab47276..e056a3f7b43 100644 --- a/lib/gitlab/metrics/prometheus.rb +++ b/lib/gitlab/metrics/prometheus.rb @@ -5,6 +5,9 @@ module Gitlab module Prometheus include Gitlab::CurrentSettings + REGISTRY_MUTEX = Mutex.new + PROVIDER_MUTEX = Mutex.new + def metrics_folder_present? multiprocess_files_dir = ::Prometheus::Client.configuration.multiprocess_files_dir @@ -20,23 +23,38 @@ module Gitlab end def registry - @registry ||= ::Prometheus::Client.registry + return @registry if @registry + + REGISTRY_MUTEX.synchronize do + @registry ||= ::Prometheus::Client.registry + end end def counter(name, docstring, base_labels = {}) - provide_metric(name) || registry.counter(name, docstring, base_labels) + safe_provide_metric(:counter, name, docstring, base_labels) end def summary(name, docstring, base_labels = {}) - provide_metric(name) || registry.summary(name, docstring, base_labels) + safe_provide_metric(:summary, name, docstring, base_labels) end def gauge(name, docstring, base_labels = {}, multiprocess_mode = :all) - provide_metric(name) || registry.gauge(name, docstring, base_labels, multiprocess_mode) + safe_provide_metric(:gauge, name, docstring, base_labels, multiprocess_mode) end def histogram(name, docstring, base_labels = {}, buckets = ::Prometheus::Client::Histogram::DEFAULT_BUCKETS) - provide_metric(name) || registry.histogram(name, docstring, base_labels, buckets) + safe_provide_metric(:histogram, name, docstring, base_labels, buckets) + end + + private + + def safe_provide_metric(method, name, *args) + metric = provide_metric(name) + return metric if metric + + PROVIDER_MUTEX.synchronize do + provide_metric(name) || registry.send(method, *args) + end end def provide_metric(name) @@ -47,8 +65,6 @@ module Gitlab end end - private - def prometheus_metrics_enabled_unmemoized metrics_folder_present? && current_application_settings[:prometheus_metrics_enabled] || false end -- cgit v1.2.3 From 44eedb22bcb799d1c8dd7b30114f3c24ad8649a4 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 20:24:31 +0200 Subject: Add info about prometheus buckets + fix cpu time --- lib/gitlab/metrics/influx_db.rb | 13 ++++++++----- lib/gitlab/metrics/method_call.rb | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb index adbc5397257..bdf7910b7c7 100644 --- a/lib/gitlab/metrics/influx_db.rb +++ b/lib/gitlab/metrics/influx_db.rb @@ -11,6 +11,8 @@ module Gitlab settings[:enabled] || false end + # Prometheus histogram buckets used for arbitrary code measurements + EXECUTION_MEASUREMENT_BUCKETS = [0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1].freeze RAILS_ROOT = Rails.root.to_s METRICS_ROOT = Rails.root.join('lib', 'gitlab', 'metrics').to_s PATH_REGEX = /^#{RAILS_ROOT}\/?/ @@ -99,22 +101,23 @@ module Gitlab cpu_stop = System.cpu_time real_stop = Time.now.to_f - real_time = (real_stop - real_start) * 1000.0 + real_time = (real_stop - real_start) cpu_time = cpu_stop - cpu_start Gitlab::Metrics.histogram("gitlab_#{name}_real_duration_seconds".to_sym, "Measure #{name}", Transaction::BASE_LABELS, - [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]) - .observe(trans.labels, real_time / 1000.0) + EXECUTION_MEASUREMENT_BUCKETS) + .observe(trans.labels, real_time) Gitlab::Metrics.histogram("gitlab_#{name}_cpu_duration_seconds".to_sym, "Measure #{name}", Transaction::BASE_LABELS, - [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2]) + EXECUTION_MEASUREMENT_BUCKETS) .observe(trans.labels, cpu_time / 1000.0) - trans.increment("#{name}_real_time", real_time, false) + # InfluxDB stores the _real_time time values as milliseconds + trans.increment("#{name}_real_time", real_time * 1000, false) trans.increment("#{name}_cpu_time", cpu_time, false) trans.increment("#{name}_call_count", 1, false) diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index f83d397fd78..2a962a82517 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -46,8 +46,7 @@ module Gitlab cpu_time = System.cpu_time - start_cpu @real_time += real_time - - @cpu_time += System.cpu_time - start_cpu + @cpu_time += cpu_time @call_count += 1 self.class.call_real_duration_histogram.observe(@transaction.labels.merge(labels), real_time / 1000.0) -- cgit v1.2.3 From c361c1e857fe165e7a838aea1ecf7a10982cdbe0 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 22:01:21 +0200 Subject: Adjust test to changed metrics methods. + remove deprecated test suite --- lib/gitlab/metrics/prometheus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb index e056a3f7b43..c32f3d103b4 100644 --- a/lib/gitlab/metrics/prometheus.rb +++ b/lib/gitlab/metrics/prometheus.rb @@ -53,7 +53,7 @@ module Gitlab return metric if metric PROVIDER_MUTEX.synchronize do - provide_metric(name) || registry.send(method, *args) + provide_metric(name) || registry.send(method, name, *args) end end -- cgit v1.2.3 From 19634c259da768700c1d6382c097fed7ef98fdca Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 22:59:27 +0200 Subject: Cleanup metrics names and removing unnecessary cache read total --- lib/gitlab/metrics/subscribers/rails_cache.rb | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index 17814f6b845..da5bf208e74 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -15,17 +15,9 @@ module Gitlab ) end - def self.metric_cache_read_hit_total - @metric_cache_read_hit_total ||= Gitlab::Metrics.counter( - :gitlab_cache_read_hit_total, - 'Cache read hit', - Transaction::BASE_LABELS - ) - end - - def self.metric_cache_read_miss_total - @metric_cache_read_miss_total ||= Gitlab::Metrics.counter( - :gitlab_cache_read_miss_total, + def self.metric_cache_misses_total + @metric_cache_misses_total ||= Gitlab::Metrics.counter( + :gitlab_cache_misses_total, 'Cache read miss', Transaction::BASE_LABELS ) @@ -38,10 +30,9 @@ module Gitlab return if event.payload[:super_operation] == :fetch if event.payload[:hit] - self.class.metric_cache_read_hit_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_hit_count, 1, false) else - self.class.metric_cache_read_miss_total.increment(current_transaction.labels) + self.class.metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1, false) end end @@ -61,14 +52,13 @@ module Gitlab def cache_fetch_hit(event) return unless current_transaction - self.class.metric_cache_read_hit_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_hit_count, 1) end def cache_generate(event) return unless current_transaction - self.class.metric_cache_read_miss_total.increment(current_transaction.labels) + self.class.metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1) end -- cgit v1.2.3 From 77e938a8f9335145e28b4d51c4ac4887121a2cf0 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 7 Sep 2017 23:13:40 +0200 Subject: Avoid using Send (but is it better?) + small rubocop cleanup --- lib/gitlab/metrics/prometheus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb index c32f3d103b4..09103b4ca2d 100644 --- a/lib/gitlab/metrics/prometheus.rb +++ b/lib/gitlab/metrics/prometheus.rb @@ -53,7 +53,7 @@ module Gitlab return metric if metric PROVIDER_MUTEX.synchronize do - provide_metric(name) || registry.send(method, name, *args) + provide_metric(name) || registry.method(method).call(name, *args) end end -- cgit v1.2.3 From 99881bb7b035429c689055c396897e8e04d31af1 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 16 Oct 2017 15:18:59 +0200 Subject: Move labels to be initialized in constructor --- lib/gitlab/metrics/method_call.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 2a962a82517..1d9cef7a6e8 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -3,7 +3,7 @@ module Gitlab # Class for tracking timing information about method calls class MethodCall BASE_LABELS = { module: nil, method: nil }.freeze - attr_reader :real_time, :cpu_time, :call_count + attr_reader :real_time, :cpu_time, :call_count, :labels def self.call_real_duration_histogram @call_real_duration_histogram ||= Gitlab::Metrics.histogram( @@ -31,6 +31,7 @@ module Gitlab @method_name = method_name @transaction = transaction @name = name + @labels = { module: @module_name, method: @method_name } @real_time = 0 @cpu_time = 0 @call_count = 0 @@ -55,10 +56,6 @@ module Gitlab retval end - def labels - @labels ||= { module: @module_name, method: @method_name } - end - # Returns a Metric instance of the current method call. def to_metric Metric.new( -- cgit v1.2.3 From 929418da32bef6e56c363a0f7bb1ffe27edf511c Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Mon, 16 Oct 2017 15:57:33 +0200 Subject: Web/Sidekiq transaction split --- lib/gitlab/metrics/base_transaction.rb | 176 +++++++++++++++++++++++++++++++++ lib/gitlab/metrics/rack_middleware.rb | 2 +- lib/gitlab/metrics/web_transaction.rb | 85 ++++++++++++++++ 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/metrics/base_transaction.rb create mode 100644 lib/gitlab/metrics/web_transaction.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/base_transaction.rb b/lib/gitlab/metrics/base_transaction.rb new file mode 100644 index 00000000000..a783bfeccd5 --- /dev/null +++ b/lib/gitlab/metrics/base_transaction.rb @@ -0,0 +1,176 @@ +module Gitlab + module Metrics + # Class for storing metrics information of a single transaction. + class BaseTransaction + BASE_LABELS = { }.freeze + + THREAD_KEY = :_gitlab_metrics_transaction + + # The series to store events (e.g. Git pushes) in. + EVENT_SERIES = 'events'.freeze + + attr_reader :tags, :values, :method, :metrics + + def self.current + Thread.current[THREAD_KEY] + end + + # action - A String describing the action performed, usually the class + # plus method name. + def initialize(env) + @metrics = [] + @methods = {} + + @started_at = nil + @finished_at = nil + + @values = Hash.new(0) + @tags = {} + @env = env + + @memory_before = 0 + @memory_after = 0 + end + + def duration + @finished_at ? (@finished_at - @started_at) : 0.0 + end + + def allocated_memory + @memory_after - @memory_before + end + + def labels + {} + end + + def run + Thread.current[THREAD_KEY] = self + + @memory_before = System.memory_usage + @started_at = System.monotonic_time + + yield + ensure + @memory_after = System.memory_usage + @finished_at = System.monotonic_time + + self.class.metric_transaction_duration_seconds.observe(labels, duration * 1000) + self.class.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) + + Thread.current[THREAD_KEY] = nil + end + + def add_metric(series, values, tags = {}) + @metrics << Metric.new("#{Metrics.series_prefix}#{series}", values, tags) + end + + # Tracks a business level event + # + # Business level events including events such as Git pushes, Emails being + # sent, etc. + # + # 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 = {}) + self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) + @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) + end + + # Returns a MethodCall object for the given name. + def method_call_for(name, module_name, method_name) + unless method = @methods[name] + @methods[name] = method = MethodCall.new(name, module_name, method_name, self) + end + + method + end + + def increment(name, value, use_prometheus = true) + self.class.metric_transaction_counter(name).increment(labels, value) if use_prometheus + @values[name] += value + end + + def set(name, value, use_prometheus = true) + self.class.metric_transaction_gauge(name).set(labels, value) if use_prometheus + @values[name] = value + end + + def finish + track_self + submit + end + + def track_self + values = { duration: duration, allocated_memory: allocated_memory } + + @values.each do |name, value| + values[name] = value + end + + add_metric('transactions', values, @tags) + end + + def submit + submit = @metrics.dup + + @methods.each do |name, method| + submit << method.to_metric if method.above_threshold? + end + + submit_hashes = submit.map do |metric| + hash = metric.to_hash + + hash[:tags][:action] ||= action if action && !metric.event? + + hash + end + + Metrics.submit_metrics(submit_hashes) + end + + private + + def self.metric_transaction_duration_seconds + @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_seconds, + 'Transaction duration', + BASE_LABELS, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + + def self.metric_transaction_allocated_memory_bytes + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, + 'Transaction allocated memory bytes', + BASE_LABELS, + [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] + ) + end + + def self.metric_event_counter(event_name, tags) + @metric_event_counters ||= {} + @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_event_#{event_name}_total".to_sym, + "Transaction event #{event_name} counter", + tags.merge(BASE_LABELS) + ) + end + + def self.metric_transaction_counter(name) + @metric_transaction_counters ||= {} + @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS + ) + end + + def self.metric_transaction_gauge(name) + @metric_transaction_gauges ||= {} + @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( + "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum + ) + end + end + end +end diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index a5aa0c49551..2d45765df3f 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -28,7 +28,7 @@ module Gitlab end def transaction_from_env(env) - trans = Transaction.new(env) + trans = WebTransaction.new(env) trans.set(:request_uri, filtered_path(env), false) trans.set(:request_method, env['REQUEST_METHOD'], false) diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb new file mode 100644 index 00000000000..e7ac9e68f9f --- /dev/null +++ b/lib/gitlab/metrics/web_transaction.rb @@ -0,0 +1,85 @@ +module Gitlab + module Metrics + # Class for storing metrics information of a single transaction. + class WebTransaction + BASE_LABELS = { controller: nil, action: nil }.freeze + CONTROLLER_KEY = 'action_controller.instance'.freeze + ENDPOINT_KEY = 'api.endpoint'.freeze + + CONTENT_TYPES = { + 'text/html' => :html, + 'text/plain' => :txt, + 'application/json' => :json, + 'text/js' => :js, + 'application/atom+xml' => :atom, + 'image/png' => :png, + 'image/jpeg' => :jpeg, + 'image/gif' => :gif, + 'image/svg+xml' => :svg + }.freeze + + attr_reader :tags, :values, :method, :metrics + + def action + "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? + end + + def labels + return @labels if @labels + + # memoize transaction labels only source env variables were present + @labels = if @env[CONTROLLER_KEY] + labels_from_controller || {} + elsif @env[ENDPOINT_KEY] + labels_from_endpoint || {} + end + + @labels || {} + end + + private + + def labels_from_controller + controller = @env[CONTROLLER_KEY] + + action = "#{controller.action_name}" + suffix = CONTENT_TYPES[controller.content_type] + + if suffix && suffix != :html + action += ".#{suffix}" + end + + { controller: controller.class.name, action: action } + end + + def labels_from_endpoint + endpoint = @env[ENDPOINT_KEY] + + begin + route = endpoint.route + rescue + # endpoint.route is calling env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info] + # but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response + # so we're rescuing exceptions and bailing out + end + + if route + path = endpoint_paths_cache[route.request_method][route.path] + { controller: 'Grape', action: "#{route.request_method} #{path}" } + end + end + + def endpoint_paths_cache + @endpoint_paths_cache ||= Hash.new do |hash, http_method| + hash[http_method] = Hash.new do |inner_hash, raw_path| + inner_hash[raw_path] = endpoint_instrumentable_path(raw_path) + end + end + end + + def endpoint_instrumentable_path(raw_path) + raw_path.sub('(.:format)', '').sub('/:version', '') + end + end + end +end -- cgit v1.2.3 From 534f6b1125313b850f8c68b90ca400ba3020417f Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 17 Oct 2017 16:06:52 +0200 Subject: Tests for Web transaction and remove simple transacton --- lib/gitlab/metrics/base_transaction.rb | 24 +-- lib/gitlab/metrics/sidekiq_middleware.rb | 2 +- lib/gitlab/metrics/sidekiq_transaction.rb | 15 ++ lib/gitlab/metrics/transaction.rb | 245 ------------------------------ lib/gitlab/metrics/web_transaction.rb | 11 +- 5 files changed, 33 insertions(+), 264 deletions(-) create mode 100644 lib/gitlab/metrics/sidekiq_transaction.rb delete mode 100644 lib/gitlab/metrics/transaction.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/base_transaction.rb b/lib/gitlab/metrics/base_transaction.rb index a783bfeccd5..cf616cb13a3 100644 --- a/lib/gitlab/metrics/base_transaction.rb +++ b/lib/gitlab/metrics/base_transaction.rb @@ -2,7 +2,8 @@ module Gitlab module Metrics # Class for storing metrics information of a single transaction. class BaseTransaction - BASE_LABELS = { }.freeze + # base labels shared among all transactions + BASE_LABELS = { controller: nil, action: nil }.freeze THREAD_KEY = :_gitlab_metrics_transaction @@ -15,9 +16,7 @@ module Gitlab Thread.current[THREAD_KEY] end - # action - A String describing the action performed, usually the class - # plus method name. - def initialize(env) + def initialize @metrics = [] @methods = {} @@ -26,7 +25,6 @@ module Gitlab @values = Hash.new(0) @tags = {} - @env = env @memory_before = 0 @memory_after = 0 @@ -40,10 +38,6 @@ module Gitlab @memory_after - @memory_before end - def labels - {} - end - def run Thread.current[THREAD_KEY] = self @@ -120,7 +114,6 @@ module Gitlab submit_hashes = submit.map do |metric| hash = metric.to_hash - hash[:tags][:action] ||= action if action && !metric.event? hash @@ -129,7 +122,16 @@ module Gitlab Metrics.submit_metrics(submit_hashes) end - private + def labels + BASE_LABELS + end + + # returns string describing the action performed, usually the class plus method name. + def action + "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? + end + + protected def self.metric_transaction_duration_seconds @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index b983a40611f..4dbf404f857 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -5,7 +5,7 @@ module Gitlab # This middleware is intended to be used as a server-side middleware. class SidekiqMiddleware def call(worker, message, queue) - trans = Transaction.new("#{worker.class.name}#perform") + trans = SidekiqTransaction.new(worker.class) begin # Old gitlad-shell messages don't provide enqueued_at/created_at attributes diff --git a/lib/gitlab/metrics/sidekiq_transaction.rb b/lib/gitlab/metrics/sidekiq_transaction.rb new file mode 100644 index 00000000000..4024b892ecc --- /dev/null +++ b/lib/gitlab/metrics/sidekiq_transaction.rb @@ -0,0 +1,15 @@ +module Gitlab + module Metrics + class SidekiqTransaction + def initialize(worker_class) + @worker_class = worker_class + end + + protected + + def labels + { controller: worker.class.name, action: 'perform' } + end + end + end +end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb deleted file mode 100644 index 5bf8580d91f..00000000000 --- a/lib/gitlab/metrics/transaction.rb +++ /dev/null @@ -1,245 +0,0 @@ -module Gitlab - module Metrics - # Class for storing metrics information of a single transaction. - class Transaction - BASE_LABELS = { controller: nil, action: nil }.freeze - CONTROLLER_KEY = 'action_controller.instance'.freeze - ENDPOINT_KEY = 'api.endpoint'.freeze - - CONTENT_TYPES = { - 'text/html' => :html, - 'text/plain' => :txt, - 'application/json' => :json, - 'text/js' => :js, - 'application/atom+xml' => :atom, - 'image/png' => :png, - 'image/jpeg' => :jpeg, - 'image/gif' => :gif, - 'image/svg+xml' => :svg - }.freeze - - THREAD_KEY = :_gitlab_metrics_transaction - - # The series to store events (e.g. Git pushes) in. - EVENT_SERIES = 'events'.freeze - - attr_reader :tags, :values, :method, :metrics - - def self.current - Thread.current[THREAD_KEY] - end - - # action - A String describing the action performed, usually the class - # plus method name. - def initialize(env) - @metrics = [] - @methods = {} - - @started_at = nil - @finished_at = nil - - @values = Hash.new(0) - @tags = {} - @env = env - - @memory_before = 0 - @memory_after = 0 - end - - def duration - @finished_at ? (@finished_at - @started_at) : 0.0 - end - - def allocated_memory - @memory_after - @memory_before - end - - def action - "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? - end - - def labels - return @labels if @labels - - # memoize transaction labels only source env variables were present - @labels = if @env[CONTROLLER_KEY] - labels_from_controller || {} - elsif @env[ENDPOINT_KEY] - labels_from_endpoint || {} - end - - @labels || {} - end - - def run - Thread.current[THREAD_KEY] = self - - @memory_before = System.memory_usage - @started_at = System.monotonic_time - - yield - ensure - @memory_after = System.memory_usage - @finished_at = System.monotonic_time - - Transaction.metric_transaction_duration_seconds.observe(labels, duration * 1000) - Transaction.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) - - Thread.current[THREAD_KEY] = nil - end - - def add_metric(series, values, tags = {}) - @metrics << Metric.new("#{Metrics.series_prefix}#{series}", values, tags) - end - - # Tracks a business level event - # - # Business level events including events such as Git pushes, Emails being - # sent, etc. - # - # 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 = {}) - self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) - @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) - end - - # Returns a MethodCall object for the given name. - def method_call_for(name, module_name, method_name) - unless method = @methods[name] - @methods[name] = method = MethodCall.new(name, module_name, method_name, self) - end - - method - end - - def increment(name, value, use_prometheus = true) - self.class.metric_transaction_counter(name).increment(labels, value) if use_prometheus - @values[name] += value - end - - def set(name, value, use_prometheus = true) - self.class.metric_transaction_gauge(name).set(labels, value) if use_prometheus - @values[name] = value - end - - def finish - track_self - submit - end - - def track_self - values = { duration: duration, allocated_memory: allocated_memory } - - @values.each do |name, value| - values[name] = value - end - - add_metric('transactions', values, @tags) - end - - def submit - submit = @metrics.dup - - @methods.each do |name, method| - submit << method.to_metric if method.above_threshold? - end - - submit_hashes = submit.map do |metric| - hash = metric.to_hash - - hash[:tags][:action] ||= action if action && !metric.event? - - hash - end - - Metrics.submit_metrics(submit_hashes) - end - - private - - def self.metric_transaction_duration_seconds - @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_transaction_duration_seconds, - 'Transaction duration', - BASE_LABELS, - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - - def self.metric_transaction_allocated_memory_bytes - @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_bytes, - 'Transaction allocated memory bytes', - BASE_LABELS, - [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] - ) - end - - def self.metric_event_counter(event_name, tags) - @metric_event_counters ||= {} - @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_event_#{event_name}_total".to_sym, - "Transaction event #{event_name} counter", - tags.merge(BASE_LABELS) - ) - end - - def self.metric_transaction_counter(name) - @metric_transaction_counters ||= {} - @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS - ) - end - - def self.metric_transaction_gauge(name) - @metric_transaction_gauges ||= {} - @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( - "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum - ) - end - - def labels_from_controller - controller = @env[CONTROLLER_KEY] - - action = "#{controller.action_name}" - suffix = CONTENT_TYPES[controller.content_type] - - if suffix && suffix != :html - action += ".#{suffix}" - end - - { controller: controller.class.name, action: action } - end - - def labels_from_endpoint - endpoint = @env[ENDPOINT_KEY] - - begin - route = endpoint.route - rescue - # endpoint.route is calling env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info] - # but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response - # so we're rescuing exceptions and bailing out - end - - if route - path = endpoint_paths_cache[route.request_method][route.path] - { controller: 'Grape', action: "#{route.request_method} #{path}" } - end - end - - def endpoint_paths_cache - @endpoint_paths_cache ||= Hash.new do |hash, http_method| - hash[http_method] = Hash.new do |inner_hash, raw_path| - inner_hash[raw_path] = endpoint_instrumentable_path(raw_path) - end - end - end - - def endpoint_instrumentable_path(raw_path) - raw_path.sub('(.:format)', '').sub('/:version', '') - end - end - end -end diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb index e7ac9e68f9f..f85929b1c4e 100644 --- a/lib/gitlab/metrics/web_transaction.rb +++ b/lib/gitlab/metrics/web_transaction.rb @@ -1,8 +1,6 @@ module Gitlab module Metrics - # Class for storing metrics information of a single transaction. - class WebTransaction - BASE_LABELS = { controller: nil, action: nil }.freeze + class WebTransaction < BaseTransaction CONTROLLER_KEY = 'action_controller.instance'.freeze ENDPOINT_KEY = 'api.endpoint'.freeze @@ -18,10 +16,9 @@ module Gitlab 'image/svg+xml' => :svg }.freeze - attr_reader :tags, :values, :method, :metrics - - def action - "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? + def initialize(env) + super() + @env = env end def labels -- cgit v1.2.3 From 043545de3a15251c69e8a7ccfd9f608e598d4ff2 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 17 Oct 2017 16:23:33 +0200 Subject: Remove unnecessary namespace use --- lib/gitlab/metrics/histogram_buckets.rb | 0 lib/gitlab/metrics/samplers/ruby_sampler.rb | 14 +++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 lib/gitlab/metrics/histogram_buckets.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/histogram_buckets.rb b/lib/gitlab/metrics/histogram_buckets.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index 9897635960a..8b5a60e6b8b 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -23,7 +23,7 @@ module Gitlab def initialize(interval) super(interval) - if Gitlab::Metrics.mri? + if Metrics.mri? require 'allocations' Allocations.start @@ -32,15 +32,15 @@ module Gitlab def init_metrics metrics = {} - metrics[:sampler_duration] = Gitlab::Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', {}) - metrics[:total_time] = Gitlab::Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum) + metrics[:sampler_duration] = Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', {}) + metrics[:total_time] = Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum) GC.stat.keys.each do |key| - metrics[key] = Gitlab::Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum) + metrics[key] = Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum) end - metrics[:objects_total] = Gitlab::Metrics.gauge(with_prefix(:objects, :total), 'Objects total', labels.merge(class: nil), :livesum) - metrics[:memory_usage] = Gitlab::Metrics.gauge(with_prefix(:memory, :usage_total), 'Memory used total', labels, :livesum) - metrics[:file_descriptors] = Gitlab::Metrics.gauge(with_prefix(:file, :descriptors_total), 'File descriptors total', labels, :livesum) + metrics[:objects_total] = Metrics.gauge(with_prefix(:objects, :total), 'Objects total', labels.merge(class: nil), :livesum) + metrics[:memory_usage] = Metrics.gauge(with_prefix(:memory, :usage_total), 'Memory used total', labels, :livesum) + metrics[:file_descriptors] = Metrics.gauge(with_prefix(:file, :descriptors_total), 'File descriptors total', labels, :livesum) metrics end -- cgit v1.2.3 From 30a4bb66281d1d83028b3258156957db23077104 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 19 Oct 2017 12:24:15 +0300 Subject: Fix sidekiq middleware tests --- lib/gitlab/metrics/base_transaction.rb | 178 ------------------------------ lib/gitlab/metrics/sidekiq_transaction.rb | 5 +- lib/gitlab/metrics/transaction.rb | 176 +++++++++++++++++++++++++++++ lib/gitlab/metrics/web_transaction.rb | 2 +- 4 files changed, 180 insertions(+), 181 deletions(-) delete mode 100644 lib/gitlab/metrics/base_transaction.rb create mode 100644 lib/gitlab/metrics/transaction.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/base_transaction.rb b/lib/gitlab/metrics/base_transaction.rb deleted file mode 100644 index cf616cb13a3..00000000000 --- a/lib/gitlab/metrics/base_transaction.rb +++ /dev/null @@ -1,178 +0,0 @@ -module Gitlab - module Metrics - # Class for storing metrics information of a single transaction. - class BaseTransaction - # base labels shared among all transactions - BASE_LABELS = { controller: nil, action: nil }.freeze - - THREAD_KEY = :_gitlab_metrics_transaction - - # The series to store events (e.g. Git pushes) in. - EVENT_SERIES = 'events'.freeze - - attr_reader :tags, :values, :method, :metrics - - def self.current - Thread.current[THREAD_KEY] - end - - def initialize - @metrics = [] - @methods = {} - - @started_at = nil - @finished_at = nil - - @values = Hash.new(0) - @tags = {} - - @memory_before = 0 - @memory_after = 0 - end - - def duration - @finished_at ? (@finished_at - @started_at) : 0.0 - end - - def allocated_memory - @memory_after - @memory_before - end - - def run - Thread.current[THREAD_KEY] = self - - @memory_before = System.memory_usage - @started_at = System.monotonic_time - - yield - ensure - @memory_after = System.memory_usage - @finished_at = System.monotonic_time - - self.class.metric_transaction_duration_seconds.observe(labels, duration * 1000) - self.class.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) - - Thread.current[THREAD_KEY] = nil - end - - def add_metric(series, values, tags = {}) - @metrics << Metric.new("#{Metrics.series_prefix}#{series}", values, tags) - end - - # Tracks a business level event - # - # Business level events including events such as Git pushes, Emails being - # sent, etc. - # - # 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 = {}) - self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) - @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) - end - - # Returns a MethodCall object for the given name. - def method_call_for(name, module_name, method_name) - unless method = @methods[name] - @methods[name] = method = MethodCall.new(name, module_name, method_name, self) - end - - method - end - - def increment(name, value, use_prometheus = true) - self.class.metric_transaction_counter(name).increment(labels, value) if use_prometheus - @values[name] += value - end - - def set(name, value, use_prometheus = true) - self.class.metric_transaction_gauge(name).set(labels, value) if use_prometheus - @values[name] = value - end - - def finish - track_self - submit - end - - def track_self - values = { duration: duration, allocated_memory: allocated_memory } - - @values.each do |name, value| - values[name] = value - end - - add_metric('transactions', values, @tags) - end - - def submit - submit = @metrics.dup - - @methods.each do |name, method| - submit << method.to_metric if method.above_threshold? - end - - submit_hashes = submit.map do |metric| - hash = metric.to_hash - hash[:tags][:action] ||= action if action && !metric.event? - - hash - end - - Metrics.submit_metrics(submit_hashes) - end - - def labels - BASE_LABELS - end - - # returns string describing the action performed, usually the class plus method name. - def action - "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? - end - - protected - - def self.metric_transaction_duration_seconds - @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_transaction_duration_seconds, - 'Transaction duration', - BASE_LABELS, - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - - def self.metric_transaction_allocated_memory_bytes - @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_bytes, - 'Transaction allocated memory bytes', - BASE_LABELS, - [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] - ) - end - - def self.metric_event_counter(event_name, tags) - @metric_event_counters ||= {} - @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_event_#{event_name}_total".to_sym, - "Transaction event #{event_name} counter", - tags.merge(BASE_LABELS) - ) - end - - def self.metric_transaction_counter(name) - @metric_transaction_counters ||= {} - @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS - ) - end - - def self.metric_transaction_gauge(name) - @metric_transaction_gauges ||= {} - @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( - "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum - ) - end - end - end -end diff --git a/lib/gitlab/metrics/sidekiq_transaction.rb b/lib/gitlab/metrics/sidekiq_transaction.rb index 4024b892ecc..797594f12e1 100644 --- a/lib/gitlab/metrics/sidekiq_transaction.rb +++ b/lib/gitlab/metrics/sidekiq_transaction.rb @@ -1,14 +1,15 @@ module Gitlab module Metrics - class SidekiqTransaction + class SidekiqTransaction < Transaction def initialize(worker_class) + super() @worker_class = worker_class end protected def labels - { controller: worker.class.name, action: 'perform' } + { controller: @worker_class.name, action: 'perform' } end end end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb new file mode 100644 index 00000000000..372922a986e --- /dev/null +++ b/lib/gitlab/metrics/transaction.rb @@ -0,0 +1,176 @@ +module Gitlab + module Metrics + # Class for storing metrics information of a single transaction. + class Transaction + # base labels shared among all transactions + BASE_LABELS = { controller: nil, action: nil }.freeze + + THREAD_KEY = :_gitlab_metrics_transaction + + # The series to store events (e.g. Git pushes) in. + EVENT_SERIES = 'events'.freeze + + attr_reader :tags, :values, :method, :metrics + + def self.current + Thread.current[THREAD_KEY] + end + + def initialize + @metrics = [] + @methods = {} + + @started_at = nil + @finished_at = nil + + @values = Hash.new(0) + @tags = {} + + @memory_before = 0 + @memory_after = 0 + end + + def duration + @finished_at ? (@finished_at - @started_at) : 0.0 + end + + def allocated_memory + @memory_after - @memory_before + end + + def run + Thread.current[THREAD_KEY] = self + + @memory_before = System.memory_usage + @started_at = System.monotonic_time + + yield + ensure + @memory_after = System.memory_usage + @finished_at = System.monotonic_time + + self.class.metric_transaction_duration_seconds.observe(labels, duration * 1000) + self.class.metric_transaction_allocated_memory_bytes.observe(labels, allocated_memory * 1024.0) + + Thread.current[THREAD_KEY] = nil + end + + def add_metric(series, values, tags = {}) + @metrics << Metric.new("#{Metrics.series_prefix}#{series}", values, tags) + end + + # Tracks a business level event + # + # Business level events including events such as Git pushes, Emails being + # sent, etc. + # + # 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 = {}) + self.class.metric_event_counter(event_name, tags).increment(tags.merge(labels)) + @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) + end + + # Returns a MethodCall object for the given name. + def method_call_for(name, module_name, method_name) + unless method = @methods[name] + @methods[name] = method = MethodCall.new(name, module_name, method_name, self) + end + + method + end + + def increment(name, value, use_prometheus = true) + self.class.metric_transaction_counter(name).increment(labels, value) if use_prometheus + @values[name] += value + end + + def set(name, value, use_prometheus = true) + self.class.metric_transaction_gauge(name).set(labels, value) if use_prometheus + @values[name] = value + end + + def finish + track_self + submit + end + + def track_self + values = { duration: duration, allocated_memory: allocated_memory } + + @values.each do |name, value| + values[name] = value + end + + add_metric('transactions', values, @tags) + end + + def submit + submit = @metrics.dup + + @methods.each do |name, method| + submit << method.to_metric if method.above_threshold? + end + + submit_hashes = submit.map do |metric| + hash = metric.to_hash + hash[:tags][:action] ||= action if action && !metric.event? + + hash + end + + Metrics.submit_metrics(submit_hashes) + end + + def labels + BASE_LABELS + end + + # returns string describing the action performed, usually the class plus method name. + def action + "#{labels[:controller]}##{labels[:action]}" if labels && !labels.empty? + end + + def self.metric_transaction_duration_seconds + @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_seconds, + 'Transaction duration', + BASE_LABELS, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + + def self.metric_transaction_allocated_memory_bytes + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, + 'Transaction allocated memory bytes', + BASE_LABELS, + [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] + ) + end + + def self.metric_event_counter(event_name, tags) + @metric_event_counters ||= {} + @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_event_#{event_name}_total".to_sym, + "Transaction event #{event_name} counter", + tags.merge(BASE_LABELS) + ) + end + + def self.metric_transaction_counter(name) + @metric_transaction_counters ||= {} + @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS + ) + end + + def self.metric_transaction_gauge(name) + @metric_transaction_gauges ||= {} + @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( + "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum + ) + end + end + end +end diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb index f85929b1c4e..89ff02a96d6 100644 --- a/lib/gitlab/metrics/web_transaction.rb +++ b/lib/gitlab/metrics/web_transaction.rb @@ -1,6 +1,6 @@ module Gitlab module Metrics - class WebTransaction < BaseTransaction + class WebTransaction < Transaction CONTROLLER_KEY = 'action_controller.instance'.freeze ENDPOINT_KEY = 'api.endpoint'.freeze -- cgit v1.2.3 From 735365a367add91790e8e00a44b78f9933622c6b Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Thu, 19 Oct 2017 14:48:27 +0300 Subject: rename BackgroundMigration to SidekiqMigration --- lib/gitlab/metrics/background_transaction.rb | 16 ++++++++++++++++ lib/gitlab/metrics/histogram_buckets.rb | 0 lib/gitlab/metrics/sidekiq_middleware.rb | 2 +- lib/gitlab/metrics/sidekiq_transaction.rb | 16 ---------------- 4 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 lib/gitlab/metrics/background_transaction.rb delete mode 100644 lib/gitlab/metrics/histogram_buckets.rb delete mode 100644 lib/gitlab/metrics/sidekiq_transaction.rb (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/background_transaction.rb b/lib/gitlab/metrics/background_transaction.rb new file mode 100644 index 00000000000..d01de5eef0a --- /dev/null +++ b/lib/gitlab/metrics/background_transaction.rb @@ -0,0 +1,16 @@ +module Gitlab + module Metrics + class BackgroundTransaction < Transaction + def initialize(worker_class) + super() + @worker_class = worker_class + end + + protected + + def labels + { controller: @worker_class.name, action: 'perform' } + end + end + end +end diff --git a/lib/gitlab/metrics/histogram_buckets.rb b/lib/gitlab/metrics/histogram_buckets.rb deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index 4dbf404f857..55c707d5386 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -5,7 +5,7 @@ module Gitlab # This middleware is intended to be used as a server-side middleware. class SidekiqMiddleware def call(worker, message, queue) - trans = SidekiqTransaction.new(worker.class) + trans = BackgroundTransaction.new(worker.class) begin # Old gitlad-shell messages don't provide enqueued_at/created_at attributes diff --git a/lib/gitlab/metrics/sidekiq_transaction.rb b/lib/gitlab/metrics/sidekiq_transaction.rb deleted file mode 100644 index 797594f12e1..00000000000 --- a/lib/gitlab/metrics/sidekiq_transaction.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gitlab - module Metrics - class SidekiqTransaction < Transaction - def initialize(worker_class) - super() - @worker_class = worker_class - end - - protected - - def labels - { controller: @worker_class.name, action: 'perform' } - end - end - end -end -- cgit v1.2.3 From 67b3e3d84a66b60c8633ef9f4fa6ba5239cb36f6 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 24 Oct 2017 10:09:13 +0300 Subject: move metrics for ActiveRecord, RailsCache and queue duration to instance variables --- lib/gitlab/metrics/subscribers/active_record.rb | 20 ++++++------- lib/gitlab/metrics/subscribers/rails_cache.rb | 40 ++++++++++++------------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 842a4cfaf55..064299f40c8 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -5,18 +5,9 @@ module Gitlab class ActiveRecord < ActiveSupport::Subscriber attach_to :active_record - def self.metric_sql_duration_seconds - @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_sql_duration_seconds, - 'SQL time', - Transaction::BASE_LABELS, - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - def sql(event) return unless current_transaction - self.class.metric_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0) + metric_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0) current_transaction.increment(:sql_duration, event.duration, false) current_transaction.increment(:sql_count, 1, false) @@ -27,6 +18,15 @@ module Gitlab def current_transaction Transaction.current end + + def metric_sql_duration_seconds + @metric_sql_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_sql_duration_seconds, + 'SQL time', + Transaction::BASE_LABELS, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end end end end diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index da5bf208e74..7383e384d66 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -6,23 +6,6 @@ module Gitlab class RailsCache < ActiveSupport::Subscriber attach_to :active_support - def self.metric_cache_operation_duration_seconds - @metric_cache_operation_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_cache_operation_duration_seconds, - 'Cache access time', - Transaction::BASE_LABELS.merge({ action: nil }), - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) - end - - def self.metric_cache_misses_total - @metric_cache_misses_total ||= Gitlab::Metrics.counter( - :gitlab_cache_misses_total, - 'Cache read miss', - Transaction::BASE_LABELS - ) - end - def cache_read(event) observe(:read, event.duration) @@ -32,7 +15,7 @@ module Gitlab if event.payload[:hit] current_transaction.increment(:cache_read_hit_count, 1, false) else - self.class.metric_cache_misses_total.increment(current_transaction.labels) + self.metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1, false) end end @@ -58,14 +41,14 @@ module Gitlab def cache_generate(event) return unless current_transaction - self.class.metric_cache_misses_total.increment(current_transaction.labels) + self.metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1) end def observe(key, duration) return unless current_transaction - self.class.metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0) + self.metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0) current_transaction.increment(:cache_duration, duration, false) current_transaction.increment(:cache_count, 1, false) current_transaction.increment("cache_#{key}_duration".to_sym, duration, false) @@ -77,6 +60,23 @@ module Gitlab def current_transaction Transaction.current end + + def metric_cache_operation_duration_seconds + @metric_cache_operation_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_cache_operation_duration_seconds, + 'Cache access time', + Transaction::BASE_LABELS.merge({ action: nil }), + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end + + def metric_cache_misses_total + @metric_cache_misses_total ||= Gitlab::Metrics.counter( + :gitlab_cache_misses_total, + 'Cache read miss', + Transaction::BASE_LABELS + ) + end end end end -- cgit v1.2.3 From a300787fa99faccb7002a3a8df6f703b08e1a090 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 24 Oct 2017 10:20:49 +0300 Subject: Use Mutex to guard metrics creation in transaction. Switch action view to threadsafe instance variables --- lib/gitlab/metrics/subscribers/action_view.rb | 4 +- lib/gitlab/metrics/transaction.rb | 73 +++++++++++++++++---------- 2 files changed, 49 insertions(+), 28 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index f7d5f8f0279..891fa92e3f7 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -15,7 +15,7 @@ module Gitlab private - def self.metric_view_rendering_duration_seconds + def metric_view_rendering_duration_seconds @metric_view_rendering_duration_seconds ||= Gitlab::Metrics.histogram( :gitlab_view_rendering_duration_seconds, 'View rendering time', @@ -28,7 +28,7 @@ module Gitlab values = values_for(event) tags = tags_for(event) - self.class.metric_view_rendering_duration_seconds.observe( + self.metric_view_rendering_duration_seconds.observe( current_transaction.labels.merge(tags), event.duration ) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 372922a986e..ee3afc5ffdb 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -6,6 +6,7 @@ module Gitlab BASE_LABELS = { controller: nil, action: nil }.freeze THREAD_KEY = :_gitlab_metrics_transaction + METRICS_MUTEX = Mutex.new # The series to store events (e.g. Git pushes) in. EVENT_SERIES = 'events'.freeze @@ -132,44 +133,64 @@ module Gitlab end def self.metric_transaction_duration_seconds - @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( - :gitlab_transaction_duration_seconds, - 'Transaction duration', - BASE_LABELS, - [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] - ) + return @metric_transaction_duration_seconds if @metric_transaction_duration_seconds + + METRICS_MUTEX.synchronize do + @metric_transaction_duration_seconds ||= Gitlab::Metrics.histogram( + :gitlab_transaction_duration_seconds, + 'Transaction duration', + BASE_LABELS, + [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.500, 2.0, 10.0] + ) + end end def self.metric_transaction_allocated_memory_bytes - @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( - :gitlab_transaction_allocated_memory_bytes, - 'Transaction allocated memory bytes', - BASE_LABELS, - [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] - ) + return @metric_transaction_allocated_memory_bytes if @metric_transaction_allocated_memory_bytes + + METRICS_MUTEX.synchronize do + @metric_transaction_allocated_memory_bytes ||= Gitlab::Metrics.histogram( + :gitlab_transaction_allocated_memory_bytes, + 'Transaction allocated memory bytes', + BASE_LABELS, + [1000, 10000, 20000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 100000000] + ) + end end def self.metric_event_counter(event_name, tags) - @metric_event_counters ||= {} - @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_event_#{event_name}_total".to_sym, - "Transaction event #{event_name} counter", - tags.merge(BASE_LABELS) - ) + return @metric_event_counters[event_name] if @metric_event_counters&.has_key?(event_name) + + METRICS_MUTEX.synchronize do + @metric_event_counters ||= {} + @metric_event_counters[event_name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_event_#{event_name}_total".to_sym, + "Transaction event #{event_name} counter", + tags.merge(BASE_LABELS) + ) + end end def self.metric_transaction_counter(name) - @metric_transaction_counters ||= {} - @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( - "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS - ) + return @metric_transaction_counters[name] if @metric_transaction_counters&.has_key?(name) + + METRICS_MUTEX.synchronize do + @metric_transaction_counters ||= {} + @metric_transaction_counters[name] ||= Gitlab::Metrics.counter( + "gitlab_transaction_#{name}_total".to_sym, "Transaction #{name} counter", BASE_LABELS + ) + end end def self.metric_transaction_gauge(name) - @metric_transaction_gauges ||= {} - @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( - "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum - ) + return @metric_transaction_gauges[name] if @metric_transaction_gauges&.has_key?(name) + + METRICS_MUTEX.synchronize do + @metric_transaction_gauges ||= {} + @metric_transaction_gauges[name] ||= Gitlab::Metrics.gauge( + "gitlab_transaction_#{name}".to_sym, "Transaction gauge #{name}", BASE_LABELS, :livesum + ) + end end end end -- cgit v1.2.3 From c9f0070b649cac40eb7a93639726aee63a1f4cc8 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 24 Oct 2017 11:57:29 +0300 Subject: Make subscriber tests pass, after refactoring metrics --- lib/gitlab/metrics/subscribers/action_view.rb | 2 +- lib/gitlab/metrics/subscribers/rails_cache.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 891fa92e3f7..3da474fc1ec 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -28,7 +28,7 @@ module Gitlab values = values_for(event) tags = tags_for(event) - self.metric_view_rendering_duration_seconds.observe( + metric_view_rendering_duration_seconds.observe( current_transaction.labels.merge(tags), event.duration ) diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb index 7383e384d66..efd3c9daf79 100644 --- a/lib/gitlab/metrics/subscribers/rails_cache.rb +++ b/lib/gitlab/metrics/subscribers/rails_cache.rb @@ -15,7 +15,7 @@ module Gitlab if event.payload[:hit] current_transaction.increment(:cache_read_hit_count, 1, false) else - self.metric_cache_misses_total.increment(current_transaction.labels) + metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1, false) end end @@ -41,14 +41,14 @@ module Gitlab def cache_generate(event) return unless current_transaction - self.metric_cache_misses_total.increment(current_transaction.labels) + metric_cache_misses_total.increment(current_transaction.labels) current_transaction.increment(:cache_read_miss_count, 1) end def observe(key, duration) return unless current_transaction - self.metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0) + metric_cache_operation_duration_seconds.observe(current_transaction.labels.merge({ operation: key }), duration / 1000.0) current_transaction.increment(:cache_duration, duration, false) current_transaction.increment(:cache_count, 1, false) current_transaction.increment("cache_#{key}_duration".to_sym, duration, false) -- cgit v1.2.3 From 5a085dc12670e6ececa566fc16172da08bac0972 Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Fri, 3 Nov 2017 12:37:08 +0100 Subject: Add missing mutex guard to method call metrics --- lib/gitlab/metrics/method_call.rb | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'lib/gitlab/metrics') diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index 1d9cef7a6e8..90235095306 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -2,25 +2,34 @@ module Gitlab module Metrics # Class for tracking timing information about method calls class MethodCall + MUTEX = Mutex.new BASE_LABELS = { module: nil, method: nil }.freeze attr_reader :real_time, :cpu_time, :call_count, :labels def self.call_real_duration_histogram - @call_real_duration_histogram ||= Gitlab::Metrics.histogram( - :gitlab_method_call_real_duration_seconds, - 'Method calls real duration', - Transaction::BASE_LABELS.merge(BASE_LABELS), - [0.1, 0.2, 0.5, 1, 2, 5, 10] - ) + return @call_real_duration_histogram if @call_real_duration_histogram + + MUTEX.synchronize do + @call_real_duration_histogram ||= Gitlab::Metrics.histogram( + :gitlab_method_call_real_duration_seconds, + 'Method calls real duration', + Transaction::BASE_LABELS.merge(BASE_LABELS), + [0.1, 0.2, 0.5, 1, 2, 5, 10] + ) + end end def self.call_cpu_duration_histogram - @call_duration_histogram ||= Gitlab::Metrics.histogram( - :gitlab_method_call_cpu_duration_seconds, - 'Method calls cpu duration', - Transaction::BASE_LABELS.merge(BASE_LABELS), - [0.1, 0.2, 0.5, 1, 2, 5, 10] - ) + return @call_cpu_duration_histogram if @call_cpu_duration_histogram + + MUTEX.synchronize do + @call_duration_histogram ||= Gitlab::Metrics.histogram( + :gitlab_method_call_cpu_duration_seconds, + 'Method calls cpu duration', + Transaction::BASE_LABELS.merge(BASE_LABELS), + [0.1, 0.2, 0.5, 1, 2, 5, 10] + ) + end end # name - The full name of the method (including namespace) such as -- cgit v1.2.3