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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# frozen_string_literal: true
module Gitlab
# A GitLab-rails specific accessor for `Labkit::Logging::ApplicationContext`
class ApplicationContext
include Gitlab::Utils::LazyAttributes
include Gitlab::Utils::StrongMemoize
Attribute = Struct.new(:name, :type)
LOG_KEY = Labkit::Context::LOG_KEY
KNOWN_KEYS = [
:user,
:user_id,
:project,
:root_namespace,
:client_id,
:caller_id,
:remote_ip,
:job_id,
:pipeline_id,
:related_class,
:feature_category,
:artifact_size,
:artifact_used_cdn,
:artifacts_dependencies_size,
:artifacts_dependencies_count,
:root_caller_id
].freeze
private_constant :KNOWN_KEYS
APPLICATION_ATTRIBUTES = [
Attribute.new(:project, Project),
Attribute.new(:namespace, Namespace),
Attribute.new(:user, User),
Attribute.new(:runner, ::Ci::Runner),
Attribute.new(:caller_id, String),
Attribute.new(:remote_ip, String),
Attribute.new(:job, ::Ci::Build),
Attribute.new(:related_class, String),
Attribute.new(:feature_category, String),
Attribute.new(:artifact, ::Ci::JobArtifact),
Attribute.new(:artifact_used_cdn, Object),
Attribute.new(:artifacts_dependencies_size, Integer),
Attribute.new(:artifacts_dependencies_count, Integer),
Attribute.new(:root_caller_id, String)
].freeze
def self.known_keys
KNOWN_KEYS
end
def self.with_context(args, &block)
application_context = new(**args)
application_context.use(&block)
end
def self.with_raw_context(attributes = {}, &block)
Labkit::Context.with_context(attributes, &block)
end
def self.push(args)
application_context = new(**args)
Labkit::Context.push(application_context.to_lazy_hash)
end
def self.current
Labkit::Context.current.to_h
end
def self.current_context_include?(attribute_name)
current.include?(Labkit::Context.log_key(attribute_name))
end
def self.current_context_attribute(attribute_name)
Labkit::Context.current&.get_attribute(attribute_name)
end
def initialize(**args)
unknown_attributes = args.keys - APPLICATION_ATTRIBUTES.map(&:name)
raise ArgumentError, "#{unknown_attributes} are not known keys" if unknown_attributes.any?
@set_values = args.keys
assign_attributes(args)
end
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/PerceivedComplexity
def to_lazy_hash
{}.tap do |hash|
assign_hash_if_value(hash, :caller_id)
assign_hash_if_value(hash, :root_caller_id)
assign_hash_if_value(hash, :remote_ip)
assign_hash_if_value(hash, :related_class)
assign_hash_if_value(hash, :feature_category)
assign_hash_if_value(hash, :artifact_used_cdn)
assign_hash_if_value(hash, :artifacts_dependencies_size)
assign_hash_if_value(hash, :artifacts_dependencies_count)
hash[:user] = -> { username } if include_user?
hash[:user_id] = -> { user_id } if include_user?
hash[:project] = -> { project_path } if include_project?
hash[:root_namespace] = -> { root_namespace_path } if include_namespace?
hash[:client_id] = -> { client } if include_client?
hash[:pipeline_id] = -> { job&.pipeline_id } if set_values.include?(:job)
hash[:job_id] = -> { job&.id } if set_values.include?(:job)
hash[:artifact_size] = -> { artifact&.size } if set_values.include?(:artifact)
end
end
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/PerceivedComplexity
def use
Labkit::Context.with_context(to_lazy_hash) { yield }
end
private
attr_reader :set_values
APPLICATION_ATTRIBUTES.each do |attr|
lazy_attr_reader attr.name, type: attr.type
end
def assign_hash_if_value(hash, attribute_name)
unless KNOWN_KEYS.include?(attribute_name)
raise ArgumentError, "unknown attribute `#{attribute_name}`"
end
# rubocop:disable GitlabSecurity/PublicSend
hash[attribute_name] = public_send(attribute_name) if set_values.include?(attribute_name)
# rubocop:enable GitlabSecurity/PublicSend
end
def assign_attributes(values)
values.slice(*APPLICATION_ATTRIBUTES.map(&:name)).each do |name, value|
instance_variable_set("@#{name}", value)
end
end
def project_path
associated_routable = project || runner_project || job_project
associated_routable&.full_path
end
def username
associated_user = user || job_user
associated_user&.username
end
def user_id
associated_user = user || job_user
associated_user&.id
end
def root_namespace_path
associated_routable = namespace || project || runner_project || runner_group || job_project
associated_routable&.full_path_components&.first
end
def include_namespace?
set_values.include?(:namespace) || set_values.include?(:project) || set_values.include?(:runner) || set_values.include?(:job)
end
def include_client?
set_values.include?(:user) || set_values.include?(:runner) || set_values.include?(:remote_ip)
end
def include_user?
set_values.include?(:user) || set_values.include?(:job)
end
def include_project?
set_values.include?(:project) || set_values.include?(:runner) || set_values.include?(:job)
end
def client
if runner
"runner/#{runner.id}"
elsif user
"user/#{user.id}"
else
"ip/#{remote_ip}"
end
end
def runner_project
strong_memoize(:runner_project) do
next unless runner&.project_type?
runner_projects = runner.runner_projects.take(2) # rubocop: disable CodeReuse/ActiveRecord
runner_projects.first.project if runner_projects.one?
end
end
def runner_group
strong_memoize(:runner_group) do
next unless runner&.group_type?
runner.groups.first
end
end
def job_project
strong_memoize(:job_project) do
job&.project
end
end
def job_user
strong_memoize(:job_user) do
job&.user
end
end
end
end
Gitlab::ApplicationContext.prepend_mod_with('Gitlab::ApplicationContext')
|