1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# frozen_string_literal: true
# Searches a projects repository for a metrics dashboard and formats the output.
# Expects any custom dashboards will be located in `.gitlab/dashboards`
module Metrics
module Dashboard
class BaseService < ::BaseService
include Gitlab::Metrics::Dashboard::Errors
STAGES = ::Gitlab::Metrics::Dashboard::Stages
SEQUENCE = [
STAGES::CommonMetricsInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter,
STAGES::AlertsInserter,
STAGES::UrlValidator
].freeze
def get_dashboard
return error('Insufficient permissions.', :unauthorized) unless allowed?
success(dashboard: process_dashboard)
rescue StandardError => e
handle_errors(e)
end
# Summary of all known dashboards for the service.
# @return [Array<Hash>] ex) [{ path: String, default: Boolean }]
def self.all_dashboard_paths(_project)
raise NotImplementedError
end
# Returns an un-processed dashboard from the cache.
def raw_dashboard
Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard }
end
# Should return true if this dashboard service is for an out-of-the-box
# dashboard.
# This method is overridden in app/services/metrics/dashboard/predefined_dashboard_service.rb.
# @return Boolean
def self.out_of_the_box_dashboard?
false
end
private
# Determines whether users should be able to view
# dashboards at all.
def allowed?
return false unless params[:environment]
project&.feature_available?(:metrics_dashboard, current_user)
end
# Returns a new dashboard Hash, supplemented with DB info
def process_dashboard
# Get the dashboard from cache/disk before beginning the benchmark.
dashboard = raw_dashboard
processed_dashboard = nil
benchmark_processing do
processed_dashboard = ::Gitlab::Metrics::Dashboard::Processor
.new(project, dashboard, sequence, process_params)
.process
end
processed_dashboard
end
def benchmark_processing
output = nil
processing_time_seconds = Benchmark.realtime { output = yield }
if output
processing_time_metric.observe(
processing_time_metric_labels,
processing_time_seconds * 1_000
)
end
end
def process_params
params
end
# @return [String] Relative filepath of the dashboard yml
def dashboard_path
params[:dashboard_path]
end
def load_yaml(data)
::Gitlab::Config::Loader::Yaml.new(data).load_raw!
rescue Gitlab::Config::Loader::Yaml::NotHashError
# Raise more informative error in app/models/performance_monitoring/prometheus_dashboard.rb.
{}
rescue Gitlab::Config::Loader::Yaml::DataTooLargeError => exception
raise Gitlab::Metrics::Dashboard::Errors::LayoutError, exception.message
rescue Gitlab::Config::Loader::FormatError
raise Gitlab::Metrics::Dashboard::Errors::LayoutError, _('Invalid yaml')
end
# @return [Hash] an unmodified dashboard
def get_raw_dashboard
raise NotImplementedError
end
# @return [String]
def cache_key
raise NotImplementedError
end
def sequence
SEQUENCE
end
def processing_time_metric
@processing_time_metric ||= ::Gitlab::Metrics.summary(
:gitlab_metrics_dashboard_processing_time_ms,
'Metrics dashboard processing time in milliseconds'
)
end
def processing_time_metric_labels
{
stages: sequence_string,
service: self.class.name
}
end
# If @sequence is [STAGES::CommonMetricsInserter, STAGES::CustomMetricsInserter],
# this function will output `CommonMetricsInserter-CustomMetricsInserter`.
def sequence_string
sequence.map { |stage_class| stage_class.to_s.split('::').last }.join('-')
end
end
end
end
|