De-Interleaving logger in Rails
class DeinterleavingLogger < ActiveSupport::BufferedLogger
def initialize(*args)
super
@writers = Hash.new
@writer_guard = Mutex.new
end
def add_writer
@writers[Thread.current] ||= Hash.new
@writers[Thread.current][Fiber.current] ||= Array.new
end
def flush_writer
return unless current_queue
@writer_guard.synchronize { current_queue.each { |args, block| add_without_deinterleaving *args, &block } }
@writers[Thread.current].delete Fiber.current
end
def add_with_deinterleaving(*args, &block)
if current_queue then
current_queue << [ args, block ]
else
add_without_deinterleaving *args, &block
end
end
alias_method_chain :add, :deinterleaving
private
def current_queue
@writers[Thread.current].try(:[], Fiber.current)
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
Rails.logger.add_writer
status, headers, response = @app.call(env)
Rails.logger.flush_writer
return [ status, headers, response ]
end
end
end
If you use something like Rack::FiberPool, you'll find your server logs interleaved due to parallelization of requests. This middleware and logger replacement will buffer each request's logs until the request is finished, then dump it in an exclusive block.
To use, add the middleware after you've fibered your request, and replace config.logger with the DeinterleavingLogger.
Comments are only visible to Forrst members. Log in or Request an invite.
