2013-04-24 2 views
1

$stdout의 출력을 어떻게 루비의 스레드 환경에서 캡처하는지 궁금합니다.

몇 가지 세부 정보. 로깅 목적으로 캡처를 사용하고 백그라운드에서 스레드를 처리하기 위해 sidekiq이 있습니다. 필자는 코딩 :

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

그것은 (특정 스레드) 저를 던졌습니다 : 당신이 일을 직접 로그인하는 경우

WARN: undefined method `string' for #<IO:<STDOUT>> 
+0

각 작업에 대해 다른 표준 출력을 사용하려고합니까? – AJcodez

+0

먼저. 당신의 오류는 현재'$ stdout'이'StringIO'가 아닌 일반'IO' 객체이기 때문에'string' 메소드가 없다는 것입니다. 둘째, 스레드로 작업 할 때 전역 변수 ($ stdout)를 사용하면 안됩니다. –

+0

실제로 $ stdout은 특정 스레드에 대해서는 StringIO이지만 모든 스레드에 대해서는 그렇지 않습니다. 예를 들어 스레딩 문제이며 각 작업에 대한 표준 출력을 얻으려고합니다. 어떤 생각? – Hartator

답변

4

는, 작업 당있는 StringIO 로그를 유지하고 쓰기 :

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

Sidekiq는 로깅 옵션도 제공합니다.

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

그렇지 않으면 실수로 잘못된 $ stdout에 쓸 수 없도록 커널에서 인쇄 방법을 덮어 쓰고 동기화하는 것과 같은 실제 해킹을 시도 할 수 있습니다. 다음과 같이하십시오 :

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"]