sidekiq 7.3.9 → 8.0.8
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 +4 -4
- data/Changes.md +116 -0
- data/README.md +16 -13
- data/bin/sidekiqload +10 -10
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +104 -58
- data/lib/sidekiq/api.rb +124 -39
- data/lib/sidekiq/capsule.rb +6 -6
- data/lib/sidekiq/cli.rb +15 -19
- data/lib/sidekiq/client.rb +28 -17
- data/lib/sidekiq/component.rb +42 -3
- data/lib/sidekiq/config.rb +23 -20
- data/lib/sidekiq/embedded.rb +2 -1
- data/lib/sidekiq/iterable_job.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +44 -16
- data/lib/sidekiq/job.rb +2 -2
- data/lib/sidekiq/job_logger.rb +4 -4
- data/lib/sidekiq/job_retry.rb +33 -10
- data/lib/sidekiq/job_util.rb +5 -1
- data/lib/sidekiq/launcher.rb +2 -1
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +25 -69
- data/lib/sidekiq/manager.rb +0 -1
- data/lib/sidekiq/metrics/query.rb +71 -45
- data/lib/sidekiq/metrics/shared.rb +8 -5
- data/lib/sidekiq/metrics/tracking.rb +12 -7
- data/lib/sidekiq/middleware/current_attributes.rb +11 -19
- data/lib/sidekiq/paginator.rb +8 -1
- data/lib/sidekiq/processor.rb +21 -14
- data/lib/sidekiq/profiler.rb +72 -0
- data/lib/sidekiq/rails.rb +46 -67
- data/lib/sidekiq/redis_client_adapter.rb +0 -1
- data/lib/sidekiq/redis_connection.rb +14 -3
- data/lib/sidekiq/testing.rb +3 -3
- data/lib/sidekiq/transaction_aware_client.rb +13 -5
- data/lib/sidekiq/version.rb +2 -2
- data/lib/sidekiq/web/action.rb +146 -83
- data/lib/sidekiq/web/application.rb +353 -332
- data/lib/sidekiq/web/config.rb +120 -0
- data/lib/sidekiq/web/helpers.rb +57 -27
- data/lib/sidekiq/web/router.rb +60 -76
- data/lib/sidekiq/web.rb +51 -156
- data/lib/sidekiq.rb +6 -1
- data/sidekiq.gemspec +6 -6
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +26 -26
- data/web/assets/javascripts/base-charts.js +30 -16
- data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/web/assets/javascripts/dashboard.js +1 -1
- data/web/assets/javascripts/metrics.js +16 -34
- data/web/assets/stylesheets/style.css +759 -0
- data/web/locales/ar.yml +1 -0
- data/web/locales/cs.yml +1 -0
- data/web/locales/da.yml +1 -0
- data/web/locales/de.yml +1 -0
- data/web/locales/el.yml +1 -0
- data/web/locales/en.yml +6 -0
- data/web/locales/es.yml +24 -2
- data/web/locales/fa.yml +1 -0
- data/web/locales/fr.yml +1 -0
- data/web/locales/gd.yml +1 -0
- data/web/locales/he.yml +1 -0
- data/web/locales/hi.yml +1 -0
- data/web/locales/it.yml +8 -0
- data/web/locales/ja.yml +1 -0
- data/web/locales/ko.yml +1 -0
- data/web/locales/lt.yml +1 -0
- data/web/locales/nb.yml +1 -0
- data/web/locales/nl.yml +1 -0
- data/web/locales/pl.yml +1 -0
- data/web/locales/{pt-br.yml → pt-BR.yml} +2 -1
- data/web/locales/pt.yml +1 -0
- data/web/locales/ru.yml +1 -0
- data/web/locales/sv.yml +1 -0
- data/web/locales/ta.yml +1 -0
- data/web/locales/tr.yml +1 -0
- data/web/locales/uk.yml +6 -5
- data/web/locales/ur.yml +1 -0
- data/web/locales/vi.yml +1 -0
- data/web/locales/{zh-cn.yml → zh-CN.yml} +85 -73
- data/web/locales/{zh-tw.yml → zh-TW.yml} +2 -1
- data/web/views/_footer.erb +31 -33
- data/web/views/_job_info.erb +91 -89
- data/web/views/_metrics_period_select.erb +13 -10
- data/web/views/_nav.erb +14 -21
- data/web/views/_paging.erb +23 -21
- data/web/views/_poll_link.erb +2 -2
- data/web/views/_summary.erb +16 -16
- data/web/views/busy.erb +124 -122
- data/web/views/dashboard.erb +62 -66
- data/web/views/dead.erb +31 -27
- data/web/views/filtering.erb +3 -3
- data/web/views/layout.erb +13 -29
- data/web/views/metrics.erb +75 -81
- data/web/views/metrics_for_job.erb +45 -46
- data/web/views/morgue.erb +61 -70
- data/web/views/profiles.erb +43 -0
- data/web/views/queue.erb +54 -52
- data/web/views/queues.erb +43 -41
- data/web/views/retries.erb +66 -75
- data/web/views/retry.erb +32 -27
- data/web/views/scheduled.erb +59 -55
- data/web/views/scheduled_job_info.erb +1 -1
- metadata +26 -25
- data/web/assets/stylesheets/application-dark.css +0 -147
- data/web/assets/stylesheets/application-rtl.css +0 -163
- data/web/assets/stylesheets/application.css +0 -759
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/views/_status.erb +0 -4
data/lib/sidekiq/job/iterable.rb
CHANGED
|
@@ -32,8 +32,14 @@ module Sidekiq
|
|
|
32
32
|
@_runtime = 0
|
|
33
33
|
@_args = nil
|
|
34
34
|
@_cancelled = nil
|
|
35
|
+
@current_object = nil
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
# Access to the current object while iterating.
|
|
39
|
+
# This value is not reset so the latest element is
|
|
40
|
+
# explicitly available to cleanup/complete callbacks.
|
|
41
|
+
attr_reader :current_object
|
|
42
|
+
|
|
37
43
|
def arguments
|
|
38
44
|
@_args
|
|
39
45
|
end
|
|
@@ -54,9 +60,7 @@ module Sidekiq
|
|
|
54
60
|
c.pipelined do |p|
|
|
55
61
|
p.hsetnx(key, "cancelled", Time.now.to_i)
|
|
56
62
|
p.hget(key, "cancelled")
|
|
57
|
-
|
|
58
|
-
# p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
|
59
|
-
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL)
|
|
63
|
+
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
|
60
64
|
end
|
|
61
65
|
end
|
|
62
66
|
@_cancelled = result.to_i
|
|
@@ -66,6 +70,10 @@ module Sidekiq
|
|
|
66
70
|
@_cancelled
|
|
67
71
|
end
|
|
68
72
|
|
|
73
|
+
def cursor
|
|
74
|
+
@_cursor.freeze
|
|
75
|
+
end
|
|
76
|
+
|
|
69
77
|
# A hook to override that will be called when the job starts iterating.
|
|
70
78
|
#
|
|
71
79
|
# It is called only once, for the first time.
|
|
@@ -93,6 +101,11 @@ module Sidekiq
|
|
|
93
101
|
def on_stop
|
|
94
102
|
end
|
|
95
103
|
|
|
104
|
+
# A hook to override that will be called when the job is cancelled.
|
|
105
|
+
#
|
|
106
|
+
def on_cancel
|
|
107
|
+
end
|
|
108
|
+
|
|
96
109
|
# A hook to override that will be called when the job finished iterating.
|
|
97
110
|
#
|
|
98
111
|
def on_complete
|
|
@@ -130,7 +143,7 @@ module Sidekiq
|
|
|
130
143
|
fetch_previous_iteration_state
|
|
131
144
|
|
|
132
145
|
@_executions += 1
|
|
133
|
-
@_start_time =
|
|
146
|
+
@_start_time = mono_now
|
|
134
147
|
|
|
135
148
|
enumerator = build_enumerator(*args, cursor: @_cursor)
|
|
136
149
|
unless enumerator
|
|
@@ -184,34 +197,40 @@ module Sidekiq
|
|
|
184
197
|
|
|
185
198
|
def iterate_with_enumerator(enumerator, arguments)
|
|
186
199
|
if is_cancelled?
|
|
200
|
+
on_cancel
|
|
187
201
|
logger.info { "Job cancelled" }
|
|
188
202
|
return true
|
|
189
203
|
end
|
|
190
204
|
|
|
191
205
|
time_limit = Sidekiq.default_configuration[:timeout]
|
|
192
206
|
found_record = false
|
|
193
|
-
state_flushed_at =
|
|
207
|
+
state_flushed_at = mono_now
|
|
194
208
|
|
|
195
209
|
enumerator.each do |object, cursor|
|
|
196
210
|
found_record = true
|
|
197
211
|
@_cursor = cursor
|
|
212
|
+
@current_object = object
|
|
198
213
|
|
|
199
|
-
|
|
200
|
-
if
|
|
214
|
+
interrupt_job = interrupted? || should_interrupt?
|
|
215
|
+
if mono_now - state_flushed_at >= STATE_FLUSH_INTERVAL || interrupt_job
|
|
201
216
|
_, _, cancelled = flush_state
|
|
202
|
-
state_flushed_at =
|
|
217
|
+
state_flushed_at = mono_now
|
|
203
218
|
if cancelled
|
|
204
219
|
@_cancelled = true
|
|
220
|
+
on_cancel
|
|
205
221
|
logger.info { "Job cancelled" }
|
|
206
222
|
return true
|
|
207
223
|
end
|
|
208
224
|
end
|
|
209
225
|
|
|
210
|
-
return false if
|
|
226
|
+
return false if interrupt_job
|
|
211
227
|
|
|
212
|
-
verify_iteration_time(time_limit
|
|
228
|
+
verify_iteration_time(time_limit) do
|
|
213
229
|
around_iteration do
|
|
214
230
|
each_iteration(object, *arguments)
|
|
231
|
+
rescue Exception
|
|
232
|
+
flush_state
|
|
233
|
+
raise
|
|
215
234
|
end
|
|
216
235
|
end
|
|
217
236
|
end
|
|
@@ -219,16 +238,16 @@ module Sidekiq
|
|
|
219
238
|
logger.debug("Enumerator found nothing to iterate!") unless found_record
|
|
220
239
|
true
|
|
221
240
|
ensure
|
|
222
|
-
@_runtime += (
|
|
241
|
+
@_runtime += (mono_now - @_start_time)
|
|
223
242
|
end
|
|
224
243
|
|
|
225
|
-
def verify_iteration_time(time_limit
|
|
226
|
-
start =
|
|
244
|
+
def verify_iteration_time(time_limit)
|
|
245
|
+
start = mono_now
|
|
227
246
|
yield
|
|
228
|
-
finish =
|
|
247
|
+
finish = mono_now
|
|
229
248
|
total = finish - start
|
|
230
249
|
if total > time_limit
|
|
231
|
-
logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d)
|
|
250
|
+
logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d). This can lead to job processing problems during deploys" % [total, time_limit] }
|
|
232
251
|
end
|
|
233
252
|
end
|
|
234
253
|
|
|
@@ -254,6 +273,11 @@ module Sidekiq
|
|
|
254
273
|
end
|
|
255
274
|
end
|
|
256
275
|
|
|
276
|
+
def should_interrupt?
|
|
277
|
+
max_iteration_runtime = Sidekiq.default_configuration[:max_iteration_runtime]
|
|
278
|
+
max_iteration_runtime && (mono_now - @_start_time > max_iteration_runtime)
|
|
279
|
+
end
|
|
280
|
+
|
|
257
281
|
def flush_state
|
|
258
282
|
key = iteration_key
|
|
259
283
|
state = {
|
|
@@ -265,7 +289,7 @@ module Sidekiq
|
|
|
265
289
|
Sidekiq.redis do |conn|
|
|
266
290
|
conn.multi do |pipe|
|
|
267
291
|
pipe.hset(key, state)
|
|
268
|
-
pipe.expire(key, STATE_TTL)
|
|
292
|
+
pipe.expire(key, STATE_TTL, "nx")
|
|
269
293
|
pipe.hget(key, "cancelled")
|
|
270
294
|
end
|
|
271
295
|
end
|
|
@@ -289,6 +313,10 @@ module Sidekiq
|
|
|
289
313
|
raise "Unexpected thrown value: #{completed.inspect}"
|
|
290
314
|
end
|
|
291
315
|
end
|
|
316
|
+
|
|
317
|
+
def mono_now
|
|
318
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
319
|
+
end
|
|
292
320
|
end
|
|
293
321
|
end
|
|
294
322
|
end
|
data/lib/sidekiq/job.rb
CHANGED
|
@@ -248,9 +248,9 @@ module Sidekiq
|
|
|
248
248
|
end
|
|
249
249
|
alias_method :perform_sync, :perform_inline
|
|
250
250
|
|
|
251
|
-
def perform_bulk(args,
|
|
251
|
+
def perform_bulk(args, **options)
|
|
252
252
|
client = @klass.build_client
|
|
253
|
-
client.push_bulk(@opts.merge("class" => @klass, "args" => args,
|
|
253
|
+
client.push_bulk(@opts.merge({"class" => @klass, "args" => args}, options))
|
|
254
254
|
end
|
|
255
255
|
|
|
256
256
|
# +interval+ must be a timestamp, numeric or something that acts
|
data/lib/sidekiq/job_logger.rb
CHANGED
|
@@ -26,16 +26,16 @@ module Sidekiq
|
|
|
26
26
|
# If we're using a wrapper class, like ActiveJob, use the "wrapped"
|
|
27
27
|
# attribute to expose the underlying thing.
|
|
28
28
|
h = {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
jid: job_hash["jid"],
|
|
30
|
+
class: job_hash["wrapped"] || job_hash["class"]
|
|
31
31
|
}
|
|
32
32
|
h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
|
|
33
33
|
h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
|
|
34
34
|
|
|
35
35
|
Thread.current[:sidekiq_context] = h
|
|
36
36
|
level = job_hash["log_level"]
|
|
37
|
-
if level
|
|
38
|
-
@logger.
|
|
37
|
+
if level
|
|
38
|
+
@logger.with_level(level, &block)
|
|
39
39
|
else
|
|
40
40
|
yield
|
|
41
41
|
end
|
data/lib/sidekiq/job_retry.rb
CHANGED
|
@@ -139,6 +139,10 @@ module Sidekiq
|
|
|
139
139
|
|
|
140
140
|
private
|
|
141
141
|
|
|
142
|
+
def now_ms
|
|
143
|
+
::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
|
144
|
+
end
|
|
145
|
+
|
|
142
146
|
# Note that +jobinst+ can be nil here if an error is raised before we can
|
|
143
147
|
# instantiate the job instance. All access must be guarded and
|
|
144
148
|
# best effort.
|
|
@@ -149,17 +153,17 @@ module Sidekiq
|
|
|
149
153
|
|
|
150
154
|
m = exception_message(exception)
|
|
151
155
|
if m.respond_to?(:scrub!)
|
|
152
|
-
m.force_encoding(
|
|
156
|
+
m.force_encoding(Encoding::UTF_8)
|
|
153
157
|
m.scrub!
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
msg["error_message"] = m
|
|
157
161
|
msg["error_class"] = exception.class.name
|
|
158
162
|
count = if msg["retry_count"]
|
|
159
|
-
msg["retried_at"] =
|
|
163
|
+
msg["retried_at"] = now_ms
|
|
160
164
|
msg["retry_count"] += 1
|
|
161
165
|
else
|
|
162
|
-
msg["failed_at"] =
|
|
166
|
+
msg["failed_at"] = now_ms
|
|
163
167
|
msg["retry_count"] = 0
|
|
164
168
|
end
|
|
165
169
|
|
|
@@ -177,19 +181,21 @@ module Sidekiq
|
|
|
177
181
|
return retries_exhausted(jobinst, msg, exception) if count >= max_retry_attempts
|
|
178
182
|
|
|
179
183
|
rf = msg["retry_for"]
|
|
180
|
-
return retries_exhausted(jobinst, msg, exception) if rf && ((msg["failed_at"] + rf) < Time.now
|
|
184
|
+
return retries_exhausted(jobinst, msg, exception) if rf && (time_for(msg["failed_at"]) + rf) < Time.now
|
|
181
185
|
|
|
182
186
|
strategy, delay = delay_for(jobinst, count, exception, msg)
|
|
183
187
|
case strategy
|
|
184
188
|
when :discard
|
|
185
|
-
|
|
189
|
+
msg["discarded_at"] = now_ms
|
|
190
|
+
|
|
191
|
+
return run_death_handlers(msg, exception)
|
|
186
192
|
when :kill
|
|
187
193
|
return retries_exhausted(jobinst, msg, exception)
|
|
188
194
|
end
|
|
189
195
|
|
|
190
196
|
# Logging here can break retries if the logging device raises ENOSPC #3979
|
|
191
197
|
# logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
|
192
|
-
jitter = rand(10
|
|
198
|
+
jitter = rand(10 * (count + 1))
|
|
193
199
|
retry_at = Time.now.to_f + delay + jitter
|
|
194
200
|
payload = Sidekiq.dump_json(msg)
|
|
195
201
|
redis do |conn|
|
|
@@ -197,6 +203,14 @@ module Sidekiq
|
|
|
197
203
|
end
|
|
198
204
|
end
|
|
199
205
|
|
|
206
|
+
def time_for(item)
|
|
207
|
+
if item.is_a?(Float)
|
|
208
|
+
Time.at(item)
|
|
209
|
+
else
|
|
210
|
+
Time.at(item / 1000, item % 1000)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
200
214
|
# returns (strategy, seconds)
|
|
201
215
|
def delay_for(jobinst, count, exception, msg)
|
|
202
216
|
rv = begin
|
|
@@ -243,13 +257,22 @@ module Sidekiq
|
|
|
243
257
|
handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
|
|
244
258
|
end
|
|
245
259
|
|
|
246
|
-
|
|
247
|
-
|
|
260
|
+
discarded = msg["dead"] == false || rv == :discard
|
|
261
|
+
|
|
262
|
+
if discarded
|
|
263
|
+
msg["discarded_at"] = now_ms
|
|
264
|
+
else
|
|
265
|
+
send_to_morgue(msg)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
run_death_handlers(msg, exception)
|
|
269
|
+
end
|
|
248
270
|
|
|
271
|
+
def run_death_handlers(job, exception)
|
|
249
272
|
@capsule.config.death_handlers.each do |handler|
|
|
250
|
-
handler.call(
|
|
273
|
+
handler.call(job, exception)
|
|
251
274
|
rescue => e
|
|
252
|
-
handle_exception(e, {context: "Error calling death handler", job:
|
|
275
|
+
handle_exception(e, {context: "Error calling death handler", job: job})
|
|
253
276
|
end
|
|
254
277
|
end
|
|
255
278
|
|
data/lib/sidekiq/job_util.rb
CHANGED
|
@@ -58,10 +58,14 @@ module Sidekiq
|
|
|
58
58
|
item["class"] = item["class"].to_s
|
|
59
59
|
item["queue"] = item["queue"].to_s
|
|
60
60
|
item["retry_for"] = item["retry_for"].to_i if item["retry_for"]
|
|
61
|
-
item["created_at"] ||=
|
|
61
|
+
item["created_at"] ||= now_in_millis
|
|
62
62
|
item
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
+
def now_in_millis
|
|
66
|
+
::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
|
67
|
+
end
|
|
68
|
+
|
|
65
69
|
def normalized_hash(item_class)
|
|
66
70
|
if item_class.is_a?(Class)
|
|
67
71
|
raise(ArgumentError, "Message must include a Sidekiq::Job class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?(:get_sidekiq_options)
|
data/lib/sidekiq/launcher.rb
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
require "sidekiq/component"
|
|
3
|
+
|
|
4
|
+
class Loader
|
|
5
|
+
include Sidekiq::Component
|
|
6
|
+
|
|
7
|
+
def initialize(cfg = Sidekiq.default_configuration)
|
|
8
|
+
@config = cfg
|
|
9
|
+
@load_hooks = Hash.new { |h, k| h[k] = [] }
|
|
10
|
+
@loaded = Set.new
|
|
11
|
+
@lock = Mutex.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Declares a block that will be executed when a Sidekiq component is fully
|
|
15
|
+
# loaded. If the component has already loaded, the block is executed
|
|
16
|
+
# immediately.
|
|
17
|
+
#
|
|
18
|
+
# Sidekiq.loader.on_load(:api) do
|
|
19
|
+
# # extend the sidekiq API
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
def on_load(name, &block)
|
|
23
|
+
# we don't want to hold the lock while calling the block
|
|
24
|
+
to_run = nil
|
|
25
|
+
|
|
26
|
+
@lock.synchronize do
|
|
27
|
+
if @loaded.include?(name)
|
|
28
|
+
to_run = block
|
|
29
|
+
else
|
|
30
|
+
@load_hooks[name] << block
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
to_run&.call
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Executes all blocks registered to +name+ via on_load.
|
|
39
|
+
#
|
|
40
|
+
# Sidekiq.loader.run_load_hooks(:api)
|
|
41
|
+
#
|
|
42
|
+
# In the case of the above example, it will execute all hooks registered for +:api+.
|
|
43
|
+
#
|
|
44
|
+
def run_load_hooks(name)
|
|
45
|
+
hks = @lock.synchronize do
|
|
46
|
+
@loaded << name
|
|
47
|
+
@load_hooks.delete(name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
hks&.each do |blk|
|
|
51
|
+
blk.call
|
|
52
|
+
rescue => ex
|
|
53
|
+
handle_exception(ex, hook: name)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/sidekiq/logger.rb
CHANGED
|
@@ -22,92 +22,48 @@ module Sidekiq
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
module LoggingUtils
|
|
26
|
-
LEVELS = {
|
|
27
|
-
"debug" => 0,
|
|
28
|
-
"info" => 1,
|
|
29
|
-
"warn" => 2,
|
|
30
|
-
"error" => 3,
|
|
31
|
-
"fatal" => 4
|
|
32
|
-
}
|
|
33
|
-
LEVELS.default_proc = proc do |_, level|
|
|
34
|
-
puts("Invalid log level: #{level.inspect}")
|
|
35
|
-
nil
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
LEVELS.each do |level, numeric_level|
|
|
39
|
-
define_method(:"#{level}?") do
|
|
40
|
-
local_level.nil? ? super() : local_level <= numeric_level
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def local_level
|
|
45
|
-
Thread.current[:sidekiq_log_level]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def local_level=(level)
|
|
49
|
-
case level
|
|
50
|
-
when Integer
|
|
51
|
-
Thread.current[:sidekiq_log_level] = level
|
|
52
|
-
when Symbol, String
|
|
53
|
-
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
|
|
54
|
-
when nil
|
|
55
|
-
Thread.current[:sidekiq_log_level] = nil
|
|
56
|
-
else
|
|
57
|
-
raise ArgumentError, "Invalid log level: #{level.inspect}"
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def level
|
|
62
|
-
local_level || super
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Change the thread-local level for the duration of the given block.
|
|
66
|
-
def log_at(level)
|
|
67
|
-
old_local_level = local_level
|
|
68
|
-
self.local_level = level
|
|
69
|
-
yield
|
|
70
|
-
ensure
|
|
71
|
-
self.local_level = old_local_level
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
25
|
class Logger < ::Logger
|
|
76
|
-
include LoggingUtils
|
|
77
|
-
|
|
78
26
|
module Formatters
|
|
79
27
|
class Base < ::Logger::Formatter
|
|
28
|
+
COLORS = {
|
|
29
|
+
"DEBUG" => "\e[1;32mDEBUG\e[0m", # green
|
|
30
|
+
"INFO" => "\e[1;34mINFO \e[0m", # blue
|
|
31
|
+
"WARN" => "\e[1;33mWARN \e[0m", # yellow
|
|
32
|
+
"ERROR" => "\e[1;31mERROR\e[0m", # red
|
|
33
|
+
"FATAL" => "\e[1;35mFATAL\e[0m" # pink
|
|
34
|
+
}
|
|
35
|
+
|
|
80
36
|
def tid
|
|
81
37
|
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
|
82
38
|
end
|
|
83
39
|
|
|
84
|
-
def
|
|
85
|
-
|
|
40
|
+
def format_context(ctxt = Sidekiq::Context.current)
|
|
41
|
+
(ctxt.size == 0) ? "" : " #{ctxt.map { |k, v|
|
|
42
|
+
case v
|
|
43
|
+
when Array
|
|
44
|
+
"#{k}=#{v.join(",")}"
|
|
45
|
+
else
|
|
46
|
+
"#{k}=#{v}"
|
|
47
|
+
end
|
|
48
|
+
}.join(" ")}"
|
|
86
49
|
end
|
|
50
|
+
end
|
|
87
51
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
case v
|
|
92
|
-
when Array
|
|
93
|
-
"#{k}=#{v.join(",")}"
|
|
94
|
-
else
|
|
95
|
-
"#{k}=#{v}"
|
|
96
|
-
end
|
|
97
|
-
}.join(" ")
|
|
98
|
-
end
|
|
52
|
+
class Pretty < Base
|
|
53
|
+
def call(severity, time, program_name, message)
|
|
54
|
+
"#{COLORS[severity]} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
99
55
|
end
|
|
100
56
|
end
|
|
101
57
|
|
|
102
|
-
class
|
|
58
|
+
class Plain < Base
|
|
103
59
|
def call(severity, time, program_name, message)
|
|
104
|
-
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}
|
|
60
|
+
"#{severity} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
105
61
|
end
|
|
106
62
|
end
|
|
107
63
|
|
|
108
64
|
class WithoutTimestamp < Pretty
|
|
109
65
|
def call(severity, time, program_name, message)
|
|
110
|
-
"pid=#{::Process.pid} tid=#{tid}#{format_context}
|
|
66
|
+
"#{COLORS[severity]} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
|
111
67
|
end
|
|
112
68
|
end
|
|
113
69
|
|
|
@@ -120,7 +76,7 @@ module Sidekiq
|
|
|
120
76
|
lvl: severity,
|
|
121
77
|
msg: message
|
|
122
78
|
}
|
|
123
|
-
c =
|
|
79
|
+
c = Sidekiq::Context.current
|
|
124
80
|
hash["ctx"] = c unless c.empty?
|
|
125
81
|
|
|
126
82
|
Sidekiq.dump_json(hash) << "\n"
|