gitlab-labkit 1.1.2 → 1.1.3

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: a3f875b5fa4af36bdb6646a69d5acab871bd2fe84ee6876a57958e9264441378
4
- data.tar.gz: f86e176c5bed6fcbeae0ac82570b7b98349a56dac3b032cb0072573206ea273e
3
+ metadata.gz: 1bbb89ac1275f06d828f9d9fa4a24e20e33f4a01d201956d4dd8d30e87dd8f5a
4
+ data.tar.gz: 1d6b24a17f8b2096e34303fba306ea1271146b3245cb5edcb3fabbccd26f1e45
5
5
  SHA512:
6
- metadata.gz: d47772da83bcf81c149e957c4e692042ce1388dc774abbfcf9e9d3a765fa9fda88da1b8742314df3b1010e800935327cf4338c0f7ef05f6d2141d8aad3cb317d
7
- data.tar.gz: fc070e4ef55f7fac9bedf2af11a88aa5c27aff9c8cae3d634b78a3381e2694a67871a7c16c7865950ad3f7d7371c10cc14e1bbeb38c9bca8f36d00c68717a70c
6
+ metadata.gz: 4950f9c1615b432b5d53ab54a51cca84a10cbf548b434665fdda93a2204f912f172540652df7b634907c61fa88f03f50a1ad9de96e04d2795a59393fe3fdcf93
7
+ data.tar.gz: a67c2262dd119373431f271779baf8311c5eb47a794a2970e1b4b9dcd28c59a4253ff844654f20a99026f95aabe6538f8fbdc864da29f007cd6c94a99eb427ad
@@ -0,0 +1,124 @@
1
+ ---
2
+ owning-stage: '~devops::developer experience'
3
+ description: 'Logging Field Standardization: Dynamic Runtime Linting'
4
+ ---
5
+
6
+ # Logging Field Standardization ADR: Dynamic Runtime Linting
7
+
8
+ ## Context
9
+
10
+ GitLab is implementing a [logging field standardization initiative](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_field_standardisation/) to ensure logs are queryable and actionable across all systems. We need a mechanism to identify and track deprecated or non-standard logging fields across multiple codebases.
11
+
12
+ ### Requirements
13
+
14
+ The solution must:
15
+
16
+ 1. Detect deprecated fields at the point of emission (runtime).
17
+ 1. Provide immediate feedback to developers (shift-left).
18
+ 1. Prevent new violations without blocking existing work.
19
+ 1. Support gradual migration from deprecated to standard fields.
20
+ 1. Never affect production performance.
21
+ 1. Provide clear guidance on standard field replacements.
22
+
23
+ ### The problem with static analysis
24
+
25
+ We initially considered RuboCop (static linting), but this approach is inadequate due to Ruby's [dynamic and complex field construction](https://gitlab.com/gitlab-org/gitlab/-/blob/cfd4fb97968d1f7d30f39f89740e414e9437063a/lib/bulk_imports/logger.rb#L46).
26
+
27
+ Static analysis can't reliably detect:
28
+
29
+ - Fields merged from hash arguments.
30
+ - Dynamically constructed field names.
31
+ - Fields passed through multiple abstraction layers.
32
+ - Conditional field inclusion based on runtime state.
33
+
34
+ ## Decision
35
+
36
+ Implement dynamic runtime linting to validate logging fields as they're emitted during development and testing, rather than using static analysis.
37
+
38
+ The validator will:
39
+
40
+ - Intercept logging calls at runtime to detect deprecated fields.
41
+ - Compare detected violations against a frozen baseline of known violations.
42
+ - Raise an error on new violations while ignoring tracked existing tracked violations.
43
+ - Provide immediate feedback to developers during local development.
44
+ - Never affect production performance.
45
+
46
+ ## Consequences
47
+
48
+ ### Benefits
49
+
50
+ - **Shift-left feedback**: Developers discover violations during local development, not in code review.
51
+ - **Comprehensive detection**: Captures violations from all code execution paths (tests, Rake tasks, console, development environments).
52
+ - **Accurate detection**: Runtime interception catches dynamically constructed fields that static analysis misses.
53
+ - **Non-blocking**: Existing violations don't prevent development. They're tracked explicitly in baseline.
54
+ - **Regression prevention**: CI fails on new violations, preventing deprecated fields from reappearing.
55
+ - **Clear guidance**: Exact replacement field suggested for each violation.
56
+ - **Zero production impact**: Validation only runs in development and test environments.
57
+
58
+ ### Trade-offs
59
+
60
+ - **Execution path dependency**: Only detects violations in code paths that execute during the process.
61
+ - **Runtime overhead**: Adds interception to all non-production processes.
62
+ - **At-exit reporting**: Violations not visible until process completes.
63
+ - **Baseline maintenance**: YAML files require updates when violations are fixed or added.
64
+ - **Learning curve**: Developers must understand baseline management.
65
+ - **File-level scoping**: Multiple violations in the same file are tracked together, making partial fixes during test runs challenging.
66
+
67
+ ### Risks and mitigations
68
+
69
+ | Risk | Mitigation |
70
+ |------|------------|
71
+ | Limited code path coverage misses violations | Use Kibana to identify fields in production logs, run comprehensive test suites, use development server testing |
72
+ | Baseline drift across branches | Clear documentation, automated baseline regeneration support |
73
+ | At-exit reporting missed if process crashes | Violations still prevented in CI where processes complete successfully |
74
+
75
+ ## Alternatives
76
+
77
+ ### Alternative 1: Static analysis with RuboCop
78
+
79
+ Use custom RuboCop cops to detect deprecated field usage.
80
+
81
+ Rejected because:
82
+
83
+ - Can't handle fields merged from hash variables.
84
+ - Would produce many false negatives.
85
+ - Complex pattern matching still misses dynamic cases.
86
+ - Poor developer experience with unclear violations.
87
+
88
+ ### Alternative 2: Manual tracking
89
+
90
+ Track violations in spreadsheets or GitLab issues.
91
+
92
+ Rejected because:
93
+
94
+ - No automated enforcement or detection.
95
+ - Manual process becomes stale quickly.
96
+ - No shift-left feedback to developers.
97
+ - Can't prevent regressions.
98
+
99
+ ### Alternative 3: Grep-based CI checks
100
+
101
+ Search source code for deprecated field strings in CI.
102
+
103
+ Rejected because:
104
+
105
+ - High false positive rate (matches in comments, strings, tests).
106
+ - Can't distinguish field usage from definitions.
107
+ - No stable tracking across refactoring.
108
+ - Poor user experience with unclear error messages.
109
+
110
+ ### Alternative 4: Production log analysis only
111
+
112
+ Rely solely on Kibana analysis to find deprecated fields.
113
+
114
+ Rejected because:
115
+
116
+ - No prevention, only reactive detection.
117
+ - Violations already in production when discovered.
118
+ - No developer feedback during development.
119
+ - Difficult to trace back to specific code locations.
120
+
121
+ ## References
122
+
123
+ - [Parent Epic](https://gitlab.com/groups/gitlab-org/quality/-/work_items/235)
124
+ - [Observability Field Standardisation](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/observability_field_standardisation/)
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_runtime_dependency "openssl", "~> 3.3.2"
30
30
  spec.add_runtime_dependency "opentracing", "~> 0.4"
31
31
  spec.add_runtime_dependency "pg_query", ">= 6.1.0", "< 7.0"
32
- spec.add_runtime_dependency "prometheus-client-mmap", "~> 1.2.9"
32
+ spec.add_runtime_dependency "prometheus-client-mmap", ">= 1.2", "< 2.0"
33
33
  spec.add_runtime_dependency "redis", "> 3.0.0", "< 6.0.0"
34
34
 
35
35
  # Please maintain alphabetical order for dev dependencies
@@ -38,6 +38,12 @@ module Labkit
38
38
  end
39
39
 
40
40
  def format_message(severity, timestamp, progname, message)
41
+ data = format_data(severity, timestamp, progname, message)
42
+
43
+ dump_json(data) << "\n"
44
+ end
45
+
46
+ def format_data(severity, timestamp, progname, message)
41
47
  data = default_attributes
42
48
  data[:severity] = severity
43
49
  data[:time] = timestamp.utc.iso8601(3)
@@ -53,11 +59,10 @@ module Labkit
53
59
  data[:message] = message
54
60
  when Hash
55
61
  reject_reserved_log_keys!(message)
56
- format_time!(message)
57
62
  data.merge!(message)
58
63
  end
59
64
 
60
- dump_json(data) << "\n"
65
+ data
61
66
  end
62
67
 
63
68
  private
@@ -79,28 +84,6 @@ module Labkit
79
84
  "\n\nUse key names that are descriptive e.g. by using a prefix."
80
85
  end
81
86
  end
82
-
83
- def format_time!(hash)
84
- hash.each do |key, value|
85
- hash[key] = convert_time_value(value)
86
- end
87
- end
88
-
89
- def convert_time_value(value)
90
- case value
91
- when Time
92
- value.utc.iso8601(3)
93
- when DateTime
94
- value.to_time.utc.iso8601(3)
95
- when Hash
96
- format_time!(value)
97
- value
98
- when Array
99
- value.map { |v| convert_time_value(v) }
100
- else
101
- value
102
- end
103
- end
104
87
  end
105
88
  end
106
89
  end
@@ -236,9 +236,9 @@ module Labkit
236
236
  user_experience_id: id,
237
237
  feature_category: @definition.feature_category,
238
238
  urgency: @definition.urgency,
239
- start_time: @start_time,
240
- checkpoint_time: @checkpoint_time,
241
- end_time: @end_time,
239
+ start_time: @start_time&.iso8601(3),
240
+ checkpoint_time: @checkpoint_time&.iso8601(3),
241
+ end_time: @end_time&.iso8601(3),
242
242
  elapsed_time_s: elapsed_time,
243
243
  urgency_threshold_s: urgency_threshold
244
244
  )
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.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate
@@ -169,16 +169,22 @@ dependencies:
169
169
  name: prometheus-client-mmap
170
170
  requirement: !ruby/object:Gem::Requirement
171
171
  requirements:
172
- - - "~>"
172
+ - - ">="
173
173
  - !ruby/object:Gem::Version
174
- version: 1.2.9
174
+ version: '1.2'
175
+ - - "<"
176
+ - !ruby/object:Gem::Version
177
+ version: '2.0'
175
178
  type: :runtime
176
179
  prerelease: false
177
180
  version_requirements: !ruby/object:Gem::Requirement
178
181
  requirements:
179
- - - "~>"
182
+ - - ">="
180
183
  - !ruby/object:Gem::Version
181
- version: 1.2.9
184
+ version: '1.2'
185
+ - - "<"
186
+ - !ruby/object:Gem::Version
187
+ version: '2.0'
182
188
  - !ruby/object:Gem::Dependency
183
189
  name: redis
184
190
  requirement: !ruby/object:Gem::Requirement
@@ -475,6 +481,7 @@ files:
475
481
  - Rakefile
476
482
  - config/user_experience_slis/schema.json
477
483
  - config/user_experience_slis/testing_sample.yml
484
+ - doc/architecture/decisions/001_field_standardization_dynamic_runtime_linting.md
478
485
  - gitlab-labkit.gemspec
479
486
  - lib/gitlab-labkit.rb
480
487
  - lib/labkit/context.rb