I already discovered that I can redirect $stdout just by pointing it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a pile of
trace data. This is very similar to the Unix program ‘tee’
I already discovered that I can redirect $stdout just by pointing
it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a
pile of
trace data. This is very similar to the Unix program ‘tee’
Any suggestions?
Sure. I showed how to replace $stdout in a post earlier today:
I already discovered that I can redirect $stdout just by pointing it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a pile of
trace data. This is very similar to the Unix program ‘tee’
Any suggestions?
Funny, I was just writing this code last night for the latest release
of ci_reporter. Have a look at the CI::Reporter::OutputCapture class
at line 11, and its usage on line 50:
I realize this is not what you are asking, but in the interest of
self-promotion (gag), here’s a Ruby app I wrote that functions as a
replacement for ‘tee’
The only changed section is then:
Also, in your invocation on line 50, is {|io| $stdio = io} only used for
resetting $stdout back to normal. Nothing else?
For the initial replacement as well as the resetting back to normal,
see the #initialize method where it is also used.
Seems like your mutation to use a @@class var should work fine.
Thanks Nick,
This is almost what I want. I want to MERGE $stdout, $stderr and my
debugging output. If I change @captured_io to @@captured_io and move it
outside Initialize, does everything else stay the same?
The only changed section is then:
class OutputCapture < DelegateClass(IO)
def initialize(io, &assign)
super @delegate_io = io @assign_block = assign @assign_block.call self
end
@@captured_io = StringIO.new
Also, in your invocation on line 50, is {|io| $stdio = io} only used for
resetting $stdout back to normal. Nothing else?
I already discovered that I can redirect $stdout just by
pointing it at a new file handle. But what I really want is
to have the output continue to STDOUT but ALSO go into my
logging file along with a pile of trace data. This is very
similar to the Unix program ‘tee’
Here’s the code of my own log library that does exactly what
you want.
[“$stdout”, “$stderr”].each do |std|
io = eval(std)
old_write = io.method(:write)
class << io
self
end.module_eval do
define_method(:write) do |text|
unless text =~ /^[\r\n]+$/ # Because puts calls twice.
File.open(“logfile.log”, “a”) do |f|
f.puts [std[1…-1].upcase, caller[2], text].join(" ")
end
end
old_write.call(text)
end
end
end
$stdout.puts “text on stdout”
$stderr.puts “text on stderr”
The last expression in a block is the value of that block. In the
above, ‘self’ is used to ‘return’ the singleton class that was opened
with “class << io”.
Various libraries have code like:
class Object
def singleton_class
class << self
self
end
end
end
abstracting the somewhat odd notation. With this, the original code
could be written as:
That’s a very elegant solution. Does IO funnel all it’s
writes through “write”? Including things like putc? And where
would I look to find this out? Is it only in the source code?
AFAIK, yes. Yes. In the source code. Probably.
Last question: what does “self” do on the line below “class
<< io”
The common way to define a method of an object is this:
class << an_object
def method
# …
end
end
The problem is scope: variables declared before “class <<
an_object” aren’t available within the “class << an_object”
block. (And variables declared before “def method” aren’t
available within the “def method” block. But that can be worked
around by using “define_method(method)”.) Defining a method
both within the context of “an_object” and within the local
scope of variables, can be achieved with this:
class << an_object
self
end.module_eval do
define_method(method) do
# …
end
end
["$stdout", “$stderr”].each do |std|
io = eval(std)
old_write = io.method(:write)
class << io
self
end.module_eval do
old_write.call(text)
Thanks Eric,
That’s a very elegant solution. Does IO funnel all it’s writes through
“write”? Including things like putc? And where would I look to find this
out? Is it only in the source code?
Last question: what does “self” do on the line below “class << io”
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.