Sidekiq: Show arguments passed to the worker on the logs

Created on 17 Jun 2014  路  10Comments  路  Source: mperham/sidekiq

Currently the logs are being displayed like this:

2014-06-17T21:17:02Z 16566 TID-ospmsznzk MyWorker JID-474923f4986cb3dc0a00a007 INFO: start
2014-06-17T21:17:03Z 16566 TID-ospmsznzk MyWorker JID-474923f4986cb3dc0a00a007 INFO: done: 1.491 sec

I would like to show which are the arguments that have been passed to the worker on that execution. So it will be much easier to debug on production
Example:

2014-06-17T21:17:02Z 16566 TID-ospmsznzk MyWorker with { first: 'some value', second: 'another value' } JID-474923f4986cb3dc0a00a007 INFO: start
2014-06-17T21:17:03Z 16566 TID-ospmsznzk MyWorker JID-474923f4986cb3dc0a00a007 INFO: done: 1.491 sec

Based on what I understood from the wiki (https://github.com/mperham/sidekiq/wiki/Logging) the only way of modifying the logging message is replacing the Formatter:

Sidekiq.logger.formatter = MyFormatter.new

But It doesn't receives the actual arguments, only (severity, time, program_name, message):
lib/sidekiq/logging.rb

    class Pretty < Logger::Formatter
      # Provide a call() method that returns the formatted message.
      def call(severity, time, program_name, message)
        "#{time.utc.iso8601} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
      end

      def context
        c = Thread.current[:sidekiq_context]
        c ? " #{c}" : ''
      end
    end

Is there any way of doing this? I found that probably some work can be done on this other class lib/sidekiq/middleware/server/logging.rb where item contains exactly what I need, but it's not being passed to Sidekiq::Logging where I can override the Sidekiq.logger.formatter:

        def call(worker, item, queue)
          Sidekiq::Logging.with_context("#{worker.class.to_s} JID-#{item['jid']}") do
            begin
              start = Time.now
              logger.info { "start" }
              yield
              logger.info { "done: #{elapsed(start)} sec" }
            rescue Exception
              logger.info { "fail: #{elapsed(start)} sec" }
              raise
            end
          end
        end

Any suggestion?
Thanks

Most helpful comment

FWIW, here is what I added to my config/initializers/sidekiq.rb for Sidekiq >= 5.0.3

require 'sidekiq'
require 'sidekiq/logging'

module Sidekiq
  module Logging
    # override existing log to include the arguments passed to `perform`
    def self.job_hash_context(job_hash)
      # If we're using a wrapper class, like ActiveJob, use the "wrapped"
      # attribute to expose the underlying thing.
      klass = job_hash['wrapped'.freeze] || job_hash["class".freeze]
      bid = job_hash['bid'.freeze]
      args = job_hash['args'.freeze]
      "#{klass} #{" ARGS-#{args}" if args} JID-#{job_hash['jid'.freeze]}#{" BID-#{bid}" if bid}"
    end
  end
end

All 10 comments

There are several gems which modify Sidekiq's logger one way or another. I'd suggest looking around at a few (gem list -r sidekiq) as I have no plans to add this to Sidekiq core.

@ezilocchi, which gem did you end up using, or did you customize log message when server picks up a job to include args?

FYI warning includes: args in log

2015-03-30T02:03:44.212Z 3 TID-or9azxdfo WARN: {"retry"=>false, "queue"=>"data_default", "class"=>"Bam", "args"=>["54ed06db6235000"], "jid"=>"ecda47e40ee045fc8452239a", "enqueued_at"=>1426933296.3268554}

@khoan To be honest with you, I don't remember quite well. I think that I followed a different approach. And thank you very much, I couldn't find it when I was working on it.

@ezilocchi that's cool. thanks.

For anyone stumbling across this like me, here's one way you can do it:

require 'sidekiq'
require 'sidekiq/middleware/server/logging'

class ParamsLogging < Sidekiq::Middleware::Server::Logging
  def log_context(worker, item)
    klass = item['wrapped'.freeze] || worker.class.to_s
    "#{klass} (#{item['args'].try(:join, ' ')}) JID-#{item['jid'.freeze]}"
  end
end

Sidekiq.configure_server do |config|
  config.server_middleware do |chain|
    chain.remove Sidekiq::Middleware::Server::Logging
    chain.add ParamsLogging
  end
end

Note that I'm using ActiveSupport's try in there for some extra safety (I'm not sure if args is ever a nil or other type), so make sure you either have that available or just remove it if I'm wrong.

A small tweak to @timdorr's patch - this relies on super to help ensure you'll pick up future changes to Sidekiq's server logging. In this case including the "BID" in the logging.
https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/middleware/server/logging.rb

require 'sidekiq'
require 'sidekiq/middleware/server/logging'

class ParamsLogging < Sidekiq::Middleware::Server::Logging
  def log_context(worker, item)
    if item['args']
      "#{super} (#{item['args'].map(&:inspect).join(' ')})"
    else
      super
    end
  end
end

Sidekiq.configure_server do |config|
  config.server_middleware do |chain|
    chain.remove Sidekiq::Middleware::Server::Logging
    chain.add ParamsLogging
  end
end

@Empact 's changes above no longer work with 5.0. Does anyone have a fix for 5.0?

@heironimus You can override Sidekiq::Logging.job_hash_context, see #3463

FWIW, here is what I added to my config/initializers/sidekiq.rb for Sidekiq >= 5.0.3

require 'sidekiq'
require 'sidekiq/logging'

module Sidekiq
  module Logging
    # override existing log to include the arguments passed to `perform`
    def self.job_hash_context(job_hash)
      # If we're using a wrapper class, like ActiveJob, use the "wrapped"
      # attribute to expose the underlying thing.
      klass = job_hash['wrapped'.freeze] || job_hash["class".freeze]
      bid = job_hash['bid'.freeze]
      args = job_hash['args'.freeze]
      "#{klass} #{" ARGS-#{args}" if args} JID-#{job_hash['jid'.freeze]}#{" BID-#{bid}" if bid}"
    end
  end
end

For Sidekiq 6, add the following in config/initializers/sidekiq.rb (@heironimus' snippet above doesn't work anymore):

# Monkey patching Sidekiq logs
module Sidekiq
  class JobLogger

    # For a perform method with args param1 and param2, this will append in the
    # logs,
    #
    # ...  args=(param1=a, param2=b) INFO: start
    #
    def job_hash_context(job_hash)
      # If we're using a wrapper class, like ActiveJob, use the "wrapped"
      # attribute to expose the underlying thing.
      hash = {
        class: job_hash["wrapped"] || job_hash["class"],
        jid: job_hash["jid"],
      }
      hash[:bid] = job_hash["bid"] if job_hash["bid"]
      hash[:tags] = job_hash["tags"] if job_hash["tags"]

      if job_hash["args"].present?
        parameters = hash[:class].constantize.new.method(:perform).parameters
        params_and_values = parameters.each_with_object([]) { |param_keys, params_and_values|
          value = job_hash["args"][params_and_values.count]

          # In case it's too long of a value, truncate it.
          value = "[omitted]" if value.to_s.size > 15

          params_and_values << "#{param_keys[1]}=#{value}"
          params_and_values
        }.join(", ")

        hash[:args] = "(#{params_and_values})"
      end

      hash
    end
  end
end
Was this page helpful?
0 / 5 - 0 ratings