Commit 420f42b0 authored by Marius Bobin's avatar Marius Bobin

Add prometheous endpoints for reporting metrics

Add prometheous endpoints for reporting metrics
parent f3d7d063
# frozen_string_literal: true
module Projects
module Ci
module PrometheusMetrics
class HistogramsController < Projects::ApplicationController
feature_category :pipeline_authoring
respond_to :json, only: [:create]
def create
result = ::Ci::PrometheusMetrics::ObserveHistogramsService.new(project, permitted_params).execute
render json: result.body, status: result.status
end
private
def permitted_params
params.permit(histograms: [:name, :value])
end
end
end
end
end
# frozen_string_literal: true
module Ci
module PrometheusMetrics
class ObserveHistogramsService
Result = Struct.new(:status, :body, keyword_init: true)
class << self
def available_histograms
@available_histograms ||= [
histogram(:pipeline_graph_link_calculation_duration_seconds, 'Total time spent calculating links, in seconds', {}, [0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2]),
histogram(:pipeline_graph_links_total, 'Number of links per graph', {}, [1, 5, 10, 25, 50, 100, 200]),
histogram(:pipeline_graph_link_per_job_ratio, 'Ratio of links to job per graph', {}, [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1])
].to_h
end
private
def histogram(name, *attrs)
[name.to_s, proc { Gitlab::Metrics.histogram(name, *attrs) }]
end
end
def initialize(project, params)
@project = project
@params = params
end
def execute
return Result.new(status: 202, body: {}) unless enabled?
params
.fetch(:histograms, [])
.each(&method(:observe))
Result.new(status: 201, body: {})
end
private
attr_reader :project, :params
def observe(data)
histogram = find_histogram(data[:name])
histogram.observe({ project: project.full_path }, data[:value].to_f)
end
def find_histogram(name)
self.class.available_histograms
.fetch(name) { raise ActiveRecord::RecordNotFound }
.call
end
def enabled?
::Feature.enabled?(:ci_accept_frontend_prometheus_metrics, project, default_enabled: :yaml)
end
end
end
end
---
name: ci_accept_frontend_prometheus_metrics
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52820
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300770
milestone: '13.9'
type: development
group: group::pipeline authoring
default_enabled: false
...@@ -87,6 +87,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -87,6 +87,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :lint, only: [:show, :create] resource :lint, only: [:show, :create]
resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor' resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor'
resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ } resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ }
namespace :prometheus_metrics do
resources :histograms, only: [:create], constraints: { format: 'json' }
end
end end
namespace :settings do namespace :settings do
......
...@@ -119,6 +119,9 @@ The following metrics are available: ...@@ -119,6 +119,9 @@ The following metrics are available:
| `gitlab_external_http_duration_seconds` | Counter | 13.8 | Duration in seconds spent on each HTTP call to external systems | | | `gitlab_external_http_duration_seconds` | Counter | 13.8 | Duration in seconds spent on each HTTP call to external systems | |
| `gitlab_external_http_exception_total` | Counter | 13.8 | Total number of exceptions raised when making external HTTP calls | | | `gitlab_external_http_exception_total` | Counter | 13.8 | Total number of exceptions raised when making external HTTP calls | |
| `ci_report_parser_duration_seconds` | Histogram | 13.9 | Time to parse CI/CD report artifacts | `parser` | | `ci_report_parser_duration_seconds` | Histogram | 13.9 | Time to parse CI/CD report artifacts | `parser` |
| `pipeline_graph_link_calculation_duration_seconds` | Histogram | 13.9 | Total time spent calculating links, in seconds | `project` |
| `pipeline_graph_links_total` | Histogram | 13.9 | Number of links per graph | `project` |
| `pipeline_graph_link_per_job_ratio` | Histogram | 13.9 | Ratio of links to job per graph | `project` |
## Metrics controlled by a feature flag ## Metrics controlled by a feature flag
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController' do
let_it_be(:project) { create(:project, :public) }
describe 'POST /*namespace_id/:project_id/-/ci/prometheus_metrics/histograms' do
context 'with known histograms' do
it 'returns 201 Created' do
post histograms_route(histograms: [
{ name: :pipeline_graph_link_calculation_duration_seconds, value: 1 },
{ name: :pipeline_graph_links_total, value: 10 }
])
expect(response).to have_gitlab_http_status(:created)
end
end
context 'with unknown histograms' do
it 'returns 404 Not Found' do
post histograms_route(histograms: [{ name: :chunky_bacon, value: 5 }])
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
def histograms_route(params = {})
namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, **params)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do
let_it_be(:project) { create(:project) }
let(:params) { {} }
subject(:execute) { described_class.new(project, params).execute }
before do
Gitlab::Metrics.reset_registry!
end
context 'with empty data' do
it 'does not raise errors' do
expect(subject.status).to eq(201)
end
end
context 'observes metrics successfully' do
let(:params) do
{
histograms: [
{ name: 'pipeline_graph_link_calculation_duration_seconds', value: '1' },
{ name: 'pipeline_graph_link_per_job_ratio', value: '0.9' }
]
}
end
it 'increments the metrics' do
execute
expect(histogram_data).to match(a_hash_including({ 0.8 => 0.0, 1 => 1.0, 2 => 1.0 }))
expect(histogram_data(:pipeline_graph_link_per_job_ratio))
.to match(a_hash_including({ 0.8 => 0.0, 0.9 => 1.0, 1 => 1.0 }))
end
it 'returns an empty body and status code' do
expect(subject.status).to eq(201)
expect(subject.body).to eq({})
end
end
context 'with unknown histograms' do
let(:params) do
{ histograms: [{ name: 'chunky_bacon', value: '4' }] }
end
it 'raises ActiveRecord::RecordNotFound error' do
expect { subject }.to raise_error ActiveRecord::RecordNotFound
end
end
context 'with feature flag disabled' do
before do
stub_feature_flags(ci_accept_frontend_prometheus_metrics: false)
end
let(:params) do
{
histograms: [
{ name: 'pipeline_graph_link_calculation_duration_seconds', value: '4' }
]
}
end
it 'does not register the metrics' do
execute
expect(histogram_data).to be_nil
end
it 'returns an empty body and status code' do
expect(subject.status).to eq(202)
expect(subject.body).to eq({})
end
end
def histogram_data(name = :pipeline_graph_link_calculation_duration_seconds)
Gitlab::Metrics.registry.get(name)&.get({ project: project.full_path })
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment