gitlab-labkit 1.5.0 → 1.12.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 +4 -4
- data/.copier-answers.yml +3 -1
- data/.gitlab/merge_request_templates/default.md +9 -0
- data/.gitlab-ci-asdf-versions.yml +2 -2
- data/.gitlab-ci.yml +14 -15
- data/.pre-commit-config.yaml +1 -1
- data/.releaserc.json +7 -1
- data/.tool-versions +2 -2
- data/CODEOWNERS +1 -1
- data/doc/FIELD_STANDARDIZATION.md +148 -128
- data/gitlab-labkit.gemspec +2 -2
- data/lib/labkit/context.rb +4 -0
- data/lib/labkit/fields.rb +127 -42
- data/lib/labkit/logging/field_validator/config.rb +5 -18
- data/lib/labkit/logging/field_validator.rb +1 -1
- data/lib/labkit/net_http_publisher.rb +20 -1
- data/lib/labkit/rspec/README.md +0 -9
- data/lib/labkit/rspec/matchers/user_experience_matchers.rb +53 -0
- data/lib/labkit/tracing/README.md +50 -25
- data/lib/labkit/tracing/abstract_instrumenter.rb +7 -8
- data/lib/labkit/tracing/adapters/opentelemetry_span.rb +4 -0
- data/lib/labkit/tracing/adapters/opentelemetry_tracer.rb +12 -0
- data/lib/labkit/tracing/adapters/opentracing_span.rb +4 -0
- data/lib/labkit/tracing/adapters/opentracing_tracer.rb +9 -6
- data/lib/labkit/tracing/auto_initialize.rb +28 -6
- data/lib/labkit/tracing/external_http/request_instrumenter.rb +4 -13
- data/lib/labkit/tracing/open_telemetry_factory.rb +3 -13
- data/lib/labkit/tracing/open_tracing_factory.rb +1 -10
- data/lib/labkit/tracing/rails/action_view/render_template_instrumenter.rb +6 -1
- data/lib/labkit/tracing/rails/active_record/sql_instrumenter.rb +9 -4
- data/lib/labkit/tracing/tracing_utils.rb +9 -7
- data/lib/labkit/tracing.rb +3 -1
- data/lib/labkit/user_experience_sli/README.md +20 -0
- data/lib/labkit/user_experience_sli/experience.rb +33 -12
- data/lib/labkit/user_experience_sli/null.rb +5 -2
- data/lib/labkit/user_experience_sli.rb +15 -3
- data/scripts/prepare-dev-env.sh +1 -0
- metadata +10 -6
data/lib/labkit/fields.rb
CHANGED
|
@@ -1,54 +1,122 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Code generated by labkit-spec. DO NOT EDIT.
|
|
4
|
+
#
|
|
5
|
+
# Source: https://gitlab.com/gitlab-org/quality/tooling/labkit-spec/-/blob/main/schema/fields.yaml (version 1.0)
|
|
6
|
+
|
|
3
7
|
module Labkit
|
|
4
|
-
##
|
|
5
|
-
# Fields is intended to be a SSOT for all of the common field names that
|
|
6
|
-
# we emit via any observability we add to our systems.
|
|
7
|
-
#
|
|
8
|
-
# These fields should span multiple services.
|
|
9
|
-
#
|
|
10
|
-
# The goal of this package is to reduce the likelihood for typos or
|
|
11
|
-
# subtly different naming conventions. This will help to ensure we
|
|
12
|
-
# are able to marry up logs between different systems as a request
|
|
13
|
-
# is being processed.
|
|
14
|
-
#
|
|
15
|
-
# Usage:
|
|
16
|
-
# require 'labkit/fields'
|
|
17
|
-
# ...
|
|
18
|
-
# data[Labkit::Fields::GL_USER_ID] = user.id
|
|
19
|
-
# ...
|
|
20
|
-
#
|
|
21
|
-
# Labkit (Go): https://gitlab.com/gitlab-org/labkit/-/tree/master/fields?ref_type=heads
|
|
22
|
-
#
|
|
23
|
-
# For Engineers Looking to add fields:
|
|
24
|
-
#
|
|
25
|
-
# These fields are derived from the Go Labkit variant. Please ensure that you've made the
|
|
26
|
-
# respective changes in that repository prior to including the fields in this package.
|
|
27
|
-
#
|
|
28
|
-
# Please see the handbook page for more information
|
|
29
|
-
# https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_field_standardisation/
|
|
30
8
|
module Fields
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# This field is used to correlate
|
|
34
|
-
# the logs emitted by all of our systems.
|
|
35
|
-
# This should be present in all log line
|
|
36
|
-
# emissions.
|
|
9
|
+
# Unique identifier for correlating requests across services. Should be
|
|
10
|
+
# present in all log line emissions.
|
|
37
11
|
CORRELATION_ID = "correlation_id"
|
|
38
12
|
|
|
39
|
-
#
|
|
40
|
-
# captures the user's numeric ID for logging purposes.
|
|
13
|
+
# GitLab user numeric ID.
|
|
41
14
|
GL_USER_ID = "gl_user_id"
|
|
42
15
|
|
|
43
|
-
#
|
|
44
|
-
# captures the user's username for logging purposes.
|
|
16
|
+
# GitLab username.
|
|
45
17
|
GL_USER_NAME = "gl_user_name"
|
|
46
18
|
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
19
|
+
# Duo Workflow definition identifier (e.g. "analytics_agent/v1"). Identifies
|
|
20
|
+
# which Duo Workflow agent originated a request, enabling per-agent
|
|
21
|
+
# filtering in Kibana and Grafana.
|
|
22
|
+
DUO_WORKFLOW_DEFINITION = "duo_workflow_definition"
|
|
23
|
+
|
|
24
|
+
# Error type or classification (e.g. "NoMethodError", "ValidationError").
|
|
25
|
+
ERROR_TYPE = "error_type"
|
|
26
|
+
|
|
27
|
+
# Detailed error message (e.g. "undefined method 'boom!' for nil").
|
|
28
|
+
ERROR_MESSAGE = "error_message"
|
|
29
|
+
|
|
30
|
+
# HTTP response status code.
|
|
31
|
+
HTTP_STATUS_CODE = "status"
|
|
32
|
+
|
|
33
|
+
# HTTP method (e.g. "GET", "POST").
|
|
34
|
+
HTTP_METHOD = "method"
|
|
35
|
+
|
|
36
|
+
# URL of an HTTP request containing only scheme, host, and path. Query
|
|
37
|
+
# strings and fragments must be omitted to avoid logging sensitive
|
|
38
|
+
# information.
|
|
39
|
+
HTTP_URL = "url"
|
|
40
|
+
|
|
41
|
+
# Duration of any operation in seconds. Not limited to HTTP requests; can be
|
|
42
|
+
# used for database queries, background jobs, external API calls. Uses
|
|
43
|
+
# float64 for sub-second precision (e.g. 0.032 for 32ms).
|
|
44
|
+
DURATION_S = "duration_s"
|
|
45
|
+
|
|
46
|
+
# Remote IP address of a request.
|
|
47
|
+
REMOTE_IP = "remote_ip"
|
|
48
|
+
|
|
49
|
+
# TCP address a service is listening on, in "host:port" format (e.g.
|
|
50
|
+
# "0.0.0.0:8080").
|
|
51
|
+
TCP_ADDRESS = "tcp_address"
|
|
52
|
+
|
|
53
|
+
# Request URI including path and query string with sensitive parameters
|
|
54
|
+
# masked (e.g. "?password=FILTERED").
|
|
55
|
+
HTTP_URI = "uri"
|
|
56
|
+
|
|
57
|
+
# HTTP Host header of a request (e.g. "api.gitlab.com").
|
|
58
|
+
HTTP_HOST = "host"
|
|
59
|
+
|
|
60
|
+
# HTTP protocol version (e.g. "HTTP/1.1", "HTTP/2.0").
|
|
61
|
+
HTTP_PROTO = "proto"
|
|
62
|
+
|
|
63
|
+
# Raw remote socket address in "host:port" format (e.g. "10.0.0.1:54321").
|
|
64
|
+
# Use remote_ip when only the IP is needed.
|
|
65
|
+
REMOTE_ADDR = "remote_addr"
|
|
66
|
+
|
|
67
|
+
# HTTP Referer header with sensitive query parameters masked.
|
|
68
|
+
HTTP_REFERRER = "referrer"
|
|
69
|
+
|
|
70
|
+
# HTTP User-Agent header.
|
|
71
|
+
HTTP_USER_AGENT = "user_agent"
|
|
72
|
+
|
|
73
|
+
# Number of bytes written to the HTTP response body.
|
|
74
|
+
WRITTEN_BYTES = "written_bytes"
|
|
75
|
+
|
|
76
|
+
# Content-Type of an HTTP response (e.g. "application/json").
|
|
77
|
+
CONTENT_TYPE = "content_type"
|
|
78
|
+
|
|
79
|
+
# Time to first byte of an HTTP response in seconds. Measures duration from
|
|
80
|
+
# request receipt to first response byte written.
|
|
81
|
+
TTFB_S = "ttfb_s"
|
|
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
|
+
|
|
105
|
+
# GitLab organization numeric ID.
|
|
106
|
+
GL_ORGANIZATION_ID = "gl_organization_id"
|
|
107
|
+
|
|
108
|
+
# GitLab project path.
|
|
109
|
+
GL_PROJECT_PATH = "gl_project_path"
|
|
110
|
+
|
|
111
|
+
# The endpoint_id of the current endpoint to be passed as caller_id when
|
|
112
|
+
# calling another service.
|
|
113
|
+
ENDPOINT_ID = "endpoint_id"
|
|
114
|
+
|
|
115
|
+
# The ID of the caller of the current service.
|
|
116
|
+
CALLER_ID = "caller_id"
|
|
117
|
+
|
|
118
|
+
# The endpoint_id of the original caller at the root of the call chain.
|
|
119
|
+
ROOT_CALLER_ID = "root_caller_id"
|
|
52
120
|
|
|
53
121
|
# Get the constant name for a field value
|
|
54
122
|
# @param field_value [String] The field value (e.g., "gl_user_id")
|
|
@@ -67,7 +135,24 @@ module Labkit
|
|
|
67
135
|
# to identify and track usage of deprecated fields in the codebase.
|
|
68
136
|
|
|
69
137
|
MAPPINGS = {
|
|
70
|
-
Fields::
|
|
138
|
+
Fields::CORRELATION_ID => %w[tags.correlation_id],
|
|
139
|
+
Fields::GL_USER_ID => %w[user_id userid extra.user_id extra.current_user_id meta.user_id],
|
|
140
|
+
Fields::GL_USER_NAME => %w[username extra.user meta.user],
|
|
141
|
+
Fields::ERROR_MESSAGE => %w[error err error.message exception.message graphql_errors],
|
|
142
|
+
Fields::HTTP_STATUS_CODE => %w[status_code extra.status status_text http_status],
|
|
143
|
+
Fields::HTTP_URL => %w[req_url],
|
|
144
|
+
Fields::DURATION_S => %w[duration duration_ms elapsed_time actual_duration time_ms total_time gitaly.duration],
|
|
145
|
+
Fields::REMOTE_IP => %w[ip source_ip ip_address meta.remote_ip],
|
|
146
|
+
Fields::HTTP_HOST => %w[hostname request_host gitlab_host kubernetes.host],
|
|
147
|
+
Fields::GL_PROJECT_ID => %w[extra.project_id meta.project_id meta.search.project_id job_project_id target_project_id],
|
|
148
|
+
Fields::GL_PIPELINE_ID => %w[extra.pipeline_id meta.pipeline_id root_pipeline_id],
|
|
149
|
+
Fields::TIMESTAMP => %w[start_time],
|
|
150
|
+
Fields::SEVERITY => %w[level],
|
|
151
|
+
Fields::LOG_MESSAGE => %w[msg custom_message extra.message fields.message graphql.message reason color_message exception.gitaly],
|
|
152
|
+
Fields::CLASS_NAME => %w[class author_class exception.class extra.class extra.class_name],
|
|
153
|
+
Fields::SERVICE_NAME => %w[service grpc.service_name auth_service type component subcomponent],
|
|
154
|
+
Fields::GL_ORGANIZATION_ID => %w[organization_id],
|
|
155
|
+
Fields::GL_PROJECT_PATH => %w[project_path full_path root_pipeline_project_path requested_project_path auth_project_path extra.gl_project_path],
|
|
71
156
|
}.freeze
|
|
72
157
|
|
|
73
158
|
class << self
|
|
@@ -88,26 +88,13 @@ module Labkit
|
|
|
88
88
|
# This file tracks deprecated logging fields that need to be migrated to standard fields.
|
|
89
89
|
# Each offense represents a file using a deprecated field that should be replaced.
|
|
90
90
|
#
|
|
91
|
-
#
|
|
91
|
+
# How to fix: https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/blob/master/doc/FIELD_STANDARDIZATION.md#how-to-fix-an-offense
|
|
92
92
|
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# 3. Run your tests - the offense will be automatically removed
|
|
93
|
+
# Adding offenses (if fixing is not immediately possible):
|
|
94
|
+
# LABKIT_LOGGING_TODO_UPDATE=true bundle exec rspec <spec_file>
|
|
96
95
|
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# logger.info(user_id: 123)
|
|
100
|
-
#
|
|
101
|
-
# # After
|
|
102
|
-
# logger.info(Labkit::Fields::GL_USER_ID => 123)
|
|
103
|
-
#
|
|
104
|
-
# === ADDING OFFENSES (if fixing is not immediately possible) ===
|
|
105
|
-
#
|
|
106
|
-
# Run: LABKIT_LOGGING_TODO_UPDATE=true bundle exec rspec <spec_file>
|
|
107
|
-
#
|
|
108
|
-
# === REGENERATE ENTIRE TODO ===
|
|
109
|
-
#
|
|
110
|
-
# Delete this file and run: LABKIT_LOGGING_TODO_UPDATE=true bundle exec rspec
|
|
96
|
+
# Regenerate entire TODO:
|
|
97
|
+
# Delete this file and run: LABKIT_LOGGING_TODO_UPDATE=true bundle exec rspec
|
|
111
98
|
|
|
112
99
|
HEADER
|
|
113
100
|
end
|
|
@@ -158,7 +158,7 @@ module Labkit
|
|
|
158
158
|
lines << ("=" * 80)
|
|
159
159
|
lines << "Total: #{new_offenses.size} new offense(s) in #{new_offenses.map { |o| o['callsite'] }.uniq.size} file(s)" # rubocop:disable Rails/Pluck
|
|
160
160
|
lines << ""
|
|
161
|
-
lines << "See https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/blob/master/doc/FIELD_STANDARDIZATION.md"
|
|
161
|
+
lines << "See https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/blob/master/doc/FIELD_STANDARDIZATION.md#new-offenses-detected"
|
|
162
162
|
lines << ("=" * 80)
|
|
163
163
|
lines << ""
|
|
164
164
|
|
|
@@ -41,6 +41,8 @@ module Labkit
|
|
|
41
41
|
|
|
42
42
|
start_time = ::Labkit::System.monotonic_time
|
|
43
43
|
|
|
44
|
+
inject_trace_context(request)
|
|
45
|
+
|
|
44
46
|
ActiveSupport::Notifications.instrument ::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, create_request_payload(request) do |payload|
|
|
45
47
|
response =
|
|
46
48
|
begin
|
|
@@ -57,7 +59,7 @@ module Labkit
|
|
|
57
59
|
|
|
58
60
|
def create_request_payload(request)
|
|
59
61
|
payload = {
|
|
60
|
-
method: request.method
|
|
62
|
+
method: request.method
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
if request.uri.nil?
|
|
@@ -84,5 +86,22 @@ module Labkit
|
|
|
84
86
|
|
|
85
87
|
payload
|
|
86
88
|
end
|
|
89
|
+
|
|
90
|
+
def inject_trace_context(request)
|
|
91
|
+
return unless Labkit::Tracing.enabled?
|
|
92
|
+
|
|
93
|
+
tracer = Labkit::Tracing::TracingUtils.tracer
|
|
94
|
+
span = tracer.active_span
|
|
95
|
+
return if span.nil?
|
|
96
|
+
|
|
97
|
+
carrier = {}
|
|
98
|
+
tracer.inject_context(span, carrier)
|
|
99
|
+
|
|
100
|
+
carrier.each do |key, value|
|
|
101
|
+
request[key] = value
|
|
102
|
+
end
|
|
103
|
+
rescue StandardError
|
|
104
|
+
warn "Labkit::NetHttpPublisher: trace context propagation failed"
|
|
105
|
+
end
|
|
87
106
|
end
|
|
88
107
|
end
|
data/lib/labkit/rspec/README.md
CHANGED
|
@@ -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
|
|
@@ -35,13 +35,18 @@ export GITLAB_TRACING="otlp://localhost:4318"
|
|
|
35
35
|
|
|
36
36
|
**Automatic Initialization**
|
|
37
37
|
|
|
38
|
-
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer
|
|
38
|
+
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer:
|
|
39
|
+
|
|
40
|
+
- **Rails applications**: Railtie automatically inserts Labkit::Tracing::RackMiddleware into the middleware stack
|
|
41
|
+
- **Non-Rails applications**: Tracer is auto-initialized, but `Labkit::Tracing::RackMiddleware` must be manually added to your middleware stack for HTTP request tracing (see [Rack Middleware](#rack-middleware))
|
|
39
42
|
|
|
40
43
|
Auto-initialization provides:
|
|
41
|
-
- Automatic tracer creation with connection string settings
|
|
44
|
+
- Automatic tracer creation with connection string settings (sampler, exporter, service name)
|
|
42
45
|
- Service name from `service_name` query parameter (defaults to `"labkit-service"`)
|
|
43
|
-
-
|
|
44
|
-
-
|
|
46
|
+
- Selective OpenTelemetry instrumentation for components that don't conflict with LabKit:
|
|
47
|
+
- `ConcurrentRuby`, `Net::HTTP`, `ActionPack`, `ActionMailer`, `ActiveJob`
|
|
48
|
+
- LabKit's own instrumentation for components it handles directly:
|
|
49
|
+
- HTTP request tracing (RackMiddleware for Rails)
|
|
45
50
|
- Rails components (ActiveRecord, ActionView, ActiveSupport) - if Rails is available
|
|
46
51
|
- Redis instrumentation - if Redis gem is loaded
|
|
47
52
|
- External HTTP instrumentation (Net::HTTP, Excon, HTTPClient)
|
|
@@ -73,6 +78,7 @@ The following connection string formats and query parameters are supported:
|
|
|
73
78
|
- **Behavior**: Outputs spans immediately to stdout with no remote export
|
|
74
79
|
- **Benefits**: No external collector required, instant feedback, simple debugging
|
|
75
80
|
|
|
81
|
+
|
|
76
82
|
### Sampling
|
|
77
83
|
|
|
78
84
|
**Default Behavior:** When no sampler is specified, probabilistic sampling is used with a **0.1% sample rate** (1 in 1000 traces).
|
|
@@ -159,7 +165,7 @@ This seamless integration means you can:
|
|
|
159
165
|
|
|
160
166
|
### Automatic Initialization (Default Behavior)
|
|
161
167
|
|
|
162
|
-
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer
|
|
168
|
+
When `GITLAB_TRACING` is set, LabKit automatically creates and configures a tracer:
|
|
163
169
|
|
|
164
170
|
```bash
|
|
165
171
|
# Set environment variable with service name
|
|
@@ -169,14 +175,23 @@ export GITLAB_TRACING="otlp://localhost:4318?service_name=my-api&sampler=probabi
|
|
|
169
175
|
export GITLAB_TRACING="otlp://localhost:4318"
|
|
170
176
|
```
|
|
171
177
|
|
|
178
|
+
**Rails Applications:**
|
|
179
|
+
- Initialization happens automatically during Rails boot via Railtie
|
|
180
|
+
- Runs after all other gem initializers (including `opentelemetry-instrumentation-rails`)
|
|
181
|
+
- LabKit's TracerProvider is the final configuration used by the application
|
|
182
|
+
- **No application code changes required** - just set the environment variable
|
|
183
|
+
|
|
184
|
+
**Non-Rails Applications (Sinatra, Grape, standalone Ruby):**
|
|
185
|
+
- Tracer auto-initialization happens automatically when `require 'gitlab-labkit'` is called.
|
|
186
|
+
- However, you must manually add `Labkit::Tracing::RackMiddleware` to your middleware stack for HTTP request tracing. See the [Rack Middleware](#rack-middleware) section for details.
|
|
187
|
+
|
|
172
188
|
Auto-initialization:
|
|
173
189
|
- Creates tracer with connection string settings (sampler, exporter, endpoints)
|
|
174
190
|
- Uses service name from `service_name` query parameter (defaults to `"labkit-service"`)
|
|
175
|
-
- Enables
|
|
191
|
+
- Enables selective OpenTelemetry instrumentation (non-conflicting with LabKit's own)
|
|
192
|
+
- Enables LabKit instrumentation for ActiveRecord, ActionView, ActiveSupport, Redis, and External HTTP
|
|
176
193
|
- Sets up the global `OpenTelemetry.tracer_provider`
|
|
177
194
|
|
|
178
|
-
**No application code changes required** - just set the environment variable.
|
|
179
|
-
|
|
180
195
|
### Automatic Rails Middleware Insertion
|
|
181
196
|
|
|
182
197
|
When using Rails, the `Labkit::Tracing::RackMiddleware` is automatically inserted into your middleware stack when `GITLAB_TRACING` is set. No manual configuration needed!
|
|
@@ -214,8 +229,8 @@ You can override auto-initialization by calling `Factory.create_tracer` in your
|
|
|
214
229
|
# config/initializers/tracing.rb
|
|
215
230
|
# Override auto-initialization with custom configuration
|
|
216
231
|
Labkit::Tracing::Factory.create_tracer("my-custom-service", ENV["GITLAB_TRACING"]) do |c|
|
|
217
|
-
#
|
|
218
|
-
c.use 'OpenTelemetry::Instrumentation::
|
|
232
|
+
# Custom OTel instrumentation
|
|
233
|
+
c.use 'OpenTelemetry::Instrumentation::ConcurrentRuby'
|
|
219
234
|
c.use 'OpenTelemetry::Instrumentation::Sidekiq'
|
|
220
235
|
|
|
221
236
|
# Add custom span processors
|
|
@@ -232,7 +247,7 @@ end
|
|
|
232
247
|
```
|
|
233
248
|
|
|
234
249
|
**When to use manual initialization:**
|
|
235
|
-
- You need
|
|
250
|
+
- You need different OTel instrumentations than the defaults
|
|
236
251
|
- You need custom span processors
|
|
237
252
|
- You need to add resource attributes
|
|
238
253
|
- You want to override the service name from code instead of the connection string
|
|
@@ -289,11 +304,16 @@ end
|
|
|
289
304
|
|
|
290
305
|
### Initialization Order and Precedence
|
|
291
306
|
|
|
292
|
-
**Auto-initialization:**
|
|
293
|
-
1. Runs when
|
|
307
|
+
**Auto-initialization (all applications):**
|
|
308
|
+
1. Runs automatically when `require 'gitlab-labkit'` is called
|
|
294
309
|
2. Only runs if `GITLAB_TRACING` environment variable is set
|
|
295
310
|
3. Uses `service_name` query parameter or defaults to `"labkit-service"`
|
|
296
|
-
4.
|
|
311
|
+
4. Enables selective OpenTelemetry instrumentation (ConcurrentRuby, Net::HTTP, ActionPack, ActionMailer, ActiveJob) to avoid conflicts with LabKit's own instrumentation
|
|
312
|
+
5. Enables LabKit instrumentation for ActiveRecord, ActionView, ActiveSupport, Redis, and External HTTP
|
|
313
|
+
|
|
314
|
+
**Additional Rails behavior:**
|
|
315
|
+
1. Railtie runs after user config initializers (via `initializer ... after: :load_config_initializers`)
|
|
316
|
+
2. Automatically inserts `Labkit::Tracing::RackMiddleware` into the middleware stack
|
|
297
317
|
|
|
298
318
|
**Manual initialization:**
|
|
299
319
|
1. Runs in your application initializer (e.g., `config/initializers/tracing.rb`)
|
|
@@ -308,7 +328,7 @@ end
|
|
|
308
328
|
- Span processors with OTLP exporter
|
|
309
329
|
|
|
310
330
|
2. **Configuration block** runs second and can:
|
|
311
|
-
- Add automatic instrumentation (`use
|
|
331
|
+
- Add automatic instrumentation (`use`)
|
|
312
332
|
- Add additional span processors
|
|
313
333
|
- Merge additional resource attributes
|
|
314
334
|
- Override service_name if explicitly set in the block
|
|
@@ -570,8 +590,13 @@ If you need to add custom metadata (deployment environment, version, etc.), over
|
|
|
570
590
|
# config/initializers/tracing.rb
|
|
571
591
|
|
|
572
592
|
Labkit::Tracing::Factory.create_tracer("my-rails-app", ENV["GITLAB_TRACING"]) do |c|
|
|
573
|
-
#
|
|
574
|
-
|
|
593
|
+
# Selective instrumentation (avoid conflicting with LabKit's own instrumentation
|
|
594
|
+
# for ActiveRecord, ActionView, ActiveSupport, Redis, Rack, and Sidekiq)
|
|
595
|
+
c.use("OpenTelemetry::Instrumentation::ConcurrentRuby")
|
|
596
|
+
c.use("OpenTelemetry::Instrumentation::Net::HTTP")
|
|
597
|
+
c.use("OpenTelemetry::Instrumentation::ActionPack") if defined?(ActionPack)
|
|
598
|
+
c.use("OpenTelemetry::Instrumentation::ActionMailer") if defined?(ActionMailer)
|
|
599
|
+
c.use("OpenTelemetry::Instrumentation::ActiveJob") if defined?(ActiveJob)
|
|
575
600
|
|
|
576
601
|
# Add deployment metadata
|
|
577
602
|
c.resource = c.resource.merge(
|
|
@@ -588,18 +613,18 @@ end
|
|
|
588
613
|
|
|
589
614
|
Note: Middleware is still automatically inserted even with custom tracer initialization.
|
|
590
615
|
|
|
591
|
-
### With
|
|
616
|
+
### With Custom Instrumentation (Manual Override)
|
|
592
617
|
|
|
593
|
-
If you want to control exactly which components are instrumented
|
|
618
|
+
If you want to control exactly which OTel components are instrumented, override tracer initialization:
|
|
594
619
|
|
|
595
620
|
**Important:** When you manually call `Factory.create_tracer`, auto-initialization still runs but is replaced by your manual configuration. The LabKit-specific instrumentation (Rails, Redis, ExternalHttp) is still automatically enabled unless you explicitly disable auto-initialization.
|
|
596
621
|
|
|
597
622
|
```ruby
|
|
598
623
|
# config/initializers/tracing.rb
|
|
599
|
-
# Override auto-initialization for
|
|
624
|
+
# Override auto-initialization for custom OpenTelemetry instrumentation
|
|
600
625
|
Labkit::Tracing::Factory.create_tracer("my-rails-app", ENV["GITLAB_TRACING"]) do |c|
|
|
601
|
-
#
|
|
602
|
-
c.use 'OpenTelemetry::Instrumentation::
|
|
626
|
+
# Only enable the OTel instrumentations you need
|
|
627
|
+
c.use 'OpenTelemetry::Instrumentation::ConcurrentRuby'
|
|
603
628
|
c.use 'OpenTelemetry::Instrumentation::Sidekiq'
|
|
604
629
|
end
|
|
605
630
|
|
|
@@ -642,9 +667,9 @@ If you're not seeing HTTP request traces in Rails:
|
|
|
642
667
|
```
|
|
643
668
|
|
|
644
669
|
2. Check Rails logs for auto-insertion message:
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
670
|
+
```
|
|
671
|
+
Labkit::Tracing::RackMiddleware automatically inserted after Labkit::Middleware::Rack
|
|
672
|
+
```
|
|
648
673
|
|
|
649
674
|
3. If using custom middleware positioning, verify the order is correct:
|
|
650
675
|
```bash
|
|
@@ -7,27 +7,26 @@ module Labkit
|
|
|
7
7
|
# https://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications/Instrumenter.html#method-c-new
|
|
8
8
|
class AbstractInstrumenter
|
|
9
9
|
def start(_name, _id, payload)
|
|
10
|
-
|
|
10
|
+
span_wrapper = Labkit::Tracing::TracingUtils.tracer.start_active_span(span_name(payload))
|
|
11
11
|
|
|
12
|
-
scope_stack.push
|
|
12
|
+
scope_stack.push span_wrapper
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def finish(_name, _id, payload)
|
|
16
|
-
|
|
17
|
-
span = scope.span
|
|
16
|
+
span_wrapper = scope_stack.pop
|
|
18
17
|
|
|
19
|
-
Labkit::Tracing::TracingUtils.log_common_fields_on_span(
|
|
18
|
+
Labkit::Tracing::TracingUtils.log_common_fields_on_span(span_wrapper, span_name(payload))
|
|
20
19
|
|
|
21
20
|
# exception_object is the standard exception payload from ActiveSupport::Notifications
|
|
22
21
|
# https://github.com/rails/rails/blob/v6.0.3.1/activesupport/lib/active_support/notifications/instrumenter.rb#L26
|
|
23
22
|
exception = payload[:exception_object].presence || payload[:exception].presence
|
|
24
|
-
|
|
23
|
+
span_wrapper.set_error(exception)
|
|
25
24
|
|
|
26
25
|
tags(payload).each do |k, v|
|
|
27
|
-
|
|
26
|
+
span_wrapper.set_tag(k, v)
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
span_wrapper.close
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def scope_stack
|
|
@@ -91,6 +91,18 @@ module Labkit
|
|
|
91
91
|
@token = token
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
def set_tag(key, value)
|
|
95
|
+
span.set_tag(key, value)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def set_error(exception)
|
|
99
|
+
span.set_error(exception)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def log_event(name, **attributes)
|
|
103
|
+
span.log_event(name, **attributes)
|
|
104
|
+
end
|
|
105
|
+
|
|
94
106
|
def close
|
|
95
107
|
@raw_span.finish
|
|
96
108
|
OpenTelemetry::Context.detach(@token)
|
|
@@ -34,15 +34,18 @@ module Labkit
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def active_span
|
|
37
|
-
OpenTracing.active_span
|
|
37
|
+
span = OpenTracing.active_span
|
|
38
|
+
OpentracingSpan.new(span) if span
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def start_active_span(operation_name, tags: nil)
|
|
41
|
-
if tags
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
scope = if tags
|
|
43
|
+
OpenTracing.start_active_span(operation_name, tags: tags)
|
|
44
|
+
else
|
|
45
|
+
OpenTracing.start_active_span(operation_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
OpentracingSpan.new(scope)
|
|
46
49
|
end
|
|
47
50
|
end
|
|
48
51
|
end
|