rails_semantic_logger 5.0.0 → 5.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 213d85a11e285b1353549f41b8a8c28068a33d3913e270d72eed94f6ee1d861c
4
- data.tar.gz: 8233c4128fdc790e3cefedd3efc8d8c6b274393604527cc162dff97842227fea
3
+ metadata.gz: f0d257eeb6faedaba38caba805893dc4c269dfaf0b1b0f5cc496d54e3bc3f6ad
4
+ data.tar.gz: 5ae1af5ac5514a6fd84f70f4b0d13232791f942b3fead595a082ae15c1258893
5
5
  SHA512:
6
- metadata.gz: 52330aaea8cf48aa21a62b8c5654163ce481382fa67bd6935c02d3deecfcef3a83a32b30c563941eb49404d86063ff2056778692781bd14a96174b55d2372f9f
7
- data.tar.gz: 5da348c363745f1e3b7a2fc73c36221658f86dafcad53d5078990d9d204fe5bf2224a319a156395597a7916bb2a950b1c27974114bbee37662f388295999758a
6
+ metadata.gz: 7a79e69e2a466f8a0f414b21e3890118eac262c7bb2983a91cfac547fc04f3cb91535bde3aa1ff611d57a1b5a6e56d7ba885f8177f3d0a0777682de844ba2b9e
7
+ data.tar.gz: cc0fb18514ef84ee7e3ec270a70bf155aa54e6f71e0fffe59f0492fb712d5dab64ec27c42ab69afaccde1259aeb75913319d1fbc07f70bea7938e3fe231ef8aa
data/Rakefile CHANGED
@@ -5,10 +5,12 @@ require "bundler/setup"
5
5
  require "rake/testtask"
6
6
  require_relative "lib/rails_semantic_logger/version"
7
7
 
8
+ desc "Build the gem"
8
9
  task :gem do
9
10
  system "gem build rails_semantic_logger.gemspec"
10
11
  end
11
12
 
13
+ desc "Build and publish the gem, and tag the release"
12
14
  task publish: :gem do
13
15
  system "git tag -a v#{RailsSemanticLogger::VERSION} -m 'Tagging #{RailsSemanticLogger::VERSION}'"
14
16
  system "git push --tags"
@@ -137,7 +137,8 @@ module RailsSemanticLogger
137
137
  payload[:source] = source if source
138
138
  end
139
139
 
140
- controller_logger(event).info(message: "Redirected to", payload: payload, metric: "rails.controller.redirect_to")
140
+ controller_logger(event).info(message: "Redirected to", payload: payload,
141
+ metric: "rails.controller.redirect_to")
141
142
  end
142
143
 
143
144
  def send_data(event)
@@ -142,7 +142,7 @@ module RailsSemanticLogger
142
142
  end
143
143
 
144
144
  def rails_root
145
- @root ||= "#{Rails.root}/"
145
+ @rails_root ||= "#{Rails.root}/"
146
146
  end
147
147
 
148
148
  def logger
@@ -161,12 +161,12 @@ module RailsSemanticLogger
161
161
  super
162
162
  end
163
163
 
164
- private
164
+ EMPTY = "".freeze
165
165
 
166
166
  @logger = ::ActionView::Base.logger
167
167
  @rendered_log_level = :debug
168
168
 
169
- EMPTY = "".freeze
169
+ private
170
170
 
171
171
  def should_log?
172
172
  logger.send("#{self.class.rendered_log_level}?")
@@ -16,8 +16,8 @@ module RailsSemanticLogger
16
16
  # destination and formatting are ordinary `SemanticLogger.add_appender` arguments:
17
17
  #
18
18
  # #add - always created, during Rails initialization.
19
- # #add_server - created only when serving requests (`rails server`, a rack
20
- # server, Sidekiq in server mode). Defaults to `$stdout`.
19
+ # #add_server - created only when serving requests (`rails server`, Sidekiq
20
+ # in server mode). Defaults to `$stdout`.
21
21
  # #add_console - created only inside a `rails console` session. Defaults to
22
22
  # `$stderr` so log output does not tangle with command results.
23
23
  #
@@ -44,9 +44,10 @@ module RailsSemanticLogger
44
44
  end
45
45
 
46
46
  # Declare an appender that is only created when the application is serving
47
- # requests: `rails server`, a rack server started directly (puma, etc.), or
48
- # Sidekiq in server mode. It is never created during a non-serving boot (rake
49
- # tasks, runners, generators), so it only appears where it is useful.
47
+ # requests: `rails server`, Sidekiq in server mode, or when the app calls
48
+ # RailsSemanticLogger.add_server_appenders from its own server's boot hook.
49
+ # It is never created during a non-serving boot (rake tasks, runners,
50
+ # generators), so it only appears where it is useful.
50
51
  #
51
52
  # Accepts the same arguments (and optional block) as SemanticLogger.add_appender.
52
53
  # When no destination is given it defaults to `$stdout`; the formatter defaults
@@ -48,7 +48,7 @@ module RailsSemanticLogger
48
48
  elsif config.rails_semantic_logger.add_file_appender
49
49
  # DEPRECATED: This option is deprecated and will be removed in a future version.
50
50
  path = config.paths["log"].first
51
- FileUtils.mkdir_p(File.dirname(path)) unless File.exist?(File.dirname(path))
51
+ FileUtils.mkdir_p(File.dirname(path))
52
52
 
53
53
  # Add the log file to the list of appenders
54
54
  # Use the colorized formatter if Rails colorized logs are enabled
@@ -67,21 +67,28 @@ module RailsSemanticLogger
67
67
 
68
68
  app.formatter = formatter
69
69
  end
70
- SemanticLogger.add_appender(file_name: path, formatter: formatter, filter: config.rails_semantic_logger.filter)
70
+ SemanticLogger.add_appender(file_name: path, formatter: formatter,
71
+ filter: config.rails_semantic_logger.filter)
71
72
  end
72
73
 
73
74
  SemanticLogger[Rails]
74
75
  rescue StandardError => e
75
- # If not able to log to file, log to standard error with warning level only
76
+ # If unable to create the appenders, log to standard error with warning level only
76
77
  SemanticLogger.default_level = :warn
77
78
 
78
79
  SemanticLogger::Processor.logger = SemanticLogger::Appender::IO.new($stderr)
79
80
  SemanticLogger.add_appender(io: $stderr)
80
81
 
82
+ message =
83
+ if path
84
+ "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666."
85
+ else
86
+ "Rails Error: Unable to create the configured appenders."
87
+ end
81
88
  logger = SemanticLogger[Rails]
82
89
  logger.warn(
83
- "Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " \
84
- "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.",
90
+ "#{message} The log level has been raised to WARN and the output directed to STDERR " \
91
+ "until the problem is fixed.",
85
92
  e
86
93
  )
87
94
  logger
@@ -122,12 +129,8 @@ module RailsSemanticLogger
122
129
  end
123
130
 
124
131
  ::Sidekiq.configure_server do |config|
125
- config.logger = ::SemanticLogger[::Sidekiq]
126
- if ::Sidekiq::VERSION.to_i < 6 || (::Sidekiq::VERSION.to_i == 6 && ::Sidekiq::VERSION.to_f < 6.5)
127
- config.options[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
128
- else
129
- config[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
130
- end
132
+ config.logger = ::SemanticLogger[::Sidekiq]
133
+ config[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
131
134
 
132
135
  # Add back the default console logger (unless the app declared its own appenders, or one exists)
133
136
  RailsSemanticLogger.add_server_appenders
@@ -137,15 +140,12 @@ module RailsSemanticLogger
137
140
  config.error_handlers << RailsSemanticLogger::Sidekiq::Defaults::ERROR_HANDLER if existing
138
141
  end
139
142
 
140
- if defined?(::Sidekiq::Job) && (::Sidekiq::VERSION.to_i != 5)
141
- ::Sidekiq::Job.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)
142
- else
143
- ::Sidekiq::Worker.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)
144
- end
143
+ ::Sidekiq::Job.singleton_class.prepend(RailsSemanticLogger::Sidekiq::Loggable)
145
144
  end
146
145
 
147
146
  # Replace the SolidQueue logger
148
- if config.rails_semantic_logger.replace_solid_queue_logger && defined?(::SolidQueue) && ::SolidQueue.respond_to?(:logger=)
147
+ if config.rails_semantic_logger.replace_solid_queue_logger &&
148
+ defined?(::SolidQueue) && ::SolidQueue.respond_to?(:logger=)
149
149
  ::SolidQueue.logger = SemanticLogger[::SolidQueue]
150
150
  end
151
151
 
@@ -224,14 +224,18 @@ module RailsSemanticLogger
224
224
  if config.rails_semantic_logger.quiet_assets && config.respond_to?(:assets) && config.assets.prefix
225
225
  assets_root = config.relative_url_root.to_s + config.assets.prefix
226
226
  assets_regex = %r(\A/{0,2}#{assets_root})
227
- RailsSemanticLogger::Rack::Logger.logger.filter = ->(log) { log.payload[:path] !~ assets_regex if log.payload }
227
+ RailsSemanticLogger::Rack::Logger.logger.filter = lambda do |log|
228
+ log.payload[:path] !~ assets_regex if log.payload
229
+ end
228
230
  end
229
231
 
230
232
  # Action View
231
233
  if defined?(::ActionView)
232
234
  require "action_view/log_subscriber"
233
235
 
234
- RailsSemanticLogger::ActionView::LogSubscriber.rendered_log_level = :info if config.rails_semantic_logger.rendered
236
+ if config.rails_semantic_logger.rendered
237
+ RailsSemanticLogger::ActionView::LogSubscriber.rendered_log_level = :info
238
+ end
235
239
  RailsSemanticLogger.swap_subscriber(
236
240
  ::ActionView::LogSubscriber,
237
241
  RailsSemanticLogger::ActionView::LogSubscriber,
@@ -264,10 +268,6 @@ module RailsSemanticLogger
264
268
  )
265
269
  end
266
270
 
267
- if config.rails_semantic_logger.replace_sidekiq_logger && defined?(::Sidekiq)
268
- require("rails_semantic_logger/extensions/sidekiq/sidekiq")
269
- end
270
-
271
271
  # SolidQueue
272
272
  if config.rails_semantic_logger.replace_solid_queue_logger && defined?(::SolidQueue::LogSubscriber)
273
273
  RailsSemanticLogger.swap_subscriber(
@@ -30,12 +30,29 @@ module ActionCable
30
30
  # wrapped logger, while still applying the connection's tags. See #220.
31
31
  %i[debug info warn error fatal unknown].each do |severity|
32
32
  define_method(severity) do |message = nil, payload = nil, exception = nil, &block|
33
- tag(@logger) { @logger.public_send(severity, message, payload, exception, &block) }
33
+ tag(@logger) { forward(severity, message, payload, exception, &block) }
34
34
  end
35
35
  end
36
36
 
37
37
  private
38
38
 
39
+ # Only forward the extra payload/exception arguments when the wrapped logger's
40
+ # method can accept them. A standard Ruby Logger (e.g. the one wrapped by
41
+ # ActionCable::Connection::TestCase) only accepts a single `progname` argument
42
+ # and raises ArgumentError given more, so fall back to the single-arg call. See #317.
43
+ def forward(severity, message, payload, exception, &)
44
+ if extended_signature?(@logger, severity)
45
+ @logger.public_send(severity, message, payload, exception, &)
46
+ else
47
+ @logger.public_send(severity, message, &)
48
+ end
49
+ end
50
+
51
+ def extended_signature?(logger, severity)
52
+ params = logger.method(severity).parameters
53
+ params.any? { |type, _| type == :rest } || params.count { |type, _| %i[req opt].include?(type) } >= 3
54
+ end
55
+
39
56
  # Tags already applied to the target logger, so they are not duplicated.
40
57
  # Semantic Logger exposes them via `#tags`; ActiveSupport::TaggedLogging
41
58
  # via `formatter.current_tags`. Fall back to none when neither is present.
@@ -181,7 +181,7 @@ module RailsSemanticLogger
181
181
  # The method names the context in which the appender is created; the destination
182
182
  # is an ordinary `SemanticLogger.add_appender` argument. Use `add` for an
183
183
  # appender that is always created, `add_server` for one created only when serving
184
- # requests (`rails server`, a rack server, Sidekiq in server mode; defaults to
184
+ # requests (`rails server`, Sidekiq in server mode; defaults to
185
185
  # `$stdout`), and `add_console` for one created only inside a `rails console`
186
186
  # session (defaults to `$stderr`). Any appender works in any context, so a
187
187
  # context may declare several (e.g. a server-only stdout and file appender).
@@ -4,38 +4,21 @@ module RailsSemanticLogger
4
4
  # Prevent exception logging during standard error handling since the Job Logger below already logs the exception.
5
5
  # Logs the remaining Sidekiq context at :info (matching upstream Sidekiq's default handler) rather than :warn,
6
6
  # since the exception itself is already logged at :error by the Job Logger.
7
+ # Sidekiq 7.1.6+ calls error handlers with a third config argument; earlier 7.x versions pass only two.
7
8
  ERROR_HANDLER =
8
- if ::Sidekiq::VERSION.to_f < 7.1 ||
9
- (::Sidekiq::VERSION.to_f == 7.1 && ::Sidekiq::VERSION.split(".").last.to_i < 6)
10
- lambda do |_ex, ctx|
11
- unless ctx.empty?
12
- job_hash = ctx[:job] || {}
13
- klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
14
- logger = klass ? SemanticLogger[klass] : ::Sidekiq.logger
15
- ctx[:context] ? logger.info(ctx[:context], ctx) : logger.info(ctx)
16
- end
17
- end
18
- else
19
- lambda do |_ex, ctx, _default_configuration|
20
- unless ctx.empty?
21
- job_hash = ctx[:job] || {}
22
- klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
23
- logger = klass ? SemanticLogger[klass] : ::Sidekiq.logger
24
- ctx[:context] ? logger.info(ctx[:context], ctx) : logger.info(ctx)
25
- end
9
+ lambda do |_ex, ctx, _config = nil|
10
+ unless ctx.empty?
11
+ job_hash = ctx[:job] || {}
12
+ klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
13
+ logger = klass ? SemanticLogger[klass] : ::Sidekiq.logger
14
+ ctx[:context] ? logger.info(ctx[:context], ctx) : logger.info(ctx)
26
15
  end
27
16
  end
28
17
 
29
- # Returns the default logger after removing from the supplied list.
30
- # Returns [nil] when the default logger was not present.
18
+ # Returns the default error handler after removing it from the supplied list.
19
+ # Returns [nil] when the default handler was not present.
31
20
  def self.delete_default_error_handler(error_handlers)
32
- return error_handlers.delete(::Sidekiq::Config::ERROR_HANDLER) if defined?(::Sidekiq::Config::ERROR_HANDLER)
33
- return error_handlers.delete(::Sidekiq::DEFAULT_ERROR_HANDLER) if defined?(::Sidekiq::DEFAULT_ERROR_HANDLER)
34
-
35
- return unless defined?(::Sidekiq::ExceptionHandler)
36
-
37
- existing = error_handlers.find { |handler| handler.is_a?(::Sidekiq::ExceptionHandler::Logger) }
38
- error_handlers.delete(existing) if existing
21
+ error_handlers.delete(::Sidekiq::Config::ERROR_HANDLER)
39
22
  end
40
23
  end
41
24
  end
@@ -1,6 +1,10 @@
1
1
  module RailsSemanticLogger
2
2
  module Sidekiq
3
3
  class JobLogger
4
+ # Job attributes copied into the logging context, matching Sidekiq's own defaults.
5
+ # Sidekiq 8 makes this configurable via `config[:logged_job_attributes]`.
6
+ DEFAULT_LOGGED_JOB_ATTRIBUTES = %w[bid tags].freeze
7
+
4
8
  class << self
5
9
  attr_writer :perform_messages
6
10
 
@@ -9,8 +13,9 @@ module RailsSemanticLogger
9
13
  end
10
14
  end
11
15
 
12
- # Sidekiq 6.5 does not take any arguments, whereas v7 is given a logger
13
- def initialize(*_args)
16
+ # Sidekiq 7 passes its logger, whereas Sidekiq 8 passes its config.
17
+ def initialize(arg = nil)
18
+ @config = arg if arg.respond_to?(:[])
14
19
  end
15
20
 
16
21
  def call(item, queue, &block)
@@ -56,17 +61,24 @@ module RailsSemanticLogger
56
61
  private
57
62
 
58
63
  def perform_messages_enabled?
64
+ return false if @config && @config[:skip_default_job_logging]
65
+
59
66
  self.class.perform_messages != false
60
67
  end
61
68
 
62
69
  def job_hash_context(job_hash)
63
- h = {jid: job_hash["jid"], class: job_hash["wrapped"] || job_hash["class"]}
64
- h[:bid] = job_hash["bid"] if job_hash["bid"]
65
- h[:tags] = job_hash["tags"] if job_hash["tags"]
70
+ h = {jid: job_hash["jid"], class: job_hash["wrapped"] || job_hash["class"]}
71
+ logged_job_attributes.each do |attr|
72
+ h[attr.to_sym] = job_hash[attr] if job_hash.key?(attr)
73
+ end
66
74
  h[:queue] = job_hash["queue"] if job_hash["queue"]
67
75
  h
68
76
  end
69
77
 
78
+ def logged_job_attributes
79
+ (@config && @config[:logged_job_attributes]) || DEFAULT_LOGGED_JOB_ATTRIBUTES
80
+ end
81
+
70
82
  def job_latency_ms(job)
71
83
  return unless job && job["enqueued_at"]
72
84
 
@@ -54,7 +54,11 @@ module RailsSemanticLogger
54
54
  elsif event.payload[:other_adapter]
55
55
  log_event(event, :debug, "Enqueued recurring task outside Solid Queue", **attributes)
56
56
  else
57
- action = event.payload[:skipped].present? ? "Skipped recurring task – already dispatched" : "Enqueued recurring task"
57
+ action = if event.payload[:skipped].present?
58
+ "Skipped recurring task – already dispatched"
59
+ else
60
+ "Enqueued recurring task"
61
+ end
58
62
  log_event(event, :debug, action, **attributes)
59
63
  end
60
64
  end
@@ -1,3 +1,3 @@
1
1
  module RailsSemanticLogger
2
- VERSION = "5.0.0".freeze
2
+ VERSION = "5.1.0".freeze
3
3
  end
@@ -116,7 +116,7 @@ module RailsSemanticLogger
116
116
 
117
117
  # Swap an existing subscriber with a new one
118
118
  def self.swap_subscriber(old_class, new_class, notifier)
119
- subscribers = ActiveSupport::LogSubscriber.subscribers.select { |s| s.is_a?(old_class) }
119
+ subscribers = ActiveSupport::LogSubscriber.subscribers.grep(old_class)
120
120
  subscribers.each { |subscriber| unattach(subscriber) }
121
121
 
122
122
  new_class.attach_to(notifier)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_semantic_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '5.0'
46
+ version: '5.1'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '5.0'
53
+ version: '5.1'
54
54
  executables: []
55
55
  extensions: []
56
56
  extra_rdoc_files: []
@@ -76,7 +76,6 @@ files:
76
76
  - lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb
77
77
  - lib/rails_semantic_logger/extensions/mongoid/config.rb
78
78
  - lib/rails_semantic_logger/extensions/rails/server.rb
79
- - lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb
80
79
  - lib/rails_semantic_logger/options.rb
81
80
  - lib/rails_semantic_logger/rack/logger.rb
82
81
  - lib/rails_semantic_logger/sidekiq/defaults.rb
@@ -90,7 +89,8 @@ licenses:
90
89
  metadata:
91
90
  bug_tracker_uri: https://github.com/reidmorrison/rails_semantic_logger/issues
92
91
  documentation_uri: https://logger.rocketjob.io
93
- source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v5.0.0
92
+ source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v5.1.0
93
+ changelog_uri: https://github.com/reidmorrison/rails_semantic_logger/blob/main/CHANGELOG.md
94
94
  rubygems_mfa_required: 'true'
95
95
  rdoc_options: []
96
96
  require_paths:
@@ -1,87 +0,0 @@
1
- # Sidekiq patches
2
- if Sidekiq::VERSION.to_i == 4
3
- require "sidekiq/logging"
4
- require "sidekiq/middleware/server/logging"
5
- require "sidekiq/processor"
6
- elsif Sidekiq::VERSION.to_i == 5
7
- require "sidekiq/logging"
8
- end
9
-
10
- module Sidekiq
11
- # Sidekiq v4 & v5
12
- if defined?(::Sidekiq::Logging)
13
- # Replace Sidekiq logging context
14
- module Logging
15
- def self.with_context(msg, &)
16
- SemanticLogger.tagged(msg, &)
17
- end
18
-
19
- def self.job_hash_context(job_hash)
20
- h = {jid: job_hash["jid"], class: job_hash["wrapped"] || job_hash["class"]}
21
- h[:bid] = job_hash["bid"] if job_hash["bid"]
22
- h[:queue] = job_hash["queue"] if job_hash["queue"]
23
- h
24
- end
25
- end
26
- end
27
-
28
- # Sidekiq v4
29
- if defined?(::Sidekiq::Middleware::Server::Logging)
30
- # Convert string to machine readable format
31
- class Processor
32
- def log_context(job_hash)
33
- h = {jid: job_hash["jid"], class: job_hash["wrapped"] || job_hash["class"]}
34
- h[:bid] = job_hash["bid"] if job_hash["bid"]
35
- h[:queue] = job_hash["queue"] if job_hash["queue"]
36
- h
37
- end
38
- end
39
-
40
- # Let Semantic Logger handle duration logging
41
- module Middleware
42
- module Server
43
- class Logging
44
- # rubocop:disable Style/ExplicitBlockArgument
45
- def call(worker, item, queue)
46
- SemanticLogger.tagged(queue: queue) do
47
- if perform_messages_enabled?
48
- worker.logger.info(
49
- "Start #perform",
50
- metric: "sidekiq.queue.latency",
51
- metric_amount: job_latency_ms(item)
52
- )
53
-
54
- worker.logger.measure_info(
55
- "Completed #perform",
56
- on_exception_level: :error,
57
- log_exception: :full,
58
- metric: "sidekiq.job.perform"
59
- ) { yield }
60
- else
61
- yield
62
- end
63
- end
64
- end
65
-
66
- def perform_messages_enabled?
67
- RailsSemanticLogger::Sidekiq::JobLogger.perform_messages != false
68
- end
69
-
70
- def job_latency_ms(job)
71
- return unless job && job["enqueued_at"]
72
-
73
- enqueued_at = job["enqueued_at"]
74
- if enqueued_at.is_a?(Float)
75
- # Sidekiq <= 7: seconds since epoch
76
- (Time.now.to_f - enqueued_at) * 1000
77
- else
78
- # Sidekiq 8+: milliseconds since epoch
79
- now = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
80
- now - enqueued_at
81
- end
82
- end
83
- end
84
- end
85
- end
86
- end
87
- end