sidekiq 7.3.9 → 8.0.1
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 +44 -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 +5 -5
- data/lib/sidekiq/api.rb +120 -36
- data/lib/sidekiq/capsule.rb +6 -6
- data/lib/sidekiq/cli.rb +15 -19
- data/lib/sidekiq/client.rb +13 -16
- data/lib/sidekiq/component.rb +40 -2
- data/lib/sidekiq/config.rb +18 -15
- data/lib/sidekiq/embedded.rb +1 -0
- data/lib/sidekiq/iterable_job.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +13 -4
- data/lib/sidekiq/job_logger.rb +4 -4
- data/lib/sidekiq/job_retry.rb +17 -5
- data/lib/sidekiq/job_util.rb +5 -1
- data/lib/sidekiq/launcher.rb +1 -1
- data/lib/sidekiq/logger.rb +19 -70
- 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 +9 -7
- data/lib/sidekiq/middleware/current_attributes.rb +5 -17
- data/lib/sidekiq/paginator.rb +8 -1
- data/lib/sidekiq/processor.rb +21 -14
- data/lib/sidekiq/profiler.rb +59 -0
- data/lib/sidekiq/redis_client_adapter.rb +0 -1
- data/lib/sidekiq/redis_connection.rb +14 -3
- data/lib/sidekiq/testing.rb +2 -2
- data/lib/sidekiq/version.rb +2 -2
- data/lib/sidekiq/web/action.rb +104 -84
- data/lib/sidekiq/web/application.rb +347 -332
- data/lib/sidekiq/web/config.rb +117 -0
- data/lib/sidekiq/web/helpers.rb +41 -16
- data/lib/sidekiq/web/router.rb +60 -76
- data/lib/sidekiq/web.rb +50 -156
- data/lib/sidekiq.rb +1 -1
- data/sidekiq.gemspec +6 -6
- data/web/assets/javascripts/application.js +6 -13
- 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/metrics.js +16 -34
- data/web/assets/stylesheets/style.css +750 -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 +1 -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 +1 -0
- 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 +6 -22
- 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 +58 -54
- data/web/views/scheduled_job_info.erb +1 -1
- metadata +24 -24
- 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/client.rb
CHANGED
@@ -67,9 +67,7 @@ module Sidekiq
|
|
67
67
|
c.pipelined do |p|
|
68
68
|
p.hsetnx(key, "cancelled", Time.now.to_i)
|
69
69
|
p.hget(key, "cancelled")
|
70
|
-
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL)
|
71
|
-
# TODO When Redis 7.2 is required
|
72
|
-
# p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
70
|
+
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
73
71
|
end
|
74
72
|
end
|
75
73
|
result.to_i
|
@@ -266,22 +264,21 @@ module Sidekiq
|
|
266
264
|
if payloads.first.key?("at")
|
267
265
|
conn.zadd("schedule", payloads.flat_map { |hash|
|
268
266
|
at = hash["at"].to_s
|
269
|
-
# ActiveJob sets
|
270
|
-
hash.
|
271
|
-
# TODO: Use hash.except("at") when support for Ruby 2.7 is dropped
|
272
|
-
hash = hash.dup
|
273
|
-
hash.delete("at")
|
267
|
+
# ActiveJob sets enqueued_at but the job has not been enqueued yet
|
268
|
+
hash = hash.except("enqueued_at", "at")
|
274
269
|
[at, Sidekiq.dump_json(hash)]
|
275
270
|
})
|
276
271
|
else
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
272
|
+
now = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) # milliseconds since the epoch
|
273
|
+
grouped_queues = payloads.group_by { |job| job["queue"] }
|
274
|
+
conn.sadd("queues", grouped_queues.keys)
|
275
|
+
grouped_queues.each do |queue, grouped_payloads|
|
276
|
+
to_push = grouped_payloads.map { |entry|
|
277
|
+
entry["enqueued_at"] = now
|
278
|
+
Sidekiq.dump_json(entry)
|
279
|
+
}
|
280
|
+
conn.lpush("queue:#{queue}", to_push)
|
281
|
+
end
|
285
282
|
end
|
286
283
|
end
|
287
284
|
end
|
data/lib/sidekiq/component.rb
CHANGED
@@ -1,11 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sidekiq
|
4
|
+
# Ruby's default thread priority is 0, which uses 100ms time slices.
|
5
|
+
# This can lead to some surprising thread starvation; if using a lot of
|
6
|
+
# CPU-heavy concurrency, it may take several seconds before a Thread gets
|
7
|
+
# on the CPU.
|
8
|
+
#
|
9
|
+
# Negative priorities lower the timeslice by half, so -1 = 50ms, -2 = 25ms, etc.
|
10
|
+
# With more frequent timeslices, we reduce the risk of unintentional timeouts
|
11
|
+
# and starvation.
|
12
|
+
#
|
13
|
+
# Customize like so:
|
14
|
+
#
|
15
|
+
# Sidekiq.configure_server do |cfg|
|
16
|
+
# cfg.thread_priority = 0
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
DEFAULT_THREAD_PRIORITY = -1
|
20
|
+
|
4
21
|
##
|
5
22
|
# Sidekiq::Component assumes a config instance is available at @config
|
6
23
|
module Component # :nodoc:
|
7
24
|
attr_reader :config
|
8
25
|
|
26
|
+
# This is epoch milliseconds, appropriate for persistence
|
27
|
+
def real_ms
|
28
|
+
::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond)
|
29
|
+
end
|
30
|
+
|
31
|
+
# used for time difference and relative comparisons, not persistence.
|
32
|
+
def mono_ms
|
33
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
|
34
|
+
end
|
35
|
+
|
9
36
|
def watchdog(last_words)
|
10
37
|
yield
|
11
38
|
rescue Exception => ex
|
@@ -13,11 +40,11 @@ module Sidekiq
|
|
13
40
|
raise ex
|
14
41
|
end
|
15
42
|
|
16
|
-
def safe_thread(name, &block)
|
43
|
+
def safe_thread(name, priority: nil, &block)
|
17
44
|
Thread.new do
|
18
45
|
Thread.current.name = "sidekiq.#{name}"
|
19
46
|
watchdog(name, &block)
|
20
|
-
end
|
47
|
+
end.tap { |t| t.priority = (priority || config.thread_priority || DEFAULT_THREAD_PRIORITY) }
|
21
48
|
end
|
22
49
|
|
23
50
|
def logger
|
@@ -86,5 +113,16 @@ module Sidekiq
|
|
86
113
|
end.join(", ")
|
87
114
|
}>"
|
88
115
|
end
|
116
|
+
|
117
|
+
def default_tag(dir = Dir.pwd)
|
118
|
+
name = File.basename(dir)
|
119
|
+
prevdir = File.dirname(dir) # Capistrano release directory?
|
120
|
+
if name.to_i != 0 && prevdir
|
121
|
+
if File.basename(prevdir) == "releases"
|
122
|
+
return File.basename(File.dirname(prevdir))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
name
|
126
|
+
end
|
89
127
|
end
|
90
128
|
end
|
data/lib/sidekiq/config.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "forwardable"
|
4
|
-
|
5
|
-
require "set"
|
6
4
|
require "sidekiq/redis_connection"
|
7
5
|
|
8
6
|
module Sidekiq
|
@@ -41,12 +39,22 @@ module Sidekiq
|
|
41
39
|
}
|
42
40
|
|
43
41
|
ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration) {
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
Sidekiq::Context.with(ctx) do
|
43
|
+
dev = cfg[:environment] == "development"
|
44
|
+
fancy = dev && $stdout.tty? # 🎩
|
45
|
+
# Weird logic here but we want to show the backtrace in local
|
46
|
+
# development or if verbose logging is enabled.
|
47
|
+
#
|
48
|
+
# `full_message` contains the error class, message and backtrace
|
49
|
+
# `detailed_message` contains the error class and message
|
50
|
+
#
|
51
|
+
# Absolutely terrible API names. Not useful at all to have two
|
52
|
+
# methods with similar but obscure names.
|
53
|
+
if dev || cfg.logger.debug?
|
54
|
+
cfg.logger.info { ex.full_message(highlight: fancy) }
|
55
|
+
else
|
56
|
+
cfg.logger.info { ex.detailed_message(highlight: fancy) }
|
57
|
+
end
|
50
58
|
end
|
51
59
|
}
|
52
60
|
|
@@ -60,6 +68,7 @@ module Sidekiq
|
|
60
68
|
|
61
69
|
def_delegators :@options, :[], :[]=, :fetch, :key?, :has_key?, :merge!, :dig
|
62
70
|
attr_reader :capsules
|
71
|
+
attr_accessor :thread_priority
|
63
72
|
|
64
73
|
def inspect
|
65
74
|
"#<#{self.class.name} @options=#{
|
@@ -293,13 +302,7 @@ module Sidekiq
|
|
293
302
|
p ["!!!!!", ex]
|
294
303
|
end
|
295
304
|
@options[:error_handlers].each do |handler|
|
296
|
-
|
297
|
-
# TODO Remove in 8.0
|
298
|
-
logger.info { "DEPRECATION: Sidekiq exception handlers now take three arguments, see #{handler}" }
|
299
|
-
handler.call(ex, {_config: self}.merge(ctx))
|
300
|
-
else
|
301
|
-
handler.call(ex, ctx, self)
|
302
|
-
end
|
305
|
+
handler.call(ex, ctx, self)
|
303
306
|
rescue Exception => e
|
304
307
|
l = logger
|
305
308
|
l.error "!!! ERROR HANDLER THREW AN ERROR !!!"
|
data/lib/sidekiq/embedded.rb
CHANGED
@@ -34,6 +34,7 @@ module Sidekiq
|
|
34
34
|
private
|
35
35
|
|
36
36
|
def housekeeping
|
37
|
+
@config[:tag] ||= default_tag
|
37
38
|
logger.info "Running in #{RUBY_DESCRIPTION}"
|
38
39
|
logger.info Sidekiq::LICENSE
|
39
40
|
logger.info "Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org" unless defined?(::Sidekiq::Pro)
|
data/lib/sidekiq/iterable_job.rb
CHANGED
data/lib/sidekiq/job/iterable.rb
CHANGED
@@ -54,9 +54,7 @@ module Sidekiq
|
|
54
54
|
c.pipelined do |p|
|
55
55
|
p.hsetnx(key, "cancelled", Time.now.to_i)
|
56
56
|
p.hget(key, "cancelled")
|
57
|
-
|
58
|
-
# p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
59
|
-
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL)
|
57
|
+
p.expire(key, Sidekiq::Job::Iterable::STATE_TTL, "nx")
|
60
58
|
end
|
61
59
|
end
|
62
60
|
@_cancelled = result.to_i
|
@@ -66,6 +64,10 @@ module Sidekiq
|
|
66
64
|
@_cancelled
|
67
65
|
end
|
68
66
|
|
67
|
+
def cursor
|
68
|
+
@_cursor.freeze
|
69
|
+
end
|
70
|
+
|
69
71
|
# A hook to override that will be called when the job starts iterating.
|
70
72
|
#
|
71
73
|
# It is called only once, for the first time.
|
@@ -93,6 +95,11 @@ module Sidekiq
|
|
93
95
|
def on_stop
|
94
96
|
end
|
95
97
|
|
98
|
+
# A hook to override that will be called when the job is cancelled.
|
99
|
+
#
|
100
|
+
def on_cancel
|
101
|
+
end
|
102
|
+
|
96
103
|
# A hook to override that will be called when the job finished iterating.
|
97
104
|
#
|
98
105
|
def on_complete
|
@@ -184,6 +191,7 @@ module Sidekiq
|
|
184
191
|
|
185
192
|
def iterate_with_enumerator(enumerator, arguments)
|
186
193
|
if is_cancelled?
|
194
|
+
on_cancel
|
187
195
|
logger.info { "Job cancelled" }
|
188
196
|
return true
|
189
197
|
end
|
@@ -202,6 +210,7 @@ module Sidekiq
|
|
202
210
|
state_flushed_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
203
211
|
if cancelled
|
204
212
|
@_cancelled = true
|
213
|
+
on_cancel
|
205
214
|
logger.info { "Job cancelled" }
|
206
215
|
return true
|
207
216
|
end
|
@@ -265,7 +274,7 @@ module Sidekiq
|
|
265
274
|
Sidekiq.redis do |conn|
|
266
275
|
conn.multi do |pipe|
|
267
276
|
pipe.hset(key, state)
|
268
|
-
pipe.expire(key, STATE_TTL)
|
277
|
+
pipe.expire(key, STATE_TTL, "nx")
|
269
278
|
pipe.hget(key, "cancelled")
|
270
279
|
end
|
271
280
|
end
|
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["display_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,7 +181,7 @@ 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
|
@@ -189,7 +193,7 @@ module Sidekiq
|
|
189
193
|
|
190
194
|
# Logging here can break retries if the logging device raises ENOSPC #3979
|
191
195
|
# logger.debug { "Failure! Retry #{count} in #{delay} seconds" }
|
192
|
-
jitter = rand(10
|
196
|
+
jitter = rand(10 * (count + 1))
|
193
197
|
retry_at = Time.now.to_f + delay + jitter
|
194
198
|
payload = Sidekiq.dump_json(msg)
|
195
199
|
redis do |conn|
|
@@ -197,6 +201,14 @@ module Sidekiq
|
|
197
201
|
end
|
198
202
|
end
|
199
203
|
|
204
|
+
def time_for(item)
|
205
|
+
if item.is_a?(Float)
|
206
|
+
Time.at(item)
|
207
|
+
else
|
208
|
+
Time.at(item / 1000, item % 1000)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
200
212
|
# returns (strategy, seconds)
|
201
213
|
def delay_for(jobinst, count, exception, msg)
|
202
214
|
rv = begin
|
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
data/lib/sidekiq/logger.rb
CHANGED
@@ -22,92 +22,41 @@ 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
|
27
|
+
COLORS = {
|
28
|
+
"DEBUG" => "\e[1;32mDEBUG\e[0m", # green
|
29
|
+
"INFO" => "\e[1;34mINFO \e[0m", # blue
|
30
|
+
"WARN" => "\e[1;33mWARN \e[0m", # yellow
|
31
|
+
"ERROR" => "\e[1;31mERROR\e[0m", # red
|
32
|
+
"FATAL" => "\e[1;35mFATAL\e[0m" # pink
|
33
|
+
}
|
79
34
|
class Base < ::Logger::Formatter
|
80
35
|
def tid
|
81
36
|
Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
|
82
37
|
end
|
83
38
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
"#{k}=#{v.join(",")}"
|
94
|
-
else
|
95
|
-
"#{k}=#{v}"
|
96
|
-
end
|
97
|
-
}.join(" ")
|
98
|
-
end
|
39
|
+
def format_context(ctxt = Sidekiq::Context.current)
|
40
|
+
(ctxt.size == 0) ? "" : " #{ctxt.map { |k, v|
|
41
|
+
case v
|
42
|
+
when Array
|
43
|
+
"#{k}=#{v.join(",")}"
|
44
|
+
else
|
45
|
+
"#{k}=#{v}"
|
46
|
+
end
|
47
|
+
}.join(" ")}"
|
99
48
|
end
|
100
49
|
end
|
101
50
|
|
102
51
|
class Pretty < Base
|
103
52
|
def call(severity, time, program_name, message)
|
104
|
-
"#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}
|
53
|
+
"#{Formatters::COLORS[severity]} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
|
105
54
|
end
|
106
55
|
end
|
107
56
|
|
108
57
|
class WithoutTimestamp < Pretty
|
109
58
|
def call(severity, time, program_name, message)
|
110
|
-
"pid=#{::Process.pid} tid=#{tid}#{format_context}
|
59
|
+
"#{Formatters::COLORS[severity]} pid=#{::Process.pid} tid=#{tid} #{format_context}: #{message}\n"
|
111
60
|
end
|
112
61
|
end
|
113
62
|
|
@@ -120,7 +69,7 @@ module Sidekiq
|
|
120
69
|
lvl: severity,
|
121
70
|
msg: message
|
122
71
|
}
|
123
|
-
c =
|
72
|
+
c = Sidekiq::Context.current
|
124
73
|
hash["ctx"] = c unless c.empty?
|
125
74
|
|
126
75
|
Sidekiq.dump_json(hash) << "\n"
|