gitlab-labkit 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4260cb369ba6b4c22a6e11a74d659eb9b4167a1686ca0cdb44790257bd998424
4
- data.tar.gz: 9dafed39c31c650cddd8db4d55e130f788ee6f5a7ba803195d194c57d4e90632
3
+ metadata.gz: c306478521702991b9c5ba1d80e6ab1c3bcb42b70562890a48204ab9e320c6b8
4
+ data.tar.gz: 5b4fdf319fa79405c89c0de7d91617602a6aec6bf784223a6d8e86830c6900ff
5
5
  SHA512:
6
- metadata.gz: 391b5320921ab7222045400828e21c8b92f28764bae9ad2bd4cd1525ce37099eeed5f9ff4af942c5a74233d9ce5819907ca77d475b3f575d281baedb08640ee1
7
- data.tar.gz: '00386cc726461cae68fa4648fd43a27eb6afeacce77e61348ce6244b0c1b4c4fd9bceb252e7c5d5464cbb1d6062f6ae6a8fb61bb013ed109d4361447ba172dcd'
6
+ metadata.gz: f88db3bf552afa6f20e95e8760d91b97149d41e4b1cfe995c0e64bdc852ca0490713ba52a5bdcadebea1c827b83b344df61695386741c37d2dd8458243bd0144
7
+ data.tar.gz: a1ab50bd164cd46da510b03ac53aa3e148a6b8720611ef380b288d282072446b06772c43777c019da4fd93d5d9b1e1f197704fc36bf0b6e47985fc35535e2a9b
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.5.3
1
+ 2.6.3
@@ -28,11 +28,13 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  # Please maintain alphabetical order for dev dependencies
30
30
  spec.add_development_dependency "grpc-tools", "~> 1.19"
31
+ spec.add_development_dependency "pry", "~> 0.12"
31
32
  spec.add_development_dependency "rack", "~> 2.0"
32
33
  spec.add_development_dependency "rake", "~> 12.3"
33
- spec.add_development_dependency "rspec", "~> 3.6.0"
34
+ spec.add_development_dependency "rspec", "~> 3.8.0"
34
35
  spec.add_development_dependency "rspec-parameterized", "~> 0.4"
35
36
  spec.add_development_dependency "rubocop", "~> 0.65.0"
36
37
  spec.add_development_dependency "rubocop-rspec", "~> 1.22.1"
37
38
  spec.add_development_dependency "rufo", "~> 0.6"
39
+ spec.add_development_dependency "sidekiq", "~> 5.2.7"
38
40
  end
data/lib/gitlab-labkit.rb CHANGED
@@ -8,8 +8,10 @@ require "active_support/all"
8
8
  # observability.
9
9
  module Labkit
10
10
  autoload :Correlation, "labkit/correlation"
11
+ autoload :Context, "labkit/context"
11
12
  autoload :Tracing, "labkit/tracing"
12
13
  autoload :Logging, "labkit/logging"
14
+ autoload :Middleware, "labkit/middleware"
13
15
  end
14
16
 
15
17
  # rubocop:enable Naming/FileName
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ # A context can be used to provide structured information on what resources
5
+ # GitLab is working on within a service.
6
+ # The currently supported keys are defined in the `KNOWN_KEYS` constant.
7
+ #
8
+ # Values can be provided by passing a hash. If one of the values is a Proc
9
+ # the proc will only be called when the value is actually needed.
10
+ #
11
+ # Multiple contexts can be nested, the nested context will inherit the values
12
+ # from the closest outer one.
13
+ # All contexts will have the same correlation id.
14
+ #
15
+ # Usage:
16
+ # Labkit::Context.with_context(user: 'username', root_namespace: -> { get_root_namespace } do |context|
17
+ # logger.info(context.to_h)
18
+ # end
19
+ #
20
+ class Context
21
+ LOG_KEY = "meta"
22
+ CORRELATION_ID_KEY = "correlation_id"
23
+ RAW_KEYS = [CORRELATION_ID_KEY].freeze
24
+ KNOWN_KEYS = %w[user project root_namespace].freeze
25
+
26
+ class << self
27
+ def with_context(attributes = {})
28
+ context = push(attributes)
29
+
30
+ begin
31
+ yield(context)
32
+ ensure
33
+ pop(context)
34
+ end
35
+ end
36
+
37
+ def push(new_attributes = {})
38
+ new_context = current&.merge(new_attributes) || new(new_attributes)
39
+
40
+ contexts.push(new_context)
41
+
42
+ new_context
43
+ end
44
+
45
+ def pop(context)
46
+ contexts.pop while contexts.include?(context)
47
+ end
48
+
49
+ def correlation_id
50
+ contexts.last&.correlation_id
51
+ end
52
+
53
+ def current
54
+ contexts.last
55
+ end
56
+
57
+ def log_key(key)
58
+ key = key.to_s
59
+ return key if RAW_KEYS.include?(key)
60
+ return key if key.starts_with?("#{LOG_KEY}.")
61
+
62
+ "#{LOG_KEY}.#{key}"
63
+ end
64
+
65
+ def known_log_keys
66
+ @known_log_keys ||= (KNOWN_KEYS.map(&method(:log_key)) + RAW_KEYS).freeze
67
+ end
68
+
69
+ private
70
+
71
+ def contexts
72
+ Thread.current[:labkit_contexts] ||= []
73
+ end
74
+ end
75
+
76
+ def initialize(values = {})
77
+ @data = {}
78
+
79
+ assign_attributes(values)
80
+ end
81
+
82
+ def merge(new_attributes)
83
+ new_context = self.class.new(data.dup)
84
+ new_context.assign_attributes(new_attributes)
85
+
86
+ new_context
87
+ end
88
+
89
+ def to_h
90
+ expand_data
91
+ end
92
+
93
+ def correlation_id
94
+ data[CORRELATION_ID_KEY]
95
+ end
96
+
97
+ protected
98
+
99
+ def assign_attributes(attributes)
100
+ attributes = attributes.transform_keys(&method(:log_key))
101
+ attributes = attributes.slice(*known_log_keys)
102
+
103
+ data.merge!(attributes)
104
+
105
+ # Remove keys that had their values set to `nil` in the new attributes
106
+ data.keep_if { |_, value| value.present? }
107
+
108
+ # Assign a correlation if it was missing in the first context or when
109
+ # explicitly removed
110
+ data[CORRELATION_ID_KEY] ||= new_id
111
+
112
+ data
113
+ end
114
+
115
+ private
116
+
117
+ delegate :log_key, :known_log_keys, to: :class
118
+
119
+ attr_reader :data
120
+
121
+ def expand_data
122
+ data.transform_values do |value|
123
+ value = value.respond_to?(:call) ? value.call : value
124
+
125
+ value.presence
126
+ end.compact
127
+ end
128
+
129
+ def new_id
130
+ SecureRandom.hex
131
+ end
132
+ end
133
+ end
@@ -5,38 +5,21 @@ module Labkit
5
5
  # CorrelationId module provides access the Correlation-ID
6
6
  # of the current request
7
7
  module CorrelationId
8
- LOG_KEY = "correlation_id"
8
+ LOG_KEY = Labkit::Context::CORRELATION_ID_KEY
9
9
 
10
10
  class << self
11
- def use_id(correlation_id, &_blk)
12
- # always generate a id if null is passed
13
- correlation_id ||= new_id
14
-
15
- ids.push(correlation_id || new_id)
16
-
17
- begin
18
- yield(current_id)
19
- ensure
20
- ids.pop
11
+ def use_id(correlation_id)
12
+ Labkit::Context.with_context(LOG_KEY => correlation_id) do |context|
13
+ yield(context.correlation_id)
21
14
  end
22
15
  end
23
16
 
24
17
  def current_id
25
- ids.last
18
+ Labkit::Context.correlation_id
26
19
  end
27
20
 
28
21
  def current_or_new_id
29
- current_id || new_id
30
- end
31
-
32
- private
33
-
34
- def ids
35
- Thread.current[:correlation_id] ||= []
36
- end
37
-
38
- def new_id
39
- SecureRandom.uuid
22
+ current_id || Labkit::Context.push.correlation_id
40
23
  end
41
24
  end
42
25
  end
@@ -4,6 +4,7 @@ module Labkit
4
4
  module Correlation
5
5
  # The GRPC module contains functionality for instrumenting GRPC calls
6
6
  module GRPC
7
+ autoload :ClientInterceptor, "labkit/correlation/grpc/client_interceptor"
7
8
  autoload :GRPCCommon, "labkit/correlation/grpc/grpc_common"
8
9
  autoload :ServerInterceptor, "labkit/correlation/grpc/server_interceptor"
9
10
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Disable the UnusedMethodArgument linter, since we need to declare the kwargs
4
+ # in the methods, but we don't actually use them.
5
+ # rubocop:disable Lint/UnusedMethodArgument
6
+
7
+ require "grpc"
8
+
9
+ module Labkit
10
+ module Correlation
11
+ module GRPC
12
+ # ClientInterceptor is used to inject the correlation_id into the metadata
13
+ # or a GRPC call for onward propagation to the server
14
+ class ClientInterceptor < ::GRPC::ClientInterceptor
15
+ include Labkit::Correlation::GRPC::GRPCCommon
16
+ include Singleton
17
+
18
+ def request_response(request:, call:, method:, metadata:)
19
+ inject_correlation_id_into_metadata(metadata)
20
+
21
+ yield
22
+ end
23
+
24
+ def client_streamer(requests:, call:, method:, metadata:)
25
+ inject_correlation_id_into_metadata(metadata)
26
+
27
+ yield
28
+ end
29
+
30
+ def server_streamer(request:, call:, method:, metadata:)
31
+ inject_correlation_id_into_metadata(metadata)
32
+
33
+ yield
34
+ end
35
+
36
+ def bidi_streamer(requests:, call:, method:, metadata:)
37
+ inject_correlation_id_into_metadata(metadata)
38
+
39
+ yield
40
+ end
41
+
42
+ private
43
+
44
+ def inject_correlation_id_into_metadata(metadata, &block)
45
+ metadata[CORRELATION_METADATA_KEY] = Labkit::Correlation::CorrelationId.current_id if Labkit::Correlation::CorrelationId.current_id
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # rubocop:enable Lint/UnusedMethodArgument
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ # Adds middlewares for using in rack and sidekiq
5
+ module Middleware
6
+ autoload :Rack, "labkit/middleware/rack"
7
+ autoload :Sidekiq, "labkit/middleware/sidekiq"
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch"
4
+
5
+ module Labkit
6
+ module Middleware
7
+ # This is a rack middleware to be inserted in GitLab-rails
8
+ # It makes sure that there's always a root context containing the correlation
9
+ # id.
10
+ # Since this context always get's cleaned up by this middleware, we can be
11
+ # sure that any nested contexts will also be cleaned up.
12
+ class Rack
13
+ def initialize(app)
14
+ @app = app
15
+ end
16
+
17
+ def call(env)
18
+ Labkit::Context.with_context(Labkit::Context::CORRELATION_ID_KEY => correlation_id(env)) do
19
+ @app.call(env)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def correlation_id(env)
26
+ request(env).request_id
27
+ end
28
+
29
+ def request(env)
30
+ ActionDispatch::Request.new(env)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ module Middleware
5
+ # Middleware for sidekiq
6
+ module Sidekiq
7
+ autoload :Client, "labkit/middleware/sidekiq/client"
8
+ autoload :Server, "labkit/middleware/sidekiq/server"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ module Middleware
5
+ module Sidekiq
6
+ # This middleware for Sidekiq-client wraps scheduling jobs in a context
7
+ # The context will also be added to the sidekiq job in redis so it can
8
+ # be reinstantiated by Sidekiq-server when running the job.
9
+ class Client
10
+ def call(_worker_class, job, _queue, _redis_pool)
11
+ Labkit::Context.with_context do |context|
12
+ job.merge!(context.to_h)
13
+
14
+ yield
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Labkit
4
+ module Middleware
5
+ module Sidekiq
6
+ # This middleware for Sidekiq-client uses the values stored on a job to
7
+ # reinstantiate a context in which the job will run.
8
+ class Server
9
+ def call(_worker_class, job, _queue)
10
+ Labkit::Context.with_context(job) do |_context|
11
+ yield
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-labkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-04 00:00:00.000000000 Z
11
+ date: 2019-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -126,6 +126,20 @@ dependencies:
126
126
  - - "~>"
127
127
  - !ruby/object:Gem::Version
128
128
  version: '1.19'
129
+ - !ruby/object:Gem::Dependency
130
+ name: pry
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '0.12'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '0.12'
129
143
  - !ruby/object:Gem::Dependency
130
144
  name: rack
131
145
  requirement: !ruby/object:Gem::Requirement
@@ -160,14 +174,14 @@ dependencies:
160
174
  requirements:
161
175
  - - "~>"
162
176
  - !ruby/object:Gem::Version
163
- version: 3.6.0
177
+ version: 3.8.0
164
178
  type: :development
165
179
  prerelease: false
166
180
  version_requirements: !ruby/object:Gem::Requirement
167
181
  requirements:
168
182
  - - "~>"
169
183
  - !ruby/object:Gem::Version
170
- version: 3.6.0
184
+ version: 3.8.0
171
185
  - !ruby/object:Gem::Dependency
172
186
  name: rspec-parameterized
173
187
  requirement: !ruby/object:Gem::Requirement
@@ -224,6 +238,20 @@ dependencies:
224
238
  - - "~>"
225
239
  - !ruby/object:Gem::Version
226
240
  version: '0.6'
241
+ - !ruby/object:Gem::Dependency
242
+ name: sidekiq
243
+ requirement: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - "~>"
246
+ - !ruby/object:Gem::Version
247
+ version: 5.2.7
248
+ type: :development
249
+ prerelease: false
250
+ version_requirements: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - "~>"
253
+ - !ruby/object:Gem::Version
254
+ version: 5.2.7
227
255
  description:
228
256
  email:
229
257
  - andrew@gitlab.com
@@ -243,13 +271,20 @@ files:
243
271
  - Rakefile
244
272
  - gitlab-labkit.gemspec
245
273
  - lib/gitlab-labkit.rb
274
+ - lib/labkit/context.rb
246
275
  - lib/labkit/correlation.rb
247
276
  - lib/labkit/correlation/correlation_id.rb
248
277
  - lib/labkit/correlation/grpc.rb
278
+ - lib/labkit/correlation/grpc/client_interceptor.rb
249
279
  - lib/labkit/correlation/grpc/grpc_common.rb
250
280
  - lib/labkit/correlation/grpc/server_interceptor.rb
251
281
  - lib/labkit/logging.rb
252
282
  - lib/labkit/logging/sanitizer.rb
283
+ - lib/labkit/middleware.rb
284
+ - lib/labkit/middleware/rack.rb
285
+ - lib/labkit/middleware/sidekiq.rb
286
+ - lib/labkit/middleware/sidekiq/client.rb
287
+ - lib/labkit/middleware/sidekiq/server.rb
253
288
  - lib/labkit/tracing.rb
254
289
  - lib/labkit/tracing/factory.rb
255
290
  - lib/labkit/tracing/grpc.rb