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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +44 -0
  3. data/README.md +16 -13
  4. data/bin/sidekiqload +10 -10
  5. data/bin/webload +69 -0
  6. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +5 -5
  7. data/lib/sidekiq/api.rb +120 -36
  8. data/lib/sidekiq/capsule.rb +6 -6
  9. data/lib/sidekiq/cli.rb +15 -19
  10. data/lib/sidekiq/client.rb +13 -16
  11. data/lib/sidekiq/component.rb +40 -2
  12. data/lib/sidekiq/config.rb +18 -15
  13. data/lib/sidekiq/embedded.rb +1 -0
  14. data/lib/sidekiq/iterable_job.rb +1 -0
  15. data/lib/sidekiq/job/iterable.rb +13 -4
  16. data/lib/sidekiq/job_logger.rb +4 -4
  17. data/lib/sidekiq/job_retry.rb +17 -5
  18. data/lib/sidekiq/job_util.rb +5 -1
  19. data/lib/sidekiq/launcher.rb +1 -1
  20. data/lib/sidekiq/logger.rb +19 -70
  21. data/lib/sidekiq/manager.rb +0 -1
  22. data/lib/sidekiq/metrics/query.rb +71 -45
  23. data/lib/sidekiq/metrics/shared.rb +8 -5
  24. data/lib/sidekiq/metrics/tracking.rb +9 -7
  25. data/lib/sidekiq/middleware/current_attributes.rb +5 -17
  26. data/lib/sidekiq/paginator.rb +8 -1
  27. data/lib/sidekiq/processor.rb +21 -14
  28. data/lib/sidekiq/profiler.rb +59 -0
  29. data/lib/sidekiq/redis_client_adapter.rb +0 -1
  30. data/lib/sidekiq/redis_connection.rb +14 -3
  31. data/lib/sidekiq/testing.rb +2 -2
  32. data/lib/sidekiq/version.rb +2 -2
  33. data/lib/sidekiq/web/action.rb +104 -84
  34. data/lib/sidekiq/web/application.rb +347 -332
  35. data/lib/sidekiq/web/config.rb +117 -0
  36. data/lib/sidekiq/web/helpers.rb +41 -16
  37. data/lib/sidekiq/web/router.rb +60 -76
  38. data/lib/sidekiq/web.rb +50 -156
  39. data/lib/sidekiq.rb +1 -1
  40. data/sidekiq.gemspec +6 -6
  41. data/web/assets/javascripts/application.js +6 -13
  42. data/web/assets/javascripts/base-charts.js +30 -16
  43. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  44. data/web/assets/javascripts/metrics.js +16 -34
  45. data/web/assets/stylesheets/style.css +750 -0
  46. data/web/locales/ar.yml +1 -0
  47. data/web/locales/cs.yml +1 -0
  48. data/web/locales/da.yml +1 -0
  49. data/web/locales/de.yml +1 -0
  50. data/web/locales/el.yml +1 -0
  51. data/web/locales/en.yml +6 -0
  52. data/web/locales/es.yml +24 -2
  53. data/web/locales/fa.yml +1 -0
  54. data/web/locales/fr.yml +1 -0
  55. data/web/locales/gd.yml +1 -0
  56. data/web/locales/he.yml +1 -0
  57. data/web/locales/hi.yml +1 -0
  58. data/web/locales/it.yml +1 -0
  59. data/web/locales/ja.yml +1 -0
  60. data/web/locales/ko.yml +1 -0
  61. data/web/locales/lt.yml +1 -0
  62. data/web/locales/nb.yml +1 -0
  63. data/web/locales/nl.yml +1 -0
  64. data/web/locales/pl.yml +1 -0
  65. data/web/locales/{pt-br.yml → pt-BR.yml} +2 -1
  66. data/web/locales/pt.yml +1 -0
  67. data/web/locales/ru.yml +1 -0
  68. data/web/locales/sv.yml +1 -0
  69. data/web/locales/ta.yml +1 -0
  70. data/web/locales/tr.yml +1 -0
  71. data/web/locales/uk.yml +1 -0
  72. data/web/locales/ur.yml +1 -0
  73. data/web/locales/vi.yml +1 -0
  74. data/web/locales/{zh-cn.yml → zh-CN.yml} +85 -73
  75. data/web/locales/{zh-tw.yml → zh-TW.yml} +2 -1
  76. data/web/views/_footer.erb +31 -33
  77. data/web/views/_job_info.erb +91 -89
  78. data/web/views/_metrics_period_select.erb +13 -10
  79. data/web/views/_nav.erb +14 -21
  80. data/web/views/_paging.erb +23 -21
  81. data/web/views/_poll_link.erb +2 -2
  82. data/web/views/_summary.erb +16 -16
  83. data/web/views/busy.erb +124 -122
  84. data/web/views/dashboard.erb +62 -66
  85. data/web/views/dead.erb +31 -27
  86. data/web/views/filtering.erb +3 -3
  87. data/web/views/layout.erb +6 -22
  88. data/web/views/metrics.erb +75 -81
  89. data/web/views/metrics_for_job.erb +45 -46
  90. data/web/views/morgue.erb +61 -70
  91. data/web/views/profiles.erb +43 -0
  92. data/web/views/queue.erb +54 -52
  93. data/web/views/queues.erb +43 -41
  94. data/web/views/retries.erb +66 -75
  95. data/web/views/retry.erb +32 -27
  96. data/web/views/scheduled.erb +58 -54
  97. data/web/views/scheduled_job_info.erb +1 -1
  98. metadata +24 -24
  99. data/web/assets/stylesheets/application-dark.css +0 -147
  100. data/web/assets/stylesheets/application-rtl.css +0 -163
  101. data/web/assets/stylesheets/application.css +0 -759
  102. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  103. data/web/assets/stylesheets/bootstrap.css +0 -5
  104. data/web/views/_status.erb +0 -4
@@ -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 this but the job has not been enqueued yet
270
- hash.delete("enqueued_at")
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
- queue = payloads.first["queue"]
278
- now = Time.now.to_f
279
- to_push = payloads.map { |entry|
280
- entry["enqueued_at"] = now
281
- Sidekiq.dump_json(entry)
282
- }
283
- conn.sadd("queues", [queue])
284
- conn.lpush("queue:#{queue}", to_push)
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
@@ -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
@@ -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
- l = cfg.logger
45
- l.warn(Sidekiq.dump_json(ctx)) unless ctx.empty?
46
- l.warn("#{ex.class.name}: #{ex.message}")
47
- unless ex.backtrace.nil?
48
- backtrace = cfg[:backtrace_cleaner].call(ex.backtrace)
49
- l.warn(backtrace.join("\n"))
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
- if parameter_size(handler) == 2
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 !!!"
@@ -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)
@@ -46,6 +46,7 @@ module Sidekiq
46
46
  # def on_start
47
47
  # def on_resume
48
48
  # def on_stop
49
+ # def on_cancel
49
50
  # def on_complete
50
51
  # def around_iteration
51
52
  #
@@ -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
- # TODO When Redis 7.2 is required
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
@@ -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
- class: job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"],
30
- jid: job_hash["jid"]
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 && @logger.respond_to?(:log_at)
38
- @logger.log_at(level, &block)
37
+ if level
38
+ @logger.with_level(level, &block)
39
39
  else
40
40
  yield
41
41
  end
@@ -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("utf-8")
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"] = Time.now.to_f
163
+ msg["retried_at"] = now_ms
160
164
  msg["retry_count"] += 1
161
165
  else
162
- msg["failed_at"] = Time.now.to_f
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.to_f)
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) * (count + 1)
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
@@ -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"] ||= Time.now.to_f
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)
@@ -81,7 +81,7 @@ module Sidekiq
81
81
 
82
82
  end
83
83
 
84
- private unless $TESTING
84
+ private
85
85
 
86
86
  BEAT_PAUSE = 10
87
87
 
@@ -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 ctx
85
- Sidekiq::Context.current
86
- end
87
-
88
- def format_context
89
- if ctx.any?
90
- " " + ctx.compact.map { |k, v|
91
- case v
92
- when Array
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} #{severity}: #{message}\n"
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} #{severity}: #{message}\n"
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 = ctx
72
+ c = Sidekiq::Context.current
124
73
  hash["ctx"] = c unless c.empty?
125
74
 
126
75
  Sidekiq.dump_json(hash) << "\n"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq/processor"
4
- require "set"
5
4
 
6
5
  module Sidekiq
7
6
  ##