gitlab-labkit 1.9.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71d570681f92f33c67e9d40571f88ad6118dfdbf3aed305be7b58a9099a9ad8d
4
- data.tar.gz: e03f282e419b06a8a030eaf5b9215eb99ced4883064c66abbca3ca84f9d452bf
3
+ metadata.gz: d63e4f781c8e316f19c419a52485bd751cef83e713f7da971e3285709760a669
4
+ data.tar.gz: a13c2d6a3b9f217f0d65c5a755ff213db29b21dad1e20d2cc5ea17a5bea3dda5
5
5
  SHA512:
6
- metadata.gz: be28ccf4c08aeff50ce11cbe5fbe552b5b26ef2967dcdcb46b9a7437783cd20535c2794e95be097d23d59126db6f0731f0cd13783ec314f72208c4789ab2c73c
7
- data.tar.gz: 7d972eaf40419b5b4c33ae4c283a72d496cfc81be35386d16924fc133b9b94f028456bd9e0bfc7f076799239f938dee931b7a7a20c690cabcb4fcab571df8b8a
6
+ metadata.gz: 5e5983bc819202df660c1965efb7d26dd74f89c7479d01974a3d4f56e5a2c09a4d9fa01cac41719d80048d6737a676cc3fb3d9492cfb1619eb715932cf2ad6ff
7
+ data.tar.gz: 44d1210ee82ef16a99150316f73b76be43942fedaad296b78c925a3e270a8e6b39e4e3cbf43e63488048b11f7d69b40ad3dcfdcc514b8d6bbb4dfe805a28173a
data/.copier-answers.yml CHANGED
@@ -3,7 +3,7 @@
3
3
  # See the project for instructions on how to update the project
4
4
  #
5
5
  # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
6
- _commit: v1.43.0
6
+ _commit: v1.45.0
7
7
  _src_path: https://gitlab.com/gitlab-com/gl-infra/common-template-copier.git
8
8
  ee_licensed: false
9
9
  golang: false
data/CODEOWNERS CHANGED
@@ -1,4 +1,4 @@
1
1
  # CODEOWNERS is used to lookup assignees for
2
2
  # Renovate Bot dependency change Merge Requests.
3
3
  # https://docs.renovatebot.com/configuration-options/#assigneesfromcodeowners
4
- * @reprazent @andrewn @mkaeppler @ayufan @hmerscher @d.barrett @splattael
4
+ * @reprazent @andrewn @mkaeppler @ayufan @hmerscher @d.barrett @splattael @e_forbes @M_Alvarez
data/lib/labkit/fields.rb CHANGED
@@ -80,6 +80,28 @@ module Labkit
80
80
  # request receipt to first response byte written.
81
81
  TTFB_S = "ttfb_s"
82
82
 
83
+ # GitLab project numeric ID.
84
+ GL_PROJECT_ID = "gl_project_id"
85
+
86
+ # GitLab pipeline numeric ID.
87
+ GL_PIPELINE_ID = "gl_pipeline_id"
88
+
89
+ # Log event timestamp in ISO 8601 format (e.g. "2026-04-02T12:00:00.000Z").
90
+ TIMESTAMP = "timestamp"
91
+
92
+ # Log severity level (e.g. "info", "warn", "error").
93
+ SEVERITY = "severity"
94
+
95
+ # Human-readable log message describing the event.
96
+ LOG_MESSAGE = "message"
97
+
98
+ # Class name associated with a log event (e.g. exception class or worker
99
+ # class).
100
+ CLASS_NAME = "class_name"
101
+
102
+ # Name of the service or component emitting the log event.
103
+ SERVICE_NAME = "service_name"
104
+
83
105
  # Get the constant name for a field value
84
106
  # @param field_value [String] The field value (e.g., "gl_user_id")
85
107
  # @return [String, nil] The constant name (e.g., "GL_USER_ID") or nil if not found
@@ -97,8 +119,22 @@ module Labkit
97
119
  # to identify and track usage of deprecated fields in the codebase.
98
120
 
99
121
  MAPPINGS = {
100
- Fields::GL_USER_ID => %w[user_id userid],
101
- Fields::HTTP_STATUS_CODE => %w[status_code extra.status status_text],
122
+ Fields::CORRELATION_ID => %w[tags.correlation_id],
123
+ Fields::GL_USER_ID => %w[user_id userid extra.user_id extra.current_user_id meta.user_id],
124
+ Fields::GL_USER_NAME => %w[username extra.user meta.user],
125
+ Fields::ERROR_MESSAGE => %w[error err error.message exception.message graphql_errors],
126
+ Fields::HTTP_STATUS_CODE => %w[status_code extra.status status_text http_status],
127
+ Fields::HTTP_URL => %w[req_url],
128
+ Fields::DURATION_S => %w[duration duration_ms elapsed_time actual_duration time_ms total_time gitaly.duration],
129
+ Fields::REMOTE_IP => %w[ip source_ip ip_address meta.remote_ip],
130
+ Fields::HTTP_HOST => %w[hostname request_host gitlab_host kubernetes.host],
131
+ Fields::GL_PROJECT_ID => %w[extra.project_id meta.project_id meta.search.project_id job_project_id target_project_id],
132
+ Fields::GL_PIPELINE_ID => %w[extra.pipeline_id meta.pipeline_id root_pipeline_id],
133
+ Fields::TIMESTAMP => %w[start_time],
134
+ Fields::SEVERITY => %w[level],
135
+ Fields::LOG_MESSAGE => %w[msg custom_message extra.message fields.message graphql.message reason color_message exception.gitaly],
136
+ Fields::CLASS_NAME => %w[class author_class exception.class extra.class extra.class_name],
137
+ Fields::SERVICE_NAME => %w[service grpc.service_name auth_service type component subcomponent],
102
138
  }.freeze
103
139
 
104
140
  class << self
@@ -74,15 +74,6 @@ expect { subject }.to complete_user_experience('rails_request', error: true, suc
74
74
  expect { subject }.not_to complete_user_experience('rails_request')
75
75
  ```
76
76
 
77
- ### Legacy Matchers (Backward Compatibility)
78
-
79
- For backward compatibility, the following legacy matchers are still available but deprecated. Please migrate to the new `*_user_experience` matchers above.
80
-
81
- - `start_covered_experience`
82
- - `checkpoint_covered_experience`
83
- - `resume_covered_experience`
84
- - `complete_covered_experience`
85
-
86
77
  ## Example Usage
87
78
 
88
79
  ### In your spec_helper.rb or rails_helper.rb:
@@ -203,6 +203,59 @@ RSpec::Matchers.define :complete_user_experience do |user_experience_id, error:
203
203
  end
204
204
  end
205
205
 
206
+ # Matcher for verifying UserExperience observed metrics instrumentation.
207
+ #
208
+ # Usage:
209
+ # expect { subject }.to observed_user_experience('rails_request')
210
+ #
211
+ # This matcher verifies that the following metrics are incremented with specific labels:
212
+ # - gitlab_user_experience_checkpoint_total (with checkpoint=start)
213
+ # - gitlab_user_experience_checkpoint_total (with checkpoint=end)
214
+ # - gitlab_user_experience_total (with error=false)
215
+ # - gitlab_user_experience_apdex_total (with success=true)
216
+ #
217
+ # Parameters:
218
+ # - user_experience_id: Required. The ID of the user experience (e.g., 'rails_request')
219
+ # - error: Optional. The expected error flag for gitlab_user_experience_total (false by default)
220
+ # - success: Optional. The expected success flag for gitlab_user_experience_apdex_total (true by default)
221
+ RSpec::Matchers.define :observed_user_experience do |user_experience_id, error: false, success: true|
222
+ include Labkit::RSpec::Matchers::UserExperience
223
+
224
+ description { "observe user experience '#{user_experience_id}'" }
225
+ supports_block_expectations
226
+
227
+ match do |actual|
228
+ labels = attributes(user_experience_id)
229
+
230
+ start_before = checkpoint_counter&.get(labels.merge(checkpoint: "start")).to_i
231
+ end_before = checkpoint_counter&.get(labels.merge(checkpoint: "end")).to_i
232
+ total_before = total_counter&.get(labels.merge(error: error)).to_i
233
+ apdex_before = apdex_counter&.get(labels.merge(success: success)).to_i
234
+
235
+ actual.call
236
+
237
+ start_after = checkpoint_counter&.get(labels.merge(checkpoint: "start")).to_i
238
+ end_after = checkpoint_counter&.get(labels.merge(checkpoint: "end")).to_i
239
+ total_after = total_counter&.get(labels.merge(error: error)).to_i
240
+ apdex_after = apdex_counter&.get(labels.merge(success: success)).to_i
241
+
242
+ @start_change = start_after - start_before
243
+ @end_change = end_after - end_before
244
+ @total_change = total_after - total_before
245
+ @apdex_change = apdex_after - apdex_before
246
+
247
+ @start_change == 1 && @end_change == 1 && @total_change == 1 && @apdex_change == (error ? 0 : 1)
248
+ end
249
+
250
+ failure_message do
251
+ "Failed to observe user experience '#{user_experience_id}':\n" \
252
+ "expected checkpoint='start' counter to increase by 1, but increased by #{@start_change}\n" \
253
+ "expected checkpoint='end' counter to increase by 1, but increased by #{@end_change}\n" \
254
+ "expected total='error: #{error}' counter to increase by 1, but increased by #{@total_change}\n" \
255
+ "expected apdex='success: #{success}' counter to increase by #{error ? 0 : 1}, but increased by #{@apdex_change}"
256
+ end
257
+ end
258
+
206
259
  # Backward compatibility matchers for CoveredExperience
207
260
  RSpec::Matchers.alias_matcher :start_covered_experience, :start_user_experience
208
261
  RSpec::Matchers.alias_matcher :checkpoint_covered_experience, :checkpoint_user_experience
@@ -161,6 +161,26 @@ experience.checkpoint
161
161
  experience.complete
162
162
  ```
163
163
 
164
+ #### Observing a Past Experience
165
+
166
+ When an action already happened and you want to record its duration retroactively, use `observed`. It fires the start and end metrics without registering in the active context:
167
+
168
+ ```ruby
169
+ Labkit::UserExperienceSli.observed('merge_request_creation', start_time: start_time_of_past_action)
170
+ ```
171
+
172
+ You can also signal that an error occurred during the action:
173
+
174
+ ```ruby
175
+ Labkit::UserExperienceSli.observed('merge_request_creation', start_time: start_time_of_past_action, error: true)
176
+ ```
177
+
178
+ Extra labels are forwarded to both the start and end log events:
179
+
180
+ ```ruby
181
+ Labkit::UserExperienceSli.observed('merge_request_creation', start_time: start_time_of_past_action, worker: 'MyWorker')
182
+ ```
183
+
164
184
  #### Resuming Experiences
165
185
 
166
186
  You can resume a user experience SLI that was previously started and stored in the context. This is useful for distributed operations or when work spans multiple processes.
@@ -135,6 +135,28 @@ module Labkit
135
135
  self
136
136
  end
137
137
 
138
+ # Records a past User Experience by its duration.
139
+ #
140
+ # @param start_time [Time] The time when the experience started.
141
+ # @param error [Boolean] Whether the experience ended in an error.
142
+ # @param extra [Hash] Additional data to include in the log events.
143
+ # @return [self]
144
+ def observed(start_time:, error: false, **extra)
145
+ @start_time = start_time.utc
146
+ @end_time = Time.now.utc
147
+ error!("observed_error") if error
148
+
149
+ checkpoint_counter.increment(checkpoint: "start", **base_labels)
150
+ log_event("start", **extra)
151
+
152
+ checkpoint_counter.increment(checkpoint: "end", **base_labels)
153
+ total_counter.increment(error: has_error?, **base_labels)
154
+ apdex_counter.increment(success: apdex_success?, **base_labels) unless has_error?
155
+ log_event("end", **extra)
156
+
157
+ self
158
+ end
159
+
138
160
  # Marks the experience as failed with an error
139
161
  #
140
162
  # @param error [StandardError, String] The error that caused the experience to fail.
@@ -20,6 +20,7 @@ module Labkit
20
20
  self
21
21
  end
22
22
 
23
+ def observed(**_kwargs) = self
23
24
  def push_attributes!(*_args) = self
24
25
  def checkpoint(*_args) = self
25
26
  def complete(*_args) = self
@@ -77,7 +77,7 @@ module Labkit
77
77
  reset_configuration
78
78
  end
79
79
 
80
- # Retrieves a covered experience using the experience_id.
80
+ # Retrieves a user experience using the experience_id.
81
81
  # It retrieves from the current context when available,
82
82
  # otherwise it instantiates a new experience with the definition
83
83
  # from the registry.
@@ -88,7 +88,7 @@ module Labkit
88
88
  find_current(experience_id) || raise_or_null(experience_id)
89
89
  end
90
90
 
91
- # Starts a covered experience using the experience_id.
91
+ # Starts a user experience using the experience_id.
92
92
  #
93
93
  # @param experience_id [String, Symbol] The ID of the experience to start.
94
94
  # @param extra [Hash] Additional data to include in the log event.
@@ -97,7 +97,19 @@ module Labkit
97
97
  get(experience_id).start(**extra, &)
98
98
  end
99
99
 
100
- # Resumes a covered experience using the experience_id.
100
+ # Records a past user experience by its duration atomically.
101
+ #
102
+ # @param experience_id [String, Symbol] The ID of the experience.
103
+ # @param start_time [Time] The time when the experience started.
104
+ # @param extra [Hash] Additional data to include in the log events.
105
+ # @return [Experience, Null] The observed experience or a Null object if not found (in production/staging).
106
+ def observed(experience_id, start_time:, **extra)
107
+ definition = registry[experience_id]
108
+ experience = definition ? Experience.new(definition) : raise_or_null(experience_id)
109
+ experience.observed(start_time: start_time, **extra)
110
+ end
111
+
112
+ # Resumes a user experience using the experience_id.
101
113
  #
102
114
  # @param experience_id [String, Symbol] The ID of the experience to resume.
103
115
  # @return [Experience, Null] The started experience or a Null object if not found (in production/staging).
@@ -54,6 +54,7 @@ fi
54
54
  # install mise/asdf dependencies
55
55
  echo "installing required plugins with mise install.."
56
56
  mise plugins update -q
57
+ mise trust
57
58
  mise install
58
59
 
59
60
  # set PROMPT_COMMAND to empty value for mise if unset
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-labkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate