sidekiq 6.0.0 → 6.0.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/6.0-Upgrade.md +3 -1
- data/Changes.md +110 -1
- data/Ent-Changes.md +7 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +105 -93
- data/Pro-Changes.md +9 -1
- data/README.md +3 -1
- data/bin/sidekiqload +8 -4
- data/bin/sidekiqmon +4 -5
- data/lib/generators/sidekiq/worker_generator.rb +11 -1
- data/lib/sidekiq.rb +12 -0
- data/lib/sidekiq/api.rb +124 -91
- data/lib/sidekiq/cli.rb +29 -18
- data/lib/sidekiq/client.rb +18 -4
- data/lib/sidekiq/fetch.rb +7 -7
- data/lib/sidekiq/job_logger.rb +11 -3
- data/lib/sidekiq/job_retry.rb +23 -10
- data/lib/sidekiq/launcher.rb +3 -5
- data/lib/sidekiq/logger.rb +107 -11
- data/lib/sidekiq/middleware/chain.rb +11 -2
- data/lib/sidekiq/monitor.rb +1 -16
- data/lib/sidekiq/paginator.rb +7 -2
- data/lib/sidekiq/processor.rb +18 -20
- data/lib/sidekiq/redis_connection.rb +3 -0
- data/lib/sidekiq/scheduled.rb +13 -12
- data/lib/sidekiq/testing.rb +12 -0
- data/lib/sidekiq/util.rb +0 -2
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +19 -18
- data/lib/sidekiq/web/helpers.rb +23 -11
- data/lib/sidekiq/worker.rb +4 -4
- data/sidekiq.gemspec +2 -2
- data/web/assets/javascripts/dashboard.js +2 -2
- data/web/assets/stylesheets/application-dark.css +125 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +2 -0
- data/web/locales/ja.yml +2 -0
- data/web/views/_job_info.erb +2 -1
- data/web/views/busy.erb +4 -1
- data/web/views/dead.erb +2 -2
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +4 -1
- data/web/views/queue.erb +10 -1
- data/web/views/queues.erb +8 -0
- data/web/views/retries.erb +4 -1
- data/web/views/retry.erb +2 -2
- data/web/views/scheduled.erb +4 -1
- metadata +9 -8
data/lib/sidekiq/cli.rb
CHANGED
@@ -38,12 +38,15 @@ module Sidekiq
|
|
38
38
|
if environment == "development" && $stdout.tty? && Sidekiq.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
|
39
39
|
print_banner
|
40
40
|
end
|
41
|
+
logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
|
41
42
|
|
42
43
|
self_read, self_write = IO.pipe
|
43
44
|
sigs = %w[INT TERM TTIN TSTP]
|
45
|
+
# USR1 and USR2 don't work on the JVM
|
46
|
+
sigs << "USR2" unless jruby?
|
44
47
|
sigs.each do |sig|
|
45
48
|
trap sig do
|
46
|
-
self_write.
|
49
|
+
self_write.puts(sig)
|
47
50
|
end
|
48
51
|
rescue ArgumentError
|
49
52
|
puts "Signal #{sig} not supported"
|
@@ -56,7 +59,7 @@ module Sidekiq
|
|
56
59
|
# touch the connection pool so it is created before we
|
57
60
|
# fire startup and start multithreading.
|
58
61
|
ver = Sidekiq.redis_info["redis_version"]
|
59
|
-
raise "You are
|
62
|
+
raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
|
60
63
|
|
61
64
|
# Since the user can pass us a connection pool explicitly in the initializer, we
|
62
65
|
# need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
|
@@ -162,15 +165,12 @@ module Sidekiq
|
|
162
165
|
end
|
163
166
|
},
|
164
167
|
}
|
168
|
+
UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
|
169
|
+
SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
|
165
170
|
|
166
171
|
def handle_signal(sig)
|
167
172
|
Sidekiq.logger.debug "Got #{sig} signal"
|
168
|
-
|
169
|
-
if handy
|
170
|
-
handy.call(self)
|
171
|
-
else
|
172
|
-
Sidekiq.logger.info { "No signal handler for #{sig}" }
|
173
|
-
end
|
173
|
+
SIGNAL_HANDLERS[sig].call(self)
|
174
174
|
end
|
175
175
|
|
176
176
|
private
|
@@ -182,7 +182,11 @@ module Sidekiq
|
|
182
182
|
end
|
183
183
|
|
184
184
|
def set_environment(cli_env)
|
185
|
-
|
185
|
+
# See #984 for discussion.
|
186
|
+
# APP_ENV is now the preferred ENV term since it is not tech-specific.
|
187
|
+
# Both Sinatra 2.0+ and Sidekiq support this term.
|
188
|
+
# RACK_ENV and RAILS_ENV are there for legacy support.
|
189
|
+
@environment = cli_env || ENV["APP_ENV"] || ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
|
186
190
|
end
|
187
191
|
|
188
192
|
def symbolize_keys_deep!(hash)
|
@@ -204,7 +208,7 @@ module Sidekiq
|
|
204
208
|
|
205
209
|
# check config file presence
|
206
210
|
if opts[:config_file]
|
207
|
-
|
211
|
+
unless File.exist?(opts[:config_file])
|
208
212
|
raise ArgumentError, "No such file #{opts[:config_file]}"
|
209
213
|
end
|
210
214
|
else
|
@@ -224,7 +228,7 @@ module Sidekiq
|
|
224
228
|
opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
|
225
229
|
|
226
230
|
# set defaults
|
227
|
-
opts[:queues] =
|
231
|
+
opts[:queues] = ["default"] if opts[:queues].nil? || opts[:queues].empty?
|
228
232
|
opts[:strict] = true if opts[:strict].nil?
|
229
233
|
opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
|
230
234
|
|
@@ -283,8 +287,13 @@ module Sidekiq
|
|
283
287
|
|
284
288
|
def parse_options(argv)
|
285
289
|
opts = {}
|
290
|
+
@parser = option_parser(opts)
|
291
|
+
@parser.parse!(argv)
|
292
|
+
opts
|
293
|
+
end
|
286
294
|
|
287
|
-
|
295
|
+
def option_parser(opts)
|
296
|
+
parser = OptionParser.new { |o|
|
288
297
|
o.on "-c", "--concurrency INT", "processor threads to use" do |arg|
|
289
298
|
opts[:concurrency] = Integer(arg)
|
290
299
|
end
|
@@ -336,15 +345,13 @@ module Sidekiq
|
|
336
345
|
end
|
337
346
|
}
|
338
347
|
|
339
|
-
|
340
|
-
|
341
|
-
logger.info
|
348
|
+
parser.banner = "sidekiq [options]"
|
349
|
+
parser.on_tail "-h", "--help", "Show help" do
|
350
|
+
logger.info parser
|
342
351
|
die 1
|
343
352
|
end
|
344
353
|
|
345
|
-
|
346
|
-
|
347
|
-
opts
|
354
|
+
parser
|
348
355
|
end
|
349
356
|
|
350
357
|
def initialize_logger
|
@@ -376,5 +383,9 @@ module Sidekiq
|
|
376
383
|
[weight.to_i, 1].max.times { opts[:queues] << queue }
|
377
384
|
opts[:strict] = false if weight.to_i > 0
|
378
385
|
end
|
386
|
+
|
387
|
+
def rails_app?
|
388
|
+
defined?(::Rails) && ::Rails.respond_to?(:application)
|
389
|
+
end
|
379
390
|
end
|
380
391
|
end
|
data/lib/sidekiq/client.rb
CHANGED
@@ -94,9 +94,14 @@ module Sidekiq
|
|
94
94
|
return [] unless arg # no jobs to push
|
95
95
|
raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless arg.is_a?(Array)
|
96
96
|
|
97
|
+
at = items.delete("at")
|
98
|
+
raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all?(Numeric))
|
99
|
+
|
97
100
|
normed = normalize_item(items)
|
98
|
-
payloads = items["args"].map { |args|
|
101
|
+
payloads = items["args"].map.with_index { |args, index|
|
99
102
|
copy = normed.merge("args" => args, "jid" => SecureRandom.hex(12), "enqueued_at" => Time.now.to_f)
|
103
|
+
copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
|
104
|
+
|
100
105
|
result = process_single(items["class"], copy)
|
101
106
|
result || nil
|
102
107
|
}.compact
|
@@ -188,7 +193,7 @@ module Sidekiq
|
|
188
193
|
end
|
189
194
|
|
190
195
|
def atomic_push(conn, payloads)
|
191
|
-
if payloads.first
|
196
|
+
if payloads.first.key?("at")
|
192
197
|
conn.zadd("schedule", payloads.map { |hash|
|
193
198
|
at = hash.delete("at").to_s
|
194
199
|
[at, Sidekiq.dump_json(hash)]
|
@@ -214,19 +219,28 @@ module Sidekiq
|
|
214
219
|
end
|
215
220
|
|
216
221
|
def normalize_item(item)
|
222
|
+
# 6.0.0 push_bulk bug, #4321
|
223
|
+
# TODO Remove after a while...
|
224
|
+
item.delete("at") if item.key?("at") && item["at"].nil?
|
225
|
+
|
217
226
|
raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
|
218
227
|
raise(ArgumentError, "Job args must be an Array") unless item["args"].is_a?(Array)
|
219
228
|
raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
|
220
229
|
raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.key?("at") && !item["at"].is_a?(Numeric)
|
230
|
+
raise(ArgumentError, "Job tags must be an Array") if item["tags"] && !item["tags"].is_a?(Array)
|
221
231
|
# raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
|
222
232
|
|
223
|
-
|
224
|
-
|
233
|
+
# merge in the default sidekiq_options for the item's class and/or wrapped element
|
234
|
+
# this allows ActiveJobs to control sidekiq_options too.
|
235
|
+
defaults = normalized_hash(item["class"])
|
236
|
+
defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?("get_sidekiq_options")
|
237
|
+
item = defaults.merge(item)
|
225
238
|
|
226
239
|
item["class"] = item["class"].to_s
|
227
240
|
item["queue"] = item["queue"].to_s
|
228
241
|
item["jid"] ||= SecureRandom.hex(12)
|
229
242
|
item["created_at"] ||= Time.now.to_f
|
243
|
+
|
230
244
|
item
|
231
245
|
end
|
232
246
|
|
data/lib/sidekiq/fetch.rb
CHANGED
@@ -14,12 +14,12 @@ module Sidekiq
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def queue_name
|
17
|
-
queue.
|
17
|
+
queue.delete_prefix("queue:")
|
18
18
|
end
|
19
19
|
|
20
20
|
def requeue
|
21
21
|
Sidekiq.redis do |conn|
|
22
|
-
conn.rpush(
|
22
|
+
conn.rpush(queue, job)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
}
|
@@ -28,7 +28,7 @@ module Sidekiq
|
|
28
28
|
@strictly_ordered_queues = !!options[:strict]
|
29
29
|
@queues = options[:queues].map { |q| "queue:#{q}" }
|
30
30
|
if @strictly_ordered_queues
|
31
|
-
@queues
|
31
|
+
@queues.uniq!
|
32
32
|
@queues << TIMEOUT
|
33
33
|
end
|
34
34
|
end
|
@@ -47,7 +47,7 @@ module Sidekiq
|
|
47
47
|
if @strictly_ordered_queues
|
48
48
|
@queues
|
49
49
|
else
|
50
|
-
queues = @queues.shuffle
|
50
|
+
queues = @queues.shuffle!.uniq
|
51
51
|
queues << TIMEOUT
|
52
52
|
queues
|
53
53
|
end
|
@@ -61,14 +61,14 @@ module Sidekiq
|
|
61
61
|
Sidekiq.logger.debug { "Re-queueing terminated jobs" }
|
62
62
|
jobs_to_requeue = {}
|
63
63
|
inprogress.each do |unit_of_work|
|
64
|
-
jobs_to_requeue[unit_of_work.
|
65
|
-
jobs_to_requeue[unit_of_work.
|
64
|
+
jobs_to_requeue[unit_of_work.queue] ||= []
|
65
|
+
jobs_to_requeue[unit_of_work.queue] << unit_of_work.job
|
66
66
|
end
|
67
67
|
|
68
68
|
Sidekiq.redis do |conn|
|
69
69
|
conn.pipelined do
|
70
70
|
jobs_to_requeue.each do |queue, jobs|
|
71
|
-
conn.rpush(
|
71
|
+
conn.rpush(queue, jobs)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/lib/sidekiq/job_logger.rb
CHANGED
@@ -23,8 +23,15 @@ module Sidekiq
|
|
23
23
|
raise
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
26
|
+
def prepare(job_hash, &block)
|
27
|
+
level = job_hash["log_level"]
|
28
|
+
if level
|
29
|
+
@logger.log_at(level) do
|
30
|
+
Sidekiq::Context.with(job_hash_context(job_hash), &block)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
Sidekiq::Context.with(job_hash_context(job_hash), &block)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
def job_hash_context(job_hash)
|
@@ -35,11 +42,12 @@ module Sidekiq
|
|
35
42
|
jid: job_hash["jid"],
|
36
43
|
}
|
37
44
|
h[:bid] = job_hash["bid"] if job_hash["bid"]
|
45
|
+
h[:tags] = job_hash["tags"] if job_hash["tags"]
|
38
46
|
h
|
39
47
|
end
|
40
48
|
|
41
49
|
def with_elapsed_time_context(start, &block)
|
42
|
-
|
50
|
+
Sidekiq::Context.with(elapsed_time_context(start), &block)
|
43
51
|
end
|
44
52
|
|
45
53
|
def elapsed_time_context(start)
|
data/lib/sidekiq/job_retry.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require "sidekiq/scheduled"
|
4
4
|
require "sidekiq/api"
|
5
5
|
|
6
|
+
require "zlib"
|
7
|
+
require "base64"
|
8
|
+
|
6
9
|
module Sidekiq
|
7
10
|
##
|
8
11
|
# Automatically retry jobs that fail in Sidekiq.
|
@@ -71,7 +74,7 @@ module Sidekiq
|
|
71
74
|
# The global retry handler requires only the barest of data.
|
72
75
|
# We want to be able to retry as much as possible so we don't
|
73
76
|
# require the worker to be instantiated.
|
74
|
-
def global(
|
77
|
+
def global(jobstr, queue)
|
75
78
|
yield
|
76
79
|
rescue Handled => ex
|
77
80
|
raise ex
|
@@ -82,6 +85,7 @@ module Sidekiq
|
|
82
85
|
# ignore, will be pushed back onto queue during hard_shutdown
|
83
86
|
raise Sidekiq::Shutdown if exception_caused_by_shutdown?(e)
|
84
87
|
|
88
|
+
msg = Sidekiq.load_json(jobstr)
|
85
89
|
if msg["retry"]
|
86
90
|
attempt_retry(nil, msg, queue, e)
|
87
91
|
else
|
@@ -103,7 +107,7 @@ module Sidekiq
|
|
103
107
|
# exception so the global block does not reprocess the error. The
|
104
108
|
# Skip exception is unwrapped within Sidekiq::Processor#process before
|
105
109
|
# calling the handle_exception handlers.
|
106
|
-
def local(worker,
|
110
|
+
def local(worker, jobstr, queue)
|
107
111
|
yield
|
108
112
|
rescue Handled => ex
|
109
113
|
raise ex
|
@@ -114,6 +118,7 @@ module Sidekiq
|
|
114
118
|
# ignore, will be pushed back onto queue during hard_shutdown
|
115
119
|
raise Sidekiq::Shutdown if exception_caused_by_shutdown?(e)
|
116
120
|
|
121
|
+
msg = Sidekiq.load_json(jobstr)
|
117
122
|
if msg["retry"].nil?
|
118
123
|
msg["retry"] = worker.class.get_sidekiq_options["retry"]
|
119
124
|
end
|
@@ -151,12 +156,14 @@ module Sidekiq
|
|
151
156
|
msg["retry_count"] = 0
|
152
157
|
end
|
153
158
|
|
154
|
-
if msg["backtrace"]
|
155
|
-
msg["
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
159
|
+
if msg["backtrace"]
|
160
|
+
lines = if msg["backtrace"] == true
|
161
|
+
exception.backtrace
|
162
|
+
else
|
163
|
+
exception.backtrace[0...msg["backtrace"].to_i]
|
164
|
+
end
|
165
|
+
|
166
|
+
msg["error_backtrace"] = compress_backtrace(lines)
|
160
167
|
end
|
161
168
|
|
162
169
|
if count < max_retry_attempts
|
@@ -182,13 +189,13 @@ module Sidekiq
|
|
182
189
|
handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
|
183
190
|
end
|
184
191
|
|
192
|
+
send_to_morgue(msg) unless msg["dead"] == false
|
193
|
+
|
185
194
|
Sidekiq.death_handlers.each do |handler|
|
186
195
|
handler.call(msg, exception)
|
187
196
|
rescue => e
|
188
197
|
handle_exception(e, {context: "Error calling death handler", job: msg})
|
189
198
|
end
|
190
|
-
|
191
|
-
send_to_morgue(msg) unless msg["dead"] == false
|
192
199
|
end
|
193
200
|
|
194
201
|
def send_to_morgue(msg)
|
@@ -245,5 +252,11 @@ module Sidekiq
|
|
245
252
|
rescue
|
246
253
|
+"!!! ERROR MESSAGE THREW AN ERROR !!!"
|
247
254
|
end
|
255
|
+
|
256
|
+
def compress_backtrace(backtrace)
|
257
|
+
serialized = Sidekiq.dump_json(backtrace)
|
258
|
+
compressed = Zlib::Deflate.deflate(serialized)
|
259
|
+
Base64.encode64(compressed)
|
260
|
+
end
|
248
261
|
end
|
249
262
|
end
|
data/lib/sidekiq/launcher.rb
CHANGED
@@ -83,7 +83,7 @@ module Sidekiq
|
|
83
83
|
Sidekiq.redis do |conn|
|
84
84
|
conn.pipelined do
|
85
85
|
conn.srem("processes", identity)
|
86
|
-
conn.
|
86
|
+
conn.unlink("#{identity}:workers")
|
87
87
|
end
|
88
88
|
end
|
89
89
|
rescue
|
@@ -118,7 +118,7 @@ module Sidekiq
|
|
118
118
|
conn.incrby("stat:failed:#{nowdate}", fails)
|
119
119
|
conn.expire("stat:failed:#{nowdate}", STATS_TTL)
|
120
120
|
|
121
|
-
conn.
|
121
|
+
conn.unlink(workers_key)
|
122
122
|
curstate.each_pair do |tid, hash|
|
123
123
|
conn.hset(workers_key, tid, Sidekiq.dump_json(hash))
|
124
124
|
end
|
@@ -129,15 +129,13 @@ module Sidekiq
|
|
129
129
|
fails = procd = 0
|
130
130
|
|
131
131
|
_, exists, _, _, msg = Sidekiq.redis { |conn|
|
132
|
-
|
132
|
+
conn.multi {
|
133
133
|
conn.sadd("processes", key)
|
134
134
|
conn.exists(key)
|
135
135
|
conn.hmset(key, "info", to_json, "busy", curstate.size, "beat", Time.now.to_f, "quiet", @done)
|
136
136
|
conn.expire(key, 60)
|
137
137
|
conn.rpop("#{key}-signals")
|
138
138
|
}
|
139
|
-
|
140
|
-
res
|
141
139
|
}
|
142
140
|
|
143
141
|
# first heartbeat or recovering from an outage and need to reestablish our heartbeat
|
data/lib/sidekiq/logger.rb
CHANGED
@@ -4,22 +4,109 @@ require "logger"
|
|
4
4
|
require "time"
|
5
5
|
|
6
6
|
module Sidekiq
|
7
|
-
|
8
|
-
def
|
9
|
-
|
7
|
+
module Context
|
8
|
+
def self.with(hash)
|
9
|
+
current.merge!(hash)
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
hash.each_key { |key| current.delete(key) }
|
13
|
+
end
|
10
14
|
|
11
|
-
|
15
|
+
def self.current
|
16
|
+
Thread.current[:sidekiq_context] ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module LoggingUtils
|
21
|
+
LEVELS = {
|
22
|
+
"debug" => 0,
|
23
|
+
"info" => 1,
|
24
|
+
"warn" => 2,
|
25
|
+
"error" => 3,
|
26
|
+
"fatal" => 4,
|
27
|
+
}
|
28
|
+
LEVELS.default_proc = proc do |_, level|
|
29
|
+
Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def debug?
|
34
|
+
level >= 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def info?
|
38
|
+
level >= 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def warn?
|
42
|
+
level >= 2
|
43
|
+
end
|
44
|
+
|
45
|
+
def error?
|
46
|
+
level >= 3
|
47
|
+
end
|
48
|
+
|
49
|
+
def fatal?
|
50
|
+
level >= 4
|
12
51
|
end
|
13
52
|
|
14
|
-
def
|
15
|
-
|
53
|
+
def local_level
|
54
|
+
Thread.current[:sidekiq_log_level]
|
55
|
+
end
|
56
|
+
|
57
|
+
def local_level=(level)
|
58
|
+
case level
|
59
|
+
when Integer
|
60
|
+
Thread.current[:sidekiq_log_level] = level
|
61
|
+
when Symbol, String
|
62
|
+
Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
|
63
|
+
when nil
|
64
|
+
Thread.current[:sidekiq_log_level] = nil
|
65
|
+
else
|
66
|
+
raise ArgumentError, "Invalid log level: #{level.inspect}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def level
|
71
|
+
local_level || super
|
72
|
+
end
|
73
|
+
|
74
|
+
# Change the thread-local level for the duration of the given block.
|
75
|
+
def log_at(level)
|
76
|
+
old_local_level = local_level
|
77
|
+
self.local_level = level
|
16
78
|
yield
|
17
79
|
ensure
|
18
|
-
|
80
|
+
self.local_level = old_local_level
|
19
81
|
end
|
20
82
|
|
21
|
-
|
22
|
-
|
83
|
+
# Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
|
84
|
+
# FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
|
85
|
+
def add(severity, message = nil, progname = nil, &block)
|
86
|
+
severity ||= ::Logger::UNKNOWN
|
87
|
+
progname ||= @progname
|
88
|
+
|
89
|
+
return true if @logdev.nil? || severity < level
|
90
|
+
|
91
|
+
if message.nil?
|
92
|
+
if block_given?
|
93
|
+
message = yield
|
94
|
+
else
|
95
|
+
message = progname
|
96
|
+
progname = @progname
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
@logdev.write format_message(format_severity(severity), Time.now, progname, message)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Logger < ::Logger
|
105
|
+
include LoggingUtils
|
106
|
+
|
107
|
+
def initialize(*args, **kwargs)
|
108
|
+
super
|
109
|
+
self.formatter = Sidekiq.log_formatter
|
23
110
|
end
|
24
111
|
|
25
112
|
module Formatters
|
@@ -29,11 +116,20 @@ module Sidekiq
|
|
29
116
|
end
|
30
117
|
|
31
118
|
def ctx
|
32
|
-
|
119
|
+
Sidekiq::Context.current
|
33
120
|
end
|
34
121
|
|
35
122
|
def format_context
|
36
|
-
|
123
|
+
if ctx.any?
|
124
|
+
" " + ctx.compact.map { |k, v|
|
125
|
+
case v
|
126
|
+
when Array
|
127
|
+
"#{k}=#{v.join(",")}"
|
128
|
+
else
|
129
|
+
"#{k}=#{v}"
|
130
|
+
end
|
131
|
+
}.join(" ")
|
132
|
+
end
|
37
133
|
end
|
38
134
|
end
|
39
135
|
|