Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread-safe: Capturing the output of $stdout

I wonder how to capture the output of $stdout in a threaded environment in ruby.

A few details. I use the capture for logging purpose and I have sidekiq to process jobs in the background so the threads. Ive coded:

@previous_stdout, $stdout = $stdout, StringIO.new
self.stdout = $stdout.string

It throws me (for certain threads):

WARN: undefined method `string' for #<IO:<STDOUT>>
like image 910
Hartator Avatar asked Oct 17 '25 02:10

Hartator


1 Answers

If you are logging things yourself, keep a StringIO log per job and write to it:

@log = StringIO.new
@log.write 'debug message'

Sidekiq also offers logging options:

https://github.com/mperham/sidekiq/wiki/Logging

Otherwise you could try something real hacky like overwriting the printing methods on Kernel and synchronizing it so you never accidentally write to the wrong $stdout. Something like:

require 'stringio'

module Kernel
  @@io_semaphore = Mutex.new

  [ :printf, :p, :print, :puts ].each do |io_write|
    hidden_io_write = "__#{io_write}__"

    alias_method hidden_io_write, io_write

    define_method(io_write) do |*args|
      @@io_semaphore.synchronize do
        $stdout = Thread.current[:log] || STDOUT
        self.__send__(hidden_io_write, *args)
        $stdout = STDOUT
      end
    end
  end
end

threads = 3.times.map do
  Thread.new do
    Thread.current[:log] = log = StringIO.new
    sleep(rand)
    puts "testing..."
    log.string
  end
end

logs = threads.map(&:value)
p logs
# => ["testing...\n", "testing...\n", "testing...\n"]
like image 79
AJcodez Avatar answered Oct 18 '25 19:10

AJcodez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!