Class: GitlabInternalEventsCli::NewMetric
- Inherits:
-
Struct
- Object
- Struct
- GitlabInternalEventsCli::NewMetric
- Defined in:
- lib/gitlab_internal_events_cli/metric.rb
Overview
NOTE: :instrumentation_operation is kept on the struct but intentionally excluded from NEW_METRIC_FIELDS so it does NOT leak into the YAML output. It is only used to generate the Ruby instrumentation class file for database metrics.
Instance Attribute Summary collapse
-
#actions ⇒ Object
Returns the value of attribute actions.
-
#filters ⇒ Object
Returns the value of attribute filters.
-
#identifier ⇒ Object
Returns the value of attribute identifier.
-
#instrumentation_operation ⇒ Object
Returns the value of attribute instrumentation_operation.
-
#key ⇒ Object
Returns the value of attribute key.
-
#operator ⇒ Object
Returns the value of attribute operator.
Instance Method Summary collapse
-
#assign_time_frame ⇒ Object
Maintain current functionality of string time_frame for backward compatibility TODO: Remove once we can deduplicate and merge metric files.
- #bulk_assign(key_value_pairs) ⇒ Object
- #database_metric? ⇒ Boolean
-
#default_name_from_instrumentation_class ⇒ Object
Derives a snake_case metric-name suggestion from the instrumentation_class so database metrics can show a sensible default in the “How should we reference this metric?” prompt.
-
#description_prefix ⇒ Object
Automatically prepended to all new descriptions ex) Total count of ex) Weekly/Monthly count of unique ex) Count of.
- #distribution_path ⇒ Object
- #event_metric? ⇒ Boolean
- #event_params(action, filter = nil) ⇒ Object
-
#events ⇒ Hash
Returns value for the ‘events` key in the metric definition.
- #file_name ⇒ Object
- #file_path ⇒ Object
-
#filtered? ⇒ Boolean
How to interpret different values for filters: nil –> not expected, assigned or filtered (metric not initialized with filters) [] –> both expected and filtered (metric initialized with filters, but not yet assigned by user) [[‘event’, {}]] –> not expected, assigned or filtered (filters were expected, but then skipped by user) [[‘event’, { ‘label’ => ‘a’ }]] –> both assigned and filtered (filters exist for any event; user is done assigning).
- #filters_expected? ⇒ Boolean
- #formatted_output ⇒ Object
-
#instrumentation_class_content ⇒ Object
Renders the body of the database instrumentation class.
-
#instrumentation_class_file_path ⇒ Object
Path to the Ruby instrumentation class file generated for database metrics.
-
#instrumentation_class_spec_content ⇒ Object
Renders the spec body for the generated instrumentation class.
-
#instrumentation_class_spec_file_path ⇒ Object
Path to the RSpec file for the generated instrumentation class.
-
#instrumentation_class_underscored ⇒ Object
Converts a CamelCase class name like “CountIssuesMetric” to the snake_case file name stem “count_issues_metric”.
- #key_path ⇒ Object
-
#technical_description ⇒ Object
Provides simplified but technically accurate description to be used before the user has provided a description.
- #time_frame ⇒ Object
-
#unique_ids ⇒ Object
Enables comparison with existing metrics.
Instance Attribute Details
#actions ⇒ Object
Returns the value of attribute actions
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def actions @actions end |
#filters ⇒ Object
Returns the value of attribute filters
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def filters @filters end |
#identifier ⇒ Object
Returns the value of attribute identifier
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def identifier @identifier end |
#instrumentation_operation ⇒ Object
Returns the value of attribute instrumentation_operation
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def instrumentation_operation @instrumentation_operation end |
#key ⇒ Object
Returns the value of attribute key
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def key @key end |
#operator ⇒ Object
Returns the value of attribute operator
86 87 88 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 86 def operator @operator end |
Instance Method Details
#assign_time_frame ⇒ Object
Maintain current functionality of string time_frame for backward compatibility TODO: Remove once we can deduplicate and merge metric files
321 322 323 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 321 def assign_time_frame time_frame.single? ? time_frame.value.first : time_frame.value end |
#bulk_assign(key_value_pairs) ⇒ Object
315 316 317 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 315 def bulk_assign(key_value_pairs) key_value_pairs.each { |key, value| self[key] = value } end |
#database_metric? ⇒ Boolean
196 197 198 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 196 def database_metric? data_source == 'database' end |
#default_name_from_instrumentation_class ⇒ Object
Derives a snake_case metric-name suggestion from the instrumentation_class so database metrics can show a sensible default in the “How should we reference this metric?” prompt.
Trailing ‘_metric` is stripped because the metric’s key_path / filename should not end in ‘_metric` (the class name does, but the key path describes the value, not the Ruby class).
Examples:
"CountIssuesMetric" -> "count_issues"
"CountStuffINTheDBMetric" -> "count_stuff_in_the_db"
nil -> nil
354 355 356 357 358 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 354 def default_name_from_instrumentation_class return unless instrumentation_class && !instrumentation_class.empty? instrumentation_class_underscored.sub(/_metric\z/, '') end |
#description_prefix ⇒ Object
Automatically prepended to all new descriptions ex) Total count of ex) Weekly/Monthly count of unique ex) Count of
291 292 293 294 295 296 297 298 299 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 291 def description_prefix return unless event_metric? [ (time_frame.description if time_frame.single?), operator.description, *(identifier.plural if identifier.default?) ].compact.join(' ').capitalize end |
#distribution_path ⇒ Object
200 201 202 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 200 def distribution_path 'ee' unless tiers.include?('free') end |
#event_metric? ⇒ Boolean
325 326 327 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 325 def event_metric? data_source == 'internal_events' end |
#event_params(action, filter = nil) ⇒ Object
257 258 259 260 261 262 263 264 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 257 def event_params(action, filter = nil) params = { 'name' => action } params['unique'] = identifier.reference if operator.value == 'unique_count' params['filter'] = filter if filter&.any? params['operator'] = operator.reference(identifier) if operator.value == 'sum' params end |
#events ⇒ Hash
Returns value for the ‘events` key in the metric definition. Requires #actions or #filters to be set by the caller first.
249 250 251 252 253 254 255 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 249 def events if filters.assigned? self[:filters].map { |(action, filter)| event_params(action, filter) } else actions.map { |action| event_params(action) } end end |
#file_name ⇒ Object
204 205 206 207 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 204 def file_name name = event_metric? ? key.value : key_path "#{name}.yml" end |
#file_path ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 106 def file_path File.join( *[ distribution_path, 'config', 'metrics', time_frame.directory_name, file_name ].compact ) end |
#filtered? ⇒ Boolean
How to interpret different values for filters: nil –> not expected, assigned or filtered
(metric not initialized with filters)
-
–> both expected and filtered
(metric initialized with filters, but not yet assigned by user) - [‘event’, {}]
-
–> not expected, assigned or filtered
(filters were expected, but then skipped by user)
- [‘event’, { ‘label’ => ‘a’ }]
-
–> both assigned and filtered
(filters exist for any event; user is done assigning)
279 280 281 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 279 def filtered? filters.assigned? || filters.expected? end |
#filters_expected? ⇒ Boolean
283 284 285 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 283 def filters_expected? filters.expected? end |
#formatted_output ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 91 def formatted_output extra_keys = event_metric? ? { events: events } : {} # These keys will always be included in the definition yaml METRIC_DEFAULTS .merge(to_h.compact) .merge( time_frame: assign_time_frame, key_path: key_path ).merge(extra_keys) .slice(*NEW_METRIC_FIELDS) .transform_keys(&:to_s) .to_yaml(line_width: 150) end |
#instrumentation_class_content ⇒ Object
Renders the body of the database instrumentation class.
Based on upstream lib/generators/gitlab/usage_metric/templates/database_instrumentation_class.rb.template with one intentional deviation: the upstream Rails generator appends “Metric” to the class name from a separate argument, whereas the CLI asks the user to provide the full class name (e.g. “CountIssuesMetric”) and interpolates it verbatim.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 150 def instrumentation_class_content <<~RUBY # frozen_string_literal: true module Gitlab module Usage module Metrics module Instrumentations class #{instrumentation_class} < DatabaseMetric operation :#{instrumentation_operation} relation do # Insert ActiveRecord relation here end end end end end end RUBY end |
#instrumentation_class_file_path ⇒ Object
Path to the Ruby instrumentation class file generated for database metrics. Mirrors the layout used by the upstream ‘rails generate gitlab:usage_metric` generator.
121 122 123 124 125 126 127 128 129 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 121 def instrumentation_class_file_path File.join( *[ distribution_path, 'lib', 'gitlab', 'usage', 'metrics', 'instrumentations', "#{instrumentation_class_underscored}.rb" ].compact ) end |
#instrumentation_class_spec_content ⇒ Object
Renders the spec body for the generated instrumentation class.
Differs from the upstream Rails template in two ways:
-
Uses the combined ‘a correct instrumented metric value and query` shared example (issue gitlab-org/gitlab#569191), where upstream uses `a correct instrumented metric value`.
-
Adds ‘data_source: ’database’‘ to the shared example params, matching the convention used by recent database metric specs in gitlab-org/gitlab (e.g. `count_admins_metric_spec.rb`).
181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 181 def instrumentation_class_spec_content <<~RUBY # frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::Usage::Metrics::Instrumentations::#{instrumentation_class}, feature_category: :service_ping do let(:expected_value) { 0 } let(:expected_query) { '' } it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', data_source: 'database' } end RUBY end |
#instrumentation_class_spec_file_path ⇒ Object
Path to the RSpec file for the generated instrumentation class.
132 133 134 135 136 137 138 139 140 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 132 def instrumentation_class_spec_file_path File.join( *[ distribution_path, 'spec', 'lib', 'gitlab', 'usage', 'metrics', 'instrumentations', "#{instrumentation_class_underscored}_spec.rb" ].compact ) end |
#instrumentation_class_underscored ⇒ Object
Converts a CamelCase class name like “CountIssuesMetric” to the snake_case file name stem “count_issues_metric”. Implemented without ActiveSupport to keep the gem dependency-free.
332 333 334 335 336 337 338 339 340 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 332 def instrumentation_class_underscored return '' unless instrumentation_class instrumentation_class .gsub('::', '/') .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end |
#key_path ⇒ Object
209 210 211 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 209 def key_path event_metric? ? key.full_path : self[:key] end |
#technical_description ⇒ Object
Provides simplified but technically accurate description to be used before the user has provided a description
303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 303 def technical_description return unless event_metric? event_name = actions.first if events.length == 1 && !filtered? event_name ||= 'the selected events' [ (time_frame.description if time_frame.single?), operator.description, (identifier.description % event_name).to_s ].compact.join(' ').capitalize end |
#time_frame ⇒ Object
213 214 215 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 213 def time_frame Metric::TimeFrames.new(self[:time_frame]) end |
#unique_ids ⇒ Object
Enables comparison with existing metrics
234 235 236 237 238 239 240 241 242 243 |
# File 'lib/gitlab_internal_events_cli/metric.rb', line 234 def unique_ids prefix = [ operator.reference(identifier), actions.sort.join('+'), 'filter-', filtered? ].join('_') time_frame.value.map { |t| prefix + t } end |