gitlab-labkit 0.7.0 → 0.8.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: 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