Move Prometheus presentation logic to PrometheusText

+ Use NullMetrics to mock metrics when unused
+ Use method_missing in NullMetrics mocking
+ Update prometheus gem to version that correctly uses transitive dependencies
+ Ensure correct folders are used in Multiprocess prometheus client tests.
+ rename Sessions controller's metric
This commit is contained in:
Pawel Chojnacki 2017-05-29 14:19:43 +02:00
parent 770f07cd5c
commit c134a72cdb
13 changed files with 87 additions and 48 deletions

View file

@ -270,8 +270,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
gem 'mmap2', '~> 2.2.6'
gem 'prometheus-client-mmap', '~>0.7.0.beta3'
gem 'prometheus-client-mmap', '~>0.7.0.beta5'
end
group :development do

View file

@ -561,8 +561,8 @@ GEM
premailer-rails (1.9.2)
actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9)
prometheus-client-mmap (0.7.0.beta3)
quantile (~> 0.2.0)
prometheus-client-mmap (0.7.0.beta5)
mmap2 (~> 2.2.6)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@ -573,7 +573,6 @@ GEM
pry-rails (0.3.5)
pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3)
quantile (0.2.0)
rack (1.6.5)
rack-accept (0.4.5)
rack (>= 0.4)
@ -972,7 +971,6 @@ DEPENDENCIES
mail_room (~> 0.9.1)
method_source (~> 0.8)
minitest (~> 5.7.0)
mmap2 (~> 2.2.6)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
net-ssh (~> 3.0.1)
@ -1000,7 +998,7 @@ DEPENDENCIES
pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0)
prometheus-client-mmap (~> 0.7.0.beta3)
prometheus-client-mmap (~> 0.7.0.beta5)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)

View file

@ -5,10 +5,8 @@ class MetricsController < ActionController::Base
before_action :validate_prometheus_metrics
def metrics
response = "#{metrics_service.health_metrics_text}\n#{metrics_service.prometheus_metrics_text}"
render text: response, content_type: 'text/plain; version=0.0.4'
def index
render text: metrics_service.metrics_text, content_type: 'text/plain; verssion=0.0.4'
end
private

View file

@ -48,7 +48,7 @@ class SessionsController < Devise::SessionsController
private
def login_counter
@login_counter ||= Gitlab::Metrics.counter(:user_session_logins, 'User logins count')
@login_counter ||= Gitlab::Metrics.counter(:user_session_logins, 'User sign in count')
end
# Handle an "initial setup" state, where there's only one user, it's an admin,

View file

@ -12,18 +12,23 @@ class MetricsService
end
def health_metrics_text
results = CHECKS.flat_map(&:metrics)
metrics = CHECKS.flat_map(&:metrics)
types = results.map(&:name).uniq.map { |metric_name| "# TYPE #{metric_name} gauge" }
metrics = results.map(&method(:metric_to_prom_line))
formatter.marshal(metrics)
end
types.concat(metrics).join("\n")
def metrics_text
"#{health_metrics_text}\n#{prometheus_metrics_text}"
end
private
def formatter
@formatter ||= PrometheusText.new
end
def multiprocess_metrics_path
Rails.root.join(ENV['prometheus_multiproc_dir'])
@multiprocess_metrics_path ||= Rails.root.join(ENV['prometheus_multiproc_dir'])
end
def metric_to_prom_line(metric)

View file

@ -16,7 +16,7 @@ if defined?(Unicorn)
end
# set default directory for multiproces metrics gathering
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_data_dir'
ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
require ::File.expand_path('../config/environment', __FILE__)

View file

@ -0,0 +1,38 @@
module Gitlab::HealthChecks
class PrometheusText
def marshal(metrics)
metrics_with_type_declarations(metrics).join("\n")
end
private
def metrics_with_type_declarations(metrics)
type_declaration_added = {}
metrics.flat_map do |metric|
metric_lines = []
unless type_declaration_added.has_key?(metric.name)
type_declaration_added[metric.name] = true
metric_lines << metric_type_declaration(metric)
end
metric_lines << metric_text(metric)
end
end
def metric_type_declaration(metric)
"# TYPE #{metric.name} gauge"
end
def metric_text(metric)
labels = metric.labels&.map { |key, value| "#{key}=\"#{value}\"" }&.join(',') || ''
if labels.empty?
"#{metric.name} #{metric.value}"
else
"#{metric.name}{#{labels}} #{metric.value}"
end
end
end
end

View file

@ -73,7 +73,7 @@ module Gitlab
if prometheus_metrics_enabled?
registry.get(name)
else
DummyMetric.new
NullMetric.new
end
end

View file

@ -1,7 +1,12 @@
module Gitlab
module Metrics
# Mocks ::Prometheus::Client::Metric and all derived metrics
class DummyMetric
class NullMetric
def method_missing(name, *args, &block)
nil
end
# these methods shouldn't be called when metrics are disabled
def get(*args)
raise NotImplementedError
end
@ -9,21 +14,6 @@ module Gitlab
def values(*args)
raise NotImplementedError
end
# counter
def increment(*args)
# noop
end
# gauge
def set(*args)
# noop
end
# histogram / summary
def observe(*args)
# noop
end
end
end
end

View file

@ -6,10 +6,10 @@ describe MetricsController do
let(:token) { current_application_settings.health_check_access_token }
let(:json_response) { JSON.parse(response.body) }
around do |examples|
around do |example|
Dir.mktmpdir do |tmp_dir|
@metrics_multiproc_dir = tmp_dir
examples.run
example.run
end
end

View file

@ -1,6 +1,8 @@
require 'spec_helper'
describe Gitlab::Metrics do
include StubENV
describe '.settings' do
it 'returns a Hash' do
expect(described_class.settings).to be_an_instance_of(Hash)
@ -247,45 +249,53 @@ describe Gitlab::Metrics do
it_behaves_like 'prometheus metrics API'
describe '#dummy_metric' do
describe '#null_metric' do
subject { described_class.provide_metric(:test) }
it { is_expected.to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#counter' do
subject { described_class.counter(:counter, 'doc') }
it { is_expected.to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#summary' do
subject { described_class.summary(:summary, 'doc') }
it { is_expected.to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#gauge' do
subject { described_class.gauge(:gauge, 'doc') }
it { is_expected.to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#histogram' do
subject { described_class.histogram(:histogram, 'doc') }
it { is_expected.to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.to be_a(Gitlab::Metrics::NullMetric) }
end
end
context 'prometheus metrics enabled' do
around do |example|
Dir.mktmpdir do |tmp_dir|
@metrics_multiproc_dir = tmp_dir
example.run
end
end
before do
stub_const('Prometheus::Client::Multiprocdir', @metrics_multiproc_dir)
allow(described_class).to receive(:prometheus_metrics_enabled?).and_return(true)
end
it_behaves_like 'prometheus metrics API'
describe '#dummy_metric' do
describe '#null_metric' do
subject { described_class.provide_metric(:test) }
it { is_expected.to be_nil }
@ -294,25 +304,25 @@ describe Gitlab::Metrics do
describe '#counter' do
subject { described_class.counter(:name, 'doc') }
it { is_expected.not_to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.not_to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#summary' do
subject { described_class.summary(:name, 'doc') }
it { is_expected.not_to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.not_to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#gauge' do
subject { described_class.gauge(:name, 'doc') }
it { is_expected.not_to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.not_to be_a(Gitlab::Metrics::NullMetric) }
end
describe '#histogram' do
subject { described_class.histogram(:name, 'doc') }
it { is_expected.not_to be_a(Gitlab::Metrics::DummyMetric) }
it { is_expected.not_to be_a(Gitlab::Metrics::NullMetric) }
end
end
end

View file

@ -3,6 +3,7 @@ SimpleCovEnv.start!
ENV["RAILS_ENV"] ||= 'test'
ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
# ENV['prometheus_multiproc_dir'] = 'tmp/prometheus_multiproc_dir_test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'