sidekiq 5.2.7 → 8.0.5

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.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +845 -8
  3. data/LICENSE.txt +9 -0
  4. data/README.md +54 -54
  5. data/bin/multi_queue_bench +271 -0
  6. data/bin/sidekiq +22 -3
  7. data/bin/sidekiqload +219 -112
  8. data/bin/sidekiqmon +11 -0
  9. data/bin/webload +69 -0
  10. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +120 -0
  11. data/lib/generators/sidekiq/job_generator.rb +59 -0
  12. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  13. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  14. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  15. data/lib/sidekiq/api.rb +757 -373
  16. data/lib/sidekiq/capsule.rb +132 -0
  17. data/lib/sidekiq/cli.rb +210 -233
  18. data/lib/sidekiq/client.rb +145 -103
  19. data/lib/sidekiq/component.rb +128 -0
  20. data/lib/sidekiq/config.rb +315 -0
  21. data/lib/sidekiq/deploy.rb +64 -0
  22. data/lib/sidekiq/embedded.rb +64 -0
  23. data/lib/sidekiq/fetch.rb +49 -42
  24. data/lib/sidekiq/iterable_job.rb +56 -0
  25. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  26. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  27. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  28. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  29. data/lib/sidekiq/job/iterable.rb +306 -0
  30. data/lib/sidekiq/job.rb +385 -0
  31. data/lib/sidekiq/job_logger.rb +34 -7
  32. data/lib/sidekiq/job_retry.rb +164 -109
  33. data/lib/sidekiq/job_util.rb +113 -0
  34. data/lib/sidekiq/launcher.rb +208 -107
  35. data/lib/sidekiq/logger.rb +80 -0
  36. data/lib/sidekiq/manager.rb +42 -46
  37. data/lib/sidekiq/metrics/query.rb +184 -0
  38. data/lib/sidekiq/metrics/shared.rb +109 -0
  39. data/lib/sidekiq/metrics/tracking.rb +150 -0
  40. data/lib/sidekiq/middleware/chain.rb +113 -56
  41. data/lib/sidekiq/middleware/current_attributes.rb +119 -0
  42. data/lib/sidekiq/middleware/i18n.rb +7 -7
  43. data/lib/sidekiq/middleware/modules.rb +23 -0
  44. data/lib/sidekiq/monitor.rb +147 -0
  45. data/lib/sidekiq/paginator.rb +41 -16
  46. data/lib/sidekiq/processor.rb +146 -127
  47. data/lib/sidekiq/profiler.rb +72 -0
  48. data/lib/sidekiq/rails.rb +46 -43
  49. data/lib/sidekiq/redis_client_adapter.rb +113 -0
  50. data/lib/sidekiq/redis_connection.rb +79 -108
  51. data/lib/sidekiq/ring_buffer.rb +31 -0
  52. data/lib/sidekiq/scheduled.rb +112 -50
  53. data/lib/sidekiq/sd_notify.rb +149 -0
  54. data/lib/sidekiq/systemd.rb +26 -0
  55. data/lib/sidekiq/testing/inline.rb +6 -5
  56. data/lib/sidekiq/testing.rb +91 -90
  57. data/lib/sidekiq/transaction_aware_client.rb +51 -0
  58. data/lib/sidekiq/version.rb +7 -1
  59. data/lib/sidekiq/web/action.rb +125 -60
  60. data/lib/sidekiq/web/application.rb +363 -259
  61. data/lib/sidekiq/web/config.rb +120 -0
  62. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  63. data/lib/sidekiq/web/helpers.rb +241 -120
  64. data/lib/sidekiq/web/router.rb +62 -71
  65. data/lib/sidekiq/web.rb +69 -161
  66. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  67. data/lib/sidekiq.rb +94 -182
  68. data/sidekiq.gemspec +26 -16
  69. data/web/assets/images/apple-touch-icon.png +0 -0
  70. data/web/assets/javascripts/application.js +150 -61
  71. data/web/assets/javascripts/base-charts.js +120 -0
  72. data/web/assets/javascripts/chart.min.js +13 -0
  73. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  74. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  75. data/web/assets/javascripts/dashboard-charts.js +194 -0
  76. data/web/assets/javascripts/dashboard.js +41 -293
  77. data/web/assets/javascripts/metrics.js +280 -0
  78. data/web/assets/stylesheets/style.css +766 -0
  79. data/web/locales/ar.yml +72 -65
  80. data/web/locales/cs.yml +63 -62
  81. data/web/locales/da.yml +61 -53
  82. data/web/locales/de.yml +66 -53
  83. data/web/locales/el.yml +44 -24
  84. data/web/locales/en.yml +94 -66
  85. data/web/locales/es.yml +92 -54
  86. data/web/locales/fa.yml +66 -65
  87. data/web/locales/fr.yml +83 -62
  88. data/web/locales/gd.yml +99 -0
  89. data/web/locales/he.yml +66 -64
  90. data/web/locales/hi.yml +60 -59
  91. data/web/locales/it.yml +93 -54
  92. data/web/locales/ja.yml +75 -64
  93. data/web/locales/ko.yml +53 -52
  94. data/web/locales/lt.yml +84 -0
  95. data/web/locales/nb.yml +62 -61
  96. data/web/locales/nl.yml +53 -52
  97. data/web/locales/pl.yml +46 -45
  98. data/web/locales/{pt-br.yml → pt-BR.yml} +84 -56
  99. data/web/locales/pt.yml +52 -51
  100. data/web/locales/ru.yml +69 -63
  101. data/web/locales/sv.yml +54 -53
  102. data/web/locales/ta.yml +61 -60
  103. data/web/locales/tr.yml +101 -0
  104. data/web/locales/uk.yml +86 -61
  105. data/web/locales/ur.yml +65 -64
  106. data/web/locales/vi.yml +84 -0
  107. data/web/locales/zh-CN.yml +106 -0
  108. data/web/locales/{zh-tw.yml → zh-TW.yml} +43 -9
  109. data/web/views/_footer.erb +31 -19
  110. data/web/views/_job_info.erb +94 -75
  111. data/web/views/_metrics_period_select.erb +15 -0
  112. data/web/views/_nav.erb +14 -21
  113. data/web/views/_paging.erb +23 -19
  114. data/web/views/_poll_link.erb +3 -6
  115. data/web/views/_summary.erb +23 -23
  116. data/web/views/busy.erb +139 -87
  117. data/web/views/dashboard.erb +82 -53
  118. data/web/views/dead.erb +31 -27
  119. data/web/views/filtering.erb +6 -0
  120. data/web/views/layout.erb +15 -29
  121. data/web/views/metrics.erb +84 -0
  122. data/web/views/metrics_for_job.erb +58 -0
  123. data/web/views/morgue.erb +60 -70
  124. data/web/views/profiles.erb +43 -0
  125. data/web/views/queue.erb +50 -39
  126. data/web/views/queues.erb +45 -29
  127. data/web/views/retries.erb +65 -75
  128. data/web/views/retry.erb +32 -27
  129. data/web/views/scheduled.erb +58 -52
  130. data/web/views/scheduled_job_info.erb +1 -1
  131. metadata +96 -76
  132. data/.circleci/config.yml +0 -61
  133. data/.github/contributing.md +0 -32
  134. data/.github/issue_template.md +0 -11
  135. data/.gitignore +0 -15
  136. data/.travis.yml +0 -11
  137. data/3.0-Upgrade.md +0 -70
  138. data/4.0-Upgrade.md +0 -53
  139. data/5.0-Upgrade.md +0 -56
  140. data/COMM-LICENSE +0 -97
  141. data/Ent-Changes.md +0 -238
  142. data/Gemfile +0 -23
  143. data/LICENSE +0 -9
  144. data/Pro-2.0-Upgrade.md +0 -138
  145. data/Pro-3.0-Upgrade.md +0 -44
  146. data/Pro-4.0-Upgrade.md +0 -35
  147. data/Pro-Changes.md +0 -759
  148. data/Rakefile +0 -9
  149. data/bin/sidekiqctl +0 -20
  150. data/code_of_conduct.md +0 -50
  151. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  152. data/lib/sidekiq/core_ext.rb +0 -1
  153. data/lib/sidekiq/ctl.rb +0 -221
  154. data/lib/sidekiq/delay.rb +0 -42
  155. data/lib/sidekiq/exception_handler.rb +0 -29
  156. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  157. data/lib/sidekiq/extensions/active_record.rb +0 -40
  158. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  159. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
  160. data/lib/sidekiq/logging.rb +0 -122
  161. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  162. data/lib/sidekiq/util.rb +0 -66
  163. data/lib/sidekiq/worker.rb +0 -220
  164. data/web/assets/stylesheets/application-rtl.css +0 -246
  165. data/web/assets/stylesheets/application.css +0 -1144
  166. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  167. data/web/assets/stylesheets/bootstrap.css +0 -5
  168. data/web/locales/zh-cn.yml +0 -68
  169. data/web/views/_status.erb +0 -4
@@ -0,0 +1,385 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq/client"
4
+
5
+ module Sidekiq
6
+ ##
7
+ # Include this module in your job class and you can easily create
8
+ # asynchronous jobs:
9
+ #
10
+ # class HardJob
11
+ # include Sidekiq::Job
12
+ # sidekiq_options queue: 'critical', retry: 5
13
+ #
14
+ # def perform(*args)
15
+ # # do some work
16
+ # end
17
+ # end
18
+ #
19
+ # Then in your Rails app, you can do this:
20
+ #
21
+ # HardJob.perform_async(1, 2, 3)
22
+ #
23
+ # Note that perform_async is a class method, perform is an instance method.
24
+ #
25
+ # Sidekiq::Job also includes several APIs to provide compatibility with
26
+ # ActiveJob.
27
+ #
28
+ # class SomeJob
29
+ # include Sidekiq::Job
30
+ # queue_as :critical
31
+ #
32
+ # def perform(...)
33
+ # end
34
+ # end
35
+ #
36
+ # SomeJob.set(wait_until: 1.hour).perform_async(123)
37
+ #
38
+ # Note that arguments passed to the job must still obey Sidekiq's
39
+ # best practice for simple, JSON-native data types. Sidekiq will not
40
+ # implement ActiveJob's more complex argument serialization. For
41
+ # this reason, we don't implement `perform_later` as our call semantics
42
+ # are very different.
43
+ #
44
+ module Job
45
+ ##
46
+ # The Options module is extracted so we can include it in ActiveJob::Base
47
+ # and allow native AJs to configure Sidekiq features/internals.
48
+ module Options
49
+ def self.included(base)
50
+ base.extend(ClassMethods)
51
+ base.sidekiq_class_attribute :sidekiq_options_hash
52
+ base.sidekiq_class_attribute :sidekiq_retry_in_block
53
+ base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
54
+ end
55
+
56
+ module ClassMethods
57
+ ACCESSOR_MUTEX = Mutex.new
58
+
59
+ ##
60
+ # Allows customization for this type of Job.
61
+ # Legal options:
62
+ #
63
+ # queue - name of queue to use for this job type, default *default*
64
+ # retry - enable retries for this Job in case of error during execution,
65
+ # *true* to use the default or *Integer* count
66
+ # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
67
+ # can be true, false or an integer number of lines to save, default *false*
68
+ #
69
+ # In practice, any option is allowed. This is the main mechanism to configure the
70
+ # options for a specific job.
71
+ def sidekiq_options(opts = {})
72
+ # stringify 2 levels of keys
73
+ opts = opts.to_h do |k, v|
74
+ [k.to_s, (Hash === v) ? v.transform_keys(&:to_s) : v]
75
+ end
76
+
77
+ self.sidekiq_options_hash = get_sidekiq_options.merge(opts)
78
+ end
79
+
80
+ def sidekiq_retry_in(&block)
81
+ self.sidekiq_retry_in_block = block
82
+ end
83
+
84
+ def sidekiq_retries_exhausted(&block)
85
+ self.sidekiq_retries_exhausted_block = block
86
+ end
87
+
88
+ def get_sidekiq_options # :nodoc:
89
+ self.sidekiq_options_hash ||= Sidekiq.default_job_options
90
+ end
91
+
92
+ def sidekiq_class_attribute(*attrs)
93
+ instance_reader = true
94
+ instance_writer = true
95
+
96
+ attrs.each do |name|
97
+ synchronized_getter = "__synchronized_#{name}"
98
+
99
+ singleton_class.instance_eval do
100
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
101
+ end
102
+
103
+ define_singleton_method(synchronized_getter) { nil }
104
+ singleton_class.class_eval do
105
+ private(synchronized_getter)
106
+ end
107
+
108
+ define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
109
+
110
+ ivar = "@#{name}"
111
+
112
+ singleton_class.instance_eval do
113
+ m = "#{name}="
114
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
115
+ end
116
+ define_singleton_method(:"#{name}=") do |val|
117
+ singleton_class.class_eval do
118
+ ACCESSOR_MUTEX.synchronize do
119
+ undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
120
+ define_method(synchronized_getter) { val }
121
+ end
122
+ end
123
+
124
+ if singleton_class?
125
+ class_eval do
126
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
127
+ define_method(name) do
128
+ if instance_variable_defined? ivar
129
+ instance_variable_get ivar
130
+ else
131
+ singleton_class.send name
132
+ end
133
+ end
134
+ end
135
+ end
136
+ val
137
+ end
138
+
139
+ if instance_reader
140
+ undef_method(name) if method_defined?(name) || private_method_defined?(name)
141
+ define_method(name) do
142
+ if instance_variable_defined?(ivar)
143
+ instance_variable_get ivar
144
+ else
145
+ self.class.public_send name
146
+ end
147
+ end
148
+ end
149
+
150
+ if instance_writer
151
+ m = "#{name}="
152
+ undef_method(m) if method_defined?(m) || private_method_defined?(m)
153
+ attr_writer name
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ attr_accessor :jid
161
+
162
+ # This attribute is implementation-specific and not a public API
163
+ attr_accessor :_context
164
+
165
+ def self.included(base)
166
+ raise ArgumentError, "Sidekiq::Job cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
167
+
168
+ base.include(Options)
169
+ base.extend(ClassMethods)
170
+ end
171
+
172
+ def logger
173
+ Sidekiq.logger
174
+ end
175
+
176
+ def interrupted?
177
+ @_context&.stopping?
178
+ end
179
+
180
+ # This helper class encapsulates the set options for `set`, e.g.
181
+ #
182
+ # SomeJob.set(queue: 'foo').perform_async(....)
183
+ #
184
+ class Setter
185
+ include Sidekiq::JobUtil
186
+
187
+ def initialize(klass, opts)
188
+ @klass = klass
189
+ # NB: the internal hash always has stringified keys
190
+ @opts = opts.transform_keys(&:to_s)
191
+
192
+ # ActiveJob compatibility
193
+ interval = @opts.delete("wait_until") || @opts.delete("wait")
194
+ at(interval) if interval
195
+ end
196
+
197
+ def set(options)
198
+ hash = options.transform_keys(&:to_s)
199
+ interval = hash.delete("wait_until") || @opts.delete("wait")
200
+ @opts.merge!(hash)
201
+ at(interval) if interval
202
+ self
203
+ end
204
+
205
+ def perform_async(*args)
206
+ if @opts["sync"] == true
207
+ perform_inline(*args)
208
+ else
209
+ @klass.client_push(@opts.merge("args" => args, "class" => @klass))
210
+ end
211
+ end
212
+
213
+ # Explicit inline execution of a job. Returns nil if the job did not
214
+ # execute, true otherwise.
215
+ def perform_inline(*args)
216
+ raw = @opts.merge("args" => args, "class" => @klass)
217
+
218
+ # validate and normalize payload
219
+ item = normalize_item(raw)
220
+ queue = item["queue"]
221
+
222
+ # run client-side middleware
223
+ cfg = Sidekiq.default_configuration
224
+ result = cfg.client_middleware.invoke(item["class"], item, queue, cfg.redis_pool) do
225
+ item
226
+ end
227
+ return nil unless result
228
+
229
+ # round-trip the payload via JSON
230
+ msg = Sidekiq.load_json(Sidekiq.dump_json(item))
231
+
232
+ # prepare the job instance
233
+ klass = Object.const_get(msg["class"])
234
+ job = klass.new
235
+ job.jid = msg["jid"]
236
+ job.bid = msg["bid"] if job.respond_to?(:bid)
237
+
238
+ # run the job through server-side middleware
239
+ result = cfg.server_middleware.invoke(job, msg, msg["queue"]) do
240
+ # perform it
241
+ job.perform(*msg["args"])
242
+ true
243
+ end
244
+ return nil unless result
245
+ # jobs do not return a result. they should store any
246
+ # modified state.
247
+ true
248
+ end
249
+ alias_method :perform_sync, :perform_inline
250
+
251
+ def perform_bulk(args, batch_size: 1_000)
252
+ client = @klass.build_client
253
+ client.push_bulk(@opts.merge("class" => @klass, "args" => args, :batch_size => batch_size))
254
+ end
255
+
256
+ # +interval+ must be a timestamp, numeric or something that acts
257
+ # numeric (like an activesupport time interval).
258
+ def perform_in(interval, *args)
259
+ at(interval).perform_async(*args)
260
+ end
261
+ alias_method :perform_at, :perform_in
262
+
263
+ private
264
+
265
+ def at(interval)
266
+ int = interval.to_f
267
+ now = Time.now.to_f
268
+ ts = ((int < 1_000_000_000) ? now + int : int)
269
+ # Optimization to enqueue something now that is scheduled to go out now or in the past
270
+ @opts["at"] = ts if ts > now
271
+ self
272
+ end
273
+ end
274
+
275
+ module ClassMethods
276
+ def delay(*args)
277
+ raise ArgumentError, "Do not call .delay on a Sidekiq::Job class, call .perform_async"
278
+ end
279
+
280
+ def delay_for(*args)
281
+ raise ArgumentError, "Do not call .delay_for on a Sidekiq::Job class, call .perform_in"
282
+ end
283
+
284
+ def delay_until(*args)
285
+ raise ArgumentError, "Do not call .delay_until on a Sidekiq::Job class, call .perform_at"
286
+ end
287
+
288
+ def queue_as(q)
289
+ sidekiq_options("queue" => q.to_s)
290
+ end
291
+
292
+ def set(options)
293
+ Setter.new(self, options)
294
+ end
295
+
296
+ def perform_async(*args)
297
+ Setter.new(self, {}).perform_async(*args)
298
+ end
299
+
300
+ # Inline execution of job's perform method after passing through Sidekiq.client_middleware and Sidekiq.server_middleware
301
+ def perform_inline(*args)
302
+ Setter.new(self, {}).perform_inline(*args)
303
+ end
304
+ alias_method :perform_sync, :perform_inline
305
+
306
+ ##
307
+ # Push a large number of jobs to Redis, while limiting the batch of
308
+ # each job payload to 1,000. This method helps cut down on the number
309
+ # of round trips to Redis, which can increase the performance of enqueueing
310
+ # large numbers of jobs.
311
+ #
312
+ # +items+ must be an Array of Arrays.
313
+ #
314
+ # For finer-grained control, use `Sidekiq::Client.push_bulk` directly.
315
+ #
316
+ # Example (3 Redis round trips):
317
+ #
318
+ # SomeJob.perform_async(1)
319
+ # SomeJob.perform_async(2)
320
+ # SomeJob.perform_async(3)
321
+ #
322
+ # Would instead become (1 Redis round trip):
323
+ #
324
+ # SomeJob.perform_bulk([[1], [2], [3]])
325
+ #
326
+ def perform_bulk(*args, **kwargs)
327
+ Setter.new(self, {}).perform_bulk(*args, **kwargs)
328
+ end
329
+
330
+ # +interval+ must be a timestamp, numeric or something that acts
331
+ # numeric (like an activesupport time interval).
332
+ def perform_in(interval, *args)
333
+ int = interval.to_f
334
+ now = Time.now.to_f
335
+ ts = ((int < 1_000_000_000) ? now + int : int)
336
+
337
+ item = {"class" => self, "args" => args}
338
+
339
+ # Optimization to enqueue something now that is scheduled to go out now or in the past
340
+ item["at"] = ts if ts > now
341
+
342
+ client_push(item)
343
+ end
344
+ alias_method :perform_at, :perform_in
345
+
346
+ ##
347
+ # Allows customization for this type of Job.
348
+ # Legal options:
349
+ #
350
+ # queue - use a named queue for this Job, default 'default'
351
+ # retry - enable the RetryJobs middleware for this Job, *true* to use the default
352
+ # or *Integer* count
353
+ # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
354
+ # can be true, false or an integer number of lines to save, default *false*
355
+ # pool - use the given Redis connection pool to push this type of job to a given shard.
356
+ #
357
+ # In practice, any option is allowed. This is the main mechanism to configure the
358
+ # options for a specific job.
359
+ def sidekiq_options(opts = {})
360
+ super
361
+ end
362
+
363
+ def client_push(item) # :nodoc:
364
+ raise ArgumentError, "Job payloads should contain no Symbols: #{item}" if item.any? { |k, v| k.is_a?(::Symbol) }
365
+
366
+ # allow the user to dynamically re-target jobs to another shard using the "pool" attribute
367
+ # FooJob.set(pool: SOME_POOL).perform_async
368
+ old = Thread.current[:sidekiq_redis_pool]
369
+ pool = item.delete("pool")
370
+ Thread.current[:sidekiq_redis_pool] = pool if pool
371
+ begin
372
+ build_client.push(item)
373
+ ensure
374
+ Thread.current[:sidekiq_redis_pool] = old
375
+ end
376
+ end
377
+
378
+ def build_client # :nodoc:
379
+ pool = Thread.current[:sidekiq_redis_pool] || get_sidekiq_options["pool"] || Sidekiq.default_configuration.redis_pool
380
+ client_class = Thread.current[:sidekiq_client_class] || get_sidekiq_options["client_class"] || Sidekiq::Client
381
+ client_class.new(pool: pool)
382
+ end
383
+ end
384
+ end
385
+ end
@@ -1,25 +1,52 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Sidekiq
3
4
  class JobLogger
5
+ def initialize(config)
6
+ @config = config
7
+ @logger = @config.logger
8
+ @skip = !!@config[:skip_default_job_logging]
9
+ end
4
10
 
5
11
  def call(item, queue)
6
12
  start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
7
- logger.info("start")
13
+ @logger.info { "start" } unless @skip
14
+
8
15
  yield
9
- logger.info("done: #{elapsed(start)} sec")
16
+
17
+ Sidekiq::Context.add(:elapsed, elapsed(start))
18
+ @logger.info { "done" } unless @skip
10
19
  rescue Exception
11
- logger.info("fail: #{elapsed(start)} sec")
20
+ Sidekiq::Context.add(:elapsed, elapsed(start))
21
+ @logger.info { "fail" } unless @skip
12
22
  raise
13
23
  end
14
24
 
25
+ def prepare(job_hash, &block)
26
+ # If we're using a wrapper class, like ActiveJob, use the "wrapped"
27
+ # attribute to expose the underlying thing.
28
+ h = {
29
+ jid: job_hash["jid"],
30
+ class: job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
31
+ }
32
+ h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
33
+ h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
34
+
35
+ Thread.current[:sidekiq_context] = h
36
+ level = job_hash["log_level"]
37
+ if level
38
+ @logger.with_level(level, &block)
39
+ else
40
+ yield
41
+ end
42
+ ensure
43
+ Thread.current[:sidekiq_context] = nil
44
+ end
45
+
15
46
  private
16
47
 
17
48
  def elapsed(start)
18
49
  (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start).round(3)
19
50
  end
20
-
21
- def logger
22
- Sidekiq.logger
23
- end
24
51
  end
25
52
  end