我正在尝试将stdout复制到文件以进行日志记录。我还希望它显示在我正在使用的IDE的Ruby控制台中。
我将此代码插入到我的脚本中,并将$stdout
重定向到my.log文件:
$stdout.reopen("my.log", "w")
有没有人知道将$stdout
的内容复制到文件而不是将其重定向到文件的gem或技术?另外,我没有使用Rails只是Ruby。
答案 0 :(得分:9)
这样的事可能会对你有所帮助:
class TeeIO < IO
def initialize orig, file
@orig = orig
@file = file
end
def write string
@file.write string
@orig.write string
end
end
IO
中输出的大多数方法最终都使用write
,因此您只需要覆盖这一方法。您可以像这样使用它:
#setup
tee = TeeIO.new $stdout, File.new('out.txt', 'w')
$stdout = tee
# Now lots of example uses:
puts "Hello"
$stdout.puts "Extending IO allows us to expicitly use $stdout"
print "heres", :an, :example, "using", 'print', "\n"
48.upto(57) do |i|
putc i
end
putc 10 #newline
printf "%s works as well - %d\n", "printf", 42
$stdout.write "Goodbye\n"
在此示例之后,以下内容与两者控制台和文件相同:
Hello
Extending IO allows us to expicitly use $stdout
heresanexampleusingprint
0123456789
printf works as well - 42
Goodbye
我不会声称这种技术是失败证明,但它应该适用于stdout的简单使用。测试它以供您使用。
请注意,您不必在reopen
上使用$stdout
,除非您想要重定向子进程或不合作扩展的输出。简单地为其分配不同的IO
对象将适用于大多数用途。
RSpec命令行在之前引用$stdout
,您可以运行任何代码来重新分配它,因此这不起作用。 reopen
在这种情况下仍然可以正常工作,因为您正在更改$stdout
指向的实际对象和RSpec所具有的引用,但这并不会为您提供输出。
一种解决方案是像这样修补$stdout
:
$out_file = File.new('out.txt', 'w')
def $stdout.write string
$out_file.write string
super
end
这有效,但与所有猴子修补一样,要小心。使用操作系统的tee
命令会更安全。
答案 1 :(得分:4)
如果您使用的是Linux或Mac OS,操作系统中提供的tee
命令可以轻松完成此操作。从其手册页:
NAME
tee -- pipe fitting
SYNOPSIS
tee [-ai] [file ...]
DESCRIPTION
The tee utility copies standard input to standard output, making a copy in zero or more files. The output is unbuffered.
类似于:
echo '>foo bar' | tee tmp.out
>foo bar
将输出回显到STDOUT和文件。捕获文件给了我:
cat tmp.out
>foo bar
否则,如果您想在代码中执行此操作,则这是一项简单的任务:
def tee_output(logfile)
log_output = File.open(logfile, 'w+')
->(o) {
log_output.puts o
puts o
}
end
tee = tee_output('tmp.out')
tee.call('foo bar')
运行它:
>ruby test.rb
foo bar
检查输出文件:
>cat tmp.out
foo bar
我使用"w+"
将我的文件访问权限附加到输出文件,而不是覆盖它。
CAVEAT :这会打开文件,并在您调用tee_output
方法后的代码生命周期中将其保持打开状态。这让一些人感到困扰,但是,就个人而言,它并没有让我感到烦恼,因为Ruby会在脚本退出时关闭文件。一般情况下,我们希望在完成文件后立即关闭文件,但在您的代码中,打开它并保持打开更有意义,而不是重复打开和关闭输出文件,但您的里程可能会有所不同。
编辑:
对于Ruby 1.8.7,使用lambda
而不是新的->
语法:
def tee_output(logfile)
log_output = File.open(logfile, 'w+')
lambda { |o|
log_output.puts o
puts o
}
end
tee = tee_output('tmp.out')
tee.call('foo bar')
答案 2 :(得分:1)
我知道这是一个老问题,但我发现自己处于同样的境地。我编写了一个扩展File
并覆盖write
puts
和close
方法的Multi-IO类,我还确保其线程安全:
require 'singleton'
class MultiIO < File
include Singleton
@@targets = []
@@mutex = Mutex.new
def self.instance
self.open('/dev/null','w+')
end
def puts(str)
write "#{str}\n"
end
def write(str)
@@mutex.synchronize do
@@targets.each { |t| t.write str; flush }
end
end
def setTargets(targets)
raise 'setTargets is a one-off operation' unless @@targets.length < 1
targets.each do |t|
@@targets.push STDOUT.clone if t == STDOUT
@@targets.push STDERR.clone if t == STDERR
break if t == STDOUT or t == STDERR
@@targets.push(File.open(t,'w+'))
end
self
end
def close
@@targets.each {|t| f.close}
end
end
STDOUT.reopen MultiIO.instance.setTargets(['/tmp/1.log',STDOUT,STDERR])
STDERR.reopen STDOUT
threads = []
5.times.each do |i|
threads.push(
Thread.new do
10000.times.each do |j|
STDOUT.puts "out#{i}:#{j}"
end
end
)
end
5.times.each do |i|
threads.push(
Thread.new do
10000.times.each do |j|
STDERR.puts "err#{i}:#{j}"
end
end
)
end
threads.each {|t| t.join}
答案 3 :(得分:0)
来自matts的微小改进回答:
"""E"" is for evidence [sound recording] / by Sue Grafton." 05 8
"""F"" is for fugitive [sound recording] / by Sue Grafton." 05 6
"""G"" is for Grafton : the world of Kinsey Millhone / Natalie Hevener Kaufman and Carol McGinnis Kay." 06 1
"""G"" is for gumshoe [text (large print)] / Sue Grafton." 09,10 1
"""Galapagos"" means ""tortoises"" / written and illustrated by Ruth Heller." 10,04,09 2
"""Git on board 09 1
"""God's banker"" / by Rupert Cornwell." 05,10,11 1
"""Gospodi-- spasi i usmiri Rossi︠i︡u"" : Nikolaĭ II 10,11 1
"""H"" is for homicide [sound recording] / by Sue Grafton." 12 4