Thỉnh thoảng giống như chức năng tee trong bộ ghi.Làm thế nào tôi có thể có bản ghi đầu ra của trình ghi nhật ký của ruby để stdout cũng như tệp?
Trả lời
Bạn có thể viết một lớp giả IO
sẽ ghi vào nhiều đối tượng IO
. Một cái gì đó như:
class MultiIO
def initialize(*targets)
@targets = targets
end
def write(*args)
@targets.each {|t| t.write(*args)}
end
def close
@targets.each(&:close)
end
end
Sau đó thiết lập đó dưới dạng file log của bạn:
log_file = File.open("log/debug.log", "a")
Logger.new MultiIO.new(STDOUT, log_file)
Mỗi lần Logger
cuộc gọi puts
trên đối tượng MultiIO
của bạn, nó sẽ viết thư cho cả STDOUT
và log file của bạn.
Chỉnh sửa: Tôi đã tiếp tục và tìm ra phần còn lại của giao diện. Thiết bị đăng nhập phải trả lời write
và close
(không phải puts
). Miễn là MultiIO
phản hồi chúng và ủy quyền chúng tới các đối tượng IO thực, điều này sẽ hoạt động.
nếu bạn xem các ctor của logger bạn sẽ thấy rằng điều này sẽ mess up đăng nhập xoay. 'def initialize (log = nil, opt = {}) @dev = @filename = @shift_age = @shift_size = nil @mutex = LogDeviceMutex.new nếu log.respond_to? (: Write) và log.respond_to? (: đóng) \t @dev = log else \t @dev = open_logfile (nhật ký) \t @dev.sync = true \t @filename = log \t @shift_age = opt [: shift_age] || 7 \t @shift_size = opt [: shift_size] || 1048576 kết thúc kết thúc' – JeffCharter
Lưu ý trong Ruby 2.2, '@ targets.each (&: close)' được khấu hao. – xis
@ Giải pháp của David rất tốt. Tôi đã thực hiện một lớp đại biểu chung cho nhiều mục tiêu dựa trên mã của mình.
require 'logger'
class MultiDelegator
def initialize(*targets)
@targets = targets
end
def self.delegate(*methods)
methods.each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
self
end
class <<self
alias to new
end
end
log_file = File.open("debug.log", "a")
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
Bạn có thể giải thích, làm thế nào điều này là tốt hơn hoặc những gì được tăng cường tiện ích của phương pháp này hơn so với đồng bằng một đề nghị của David –
Đó là tách mối quan tâm. MultiDelegator chỉ biết về việc ủy quyền các cuộc gọi đến nhiều mục tiêu. Thực tế là thiết bị ghi nhật ký cần viết và phương thức đóng được thực hiện trong trình gọi. Điều này làm cho MultiDelegator có thể sử dụng được trong các tình huống khác ngoài ghi nhật ký. – jonas054
Giải pháp đẹp. – Lykos
Bạn cũng có thể thêm nhiều chức năng thiết bị khai thác gỗ trực tiếp vào Logger:
require 'logger'
class Logger
# Creates or opens a secondary log file.
def attach(name)
@logdev.attach(name)
end
# Closes a secondary log file.
def detach(name)
@logdev.detach(name)
end
class LogDevice # :nodoc:
attr_reader :devs
def attach(log)
@devs ||= {}
@devs[log] = open_logfile(log)
end
def detach(log)
@devs ||= {}
@devs[log].close
@devs.delete(log)
end
alias_method :old_write, :write
def write(message)
old_write(message)
@devs ||= {}
@devs.each do |log, dev|
dev.write(message)
end
end
end
end
Ví dụ:
logger = Logger.new(STDOUT)
logger.warn('This message goes to stdout')
logger.attach('logfile.txt')
logger.warn('This message goes both to stdout and logfile.txt')
logger.detach('logfile.txt')
logger.warn('This message goes just to stdout')
Dưới đây là một thực hiện, lấy cảm hứng từ @ jonas054 's câu trả lời .
Điều này sử dụng mẫu tương tự như Delegator
. Bằng cách này bạn không cần phải liệt kê tất cả những phương pháp bạn muốn giao phó, vì nó sẽ ủy thác tất cả các phương pháp được quy định tại một trong các đối tượng mục tiêu:
class Tee < DelegateToAllClass(IO)
end
$stdout = Tee.new(STDOUT, File.open("#{__FILE__}.log", "a"))
Bạn sẽ có thể sử dụng điều này với Logger như tốt.
delegate_to_all.rb có sẵn từ đây: https://gist.github.com/TylerRick/4990898
Điều này có vẻ giống như một cách siêu thanh lịch để giải quyết vấn đề. –
Trong khi tôi khá giống như những gợi ý khác, tôi thấy tôi có vấn đề này tương tự nhưng muốn khả năng có mức độ khai thác gỗ khác nhau cho thiết bị lỗi chuẩn và tập tin (như tôi có thể với các khung khai thác lớn hơn như NLog). Tôi đã kết thúc với một chiến lược định tuyến mà nhiều thành ở cấp logger chứ không phải ở cấp IO, để mỗi logger có thể sau đó hoạt động ở log-mức độc lập:
class MultiLogger
def initialize(*targets)
@targets = targets
end
%w(log debug info warn error fatal unknown).each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
end
$stderr_log = Logger.new(STDERR)
$file_log = Logger.new(File.open('logger.log','a'))
$stderr_log.level = Logger::INFO
$file_log.level = Logger::DEBUG
$log = MultiLogger.new($stderr_log, $file_log)
Tôi đã đi đến ý tưởng tương tự của "ủy thác tất cả các phương thức cho các phần tử con "mà những người khác đã khám phá, nhưng đang trả về cho mỗi giá trị trả về của cuộc gọi cuối cùng của phương thức. Nếu không, nó đã phá vỡ logger-colors
đang mong đợi một Integer
và bản đồ đã trả về một Array
.
class MultiIO
def self.delegate_all
IO.methods.each do |m|
define_method(m) do |*args|
ret = nil
@targets.each { |t| ret = t.send(m, *args) }
ret
end
end
end
def initialize(*targets)
@targets = targets
MultiIO.delegate_all
end
end
Điều này sẽ sắp xếp lại mọi phương thức cho tất cả các mục tiêu và chỉ trả về giá trị trả lại của cuộc gọi cuối cùng.
Ngoài ra, nếu bạn muốn màu sắc, STDOUT hoặc STDERR phải được đặt cuối cùng, vì đó là hai chỉ có màu sắc được cho là đầu ra. Nhưng sau đó, nó cũng sẽ xuất ra màu sắc cho tệp của bạn.
logger = Logger.new MultiIO.new(File.open("log/test.log", 'w'), STDOUT)
logger.error "Roses are red"
logger.unknown "Violets are blue"
Tôi đã viết một RubyGem nhỏ mà cho phép bạn làm một số các điều:
# Pipe calls to an instance of Ruby's logger class to $stdout
require 'teerb'
log_file = File.open("debug.log", "a")
logger = Logger.new(TeeRb::IODelegate.new(log_file, STDOUT))
logger.warn "warn"
$stderr.puts "stderr hello"
puts "stdout hello"
Bạn có thể tìm mã trên github: teerb
Tôi nghĩ STDOUT bạn được sử dụng cho thông tin thời gian chạy quan trọng và lỗi được nêu ra.
Vì vậy, tôi sử dụng
$log = Logger.new('process.log', 'daily')
để đăng nhập debug và khai thác gỗ thông thường, và sau đó viết một vài
puts "doing stuff..."
nơi mà tôi cần để xem thông tin STDOUT rằng kịch bản của tôi đã chạy ở tất cả!
Bah, chỉ cần tôi 10 cent :-)
Nếu bạn đang ở trong Rails 3 hoặc 4, như this blog post points out, Rails 4 has this functionality built in. Vì vậy, bạn có thể làm:
# config/environment/production.rb
file_logger = Logger.new(Rails.root.join("log/alternative-output.log"))
config.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
Hoặc nếu bạn đang on Rails 3, bạn có thể backport nó:
# config/initializers/alternative_output_log.rb
# backported from rails4
module ActiveSupport
class Logger < ::Logger
# Broadcasts logs to multiple loggers. Returns a module to be
# `extended`'ed into other logger instances.
def self.broadcast(logger)
Module.new do
define_method(:add) do |*args, &block|
logger.add(*args, &block)
super(*args, &block)
end
define_method(:<<) do |x|
logger << x
super(x)
end
define_method(:close) do
logger.close
super()
end
define_method(:progname=) do |name|
logger.progname = name
super(name)
end
define_method(:formatter=) do |formatter|
logger.formatter = formatter
super(formatter)
end
define_method(:level=) do |level|
logger.level = level
super(level)
end
end
end
end
end
file_logger = Logger.new(Rails.root.join("log/alternative-output.log"))
Rails.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
là điều này có thể áp dụng bên ngoài đường ray hay chỉ đường ray? –
Nó dựa trên ActiveSupport, vì vậy nếu bạn đã có sự phụ thuộc đó, bạn có thể 'mở rộng' bất kỳ trường hợp' ActiveSupport :: Logger' nào như được hiển thị ở trên. – phillbaker
Cảm ơn, nó rất hữu ích. – Lucas
Thêm một cách. Nếu bạn đang sử dụng tagged khai thác gỗ và cần thẻ trong logfile khác là tốt, bạn có thể làm điều đó theo cách này
# backported from rails4
# config/initializers/active_support_logger.rb
module ActiveSupport
class Logger < ::Logger
# Broadcasts logs to multiple loggers. Returns a module to be
# `extended`'ed into other logger instances.
def self.broadcast(logger)
Module.new do
define_method(:add) do |*args, &block|
logger.add(*args, &block)
super(*args, &block)
end
define_method(:<<) do |x|
logger << x
super(x)
end
define_method(:close) do
logger.close
super()
end
define_method(:progname=) do |name|
logger.progname = name
super(name)
end
define_method(:formatter=) do |formatter|
logger.formatter = formatter
super(formatter)
end
define_method(:level=) do |level|
logger.level = level
super(level)
end
end # Module.new
end # broadcast
def initialize(*args)
super
@formatter = SimpleFormatter.new
end
# Simple formatter which only displays the message.
class SimpleFormatter < ::Logger::Formatter
# This method is invoked when a log event occurs
def call(severity, time, progname, msg)
element = caller[4] ? caller[4].split("/").last : "UNDEFINED"
"#{Thread.current[:activesupport_tagged_logging_tags]||nil } # {time.to_s(:db)} #{severity} #{element} -- #{String === msg ? msg : msg.inspect}\n"
end
end
end # class Logger
end # module ActiveSupport
custom_logger = ActiveSupport::Logger.new(Rails.root.join("log/alternative_#{Rails.env}.log"))
Rails.logger.extend(ActiveSupport::Logger.broadcast(custom_logger))
Sau đó bạn sẽ nhận được thẻ uuid trong logger thay thế
["fbfea87d1d8cc101a4ff9d12461ae810"] 2015-03-12 16:54:04 INFO logger.rb:28:in `call_app' --
["fbfea87d1d8cc101a4ff9d12461ae810"] 2015-03-12 16:54:04 INFO logger.rb:31:in `call_app' -- Started POST "/psp/entrypoint" for 192.168.56.1 at 2015-03-12 16:54:04 +0700
Hope giúp ai đó.
Đối với những người thích nó đơn giản:
log = Logger.new("| tee test.log") # note the pipe ('|')
log.info "hi" # will log to both STDOUT and test.log
Hoặc in thông điệp trong định dạng Logger:
log = Logger.new("test.log")
log.formatter = proc do |severity, datetime, progname, msg|
puts msg
msg
end
log.info "hi" # will log to both STDOUT and test.log
Tôi đang thực sự sử dụng kỹ thuật này để in ra một tệp nhật ký, dịch vụ nhật ký đám mây (logentries) và nếu môi trường dev của nó - cũng in tới STDOUT.
Bạn có bị hạn chế đối với bộ ghi chuẩn không?
Nếu không bạn có thể sử dụng log4r:
require 'log4r'
LOGGER = Log4r::Logger.new('mylog')
LOGGER.outputters << Log4r::StdoutOutputter.new('stdout')
LOGGER.outputters << Log4r::FileOutputter.new('file', :filename => 'test.log') #attach to existing log-file
LOGGER.info('aa') #Writs on STDOUT and sends to file
Một ưu điểm: Bạn cũng có thể xác định log-cấp độ khác nhau cho stdout và tập tin.
câu trả lời của @ jonas054 ở trên rất tuyệt, nhưng nó gây ô nhiễm cho lớp MultiDelegator
với mọi đại biểu mới. Nếu bạn sử dụng MultiDelegator
nhiều lần, nó sẽ tiếp tục thêm phương thức vào lớp, điều này là không mong muốn. (Xem dưới đây ví dụ)
Đây là cùng một triển khai, nhưng sử dụng các lớp ẩn danh để các phương thức không gây ô nhiễm cho lớp người ủy nhiệm.
class BetterMultiDelegator
def self.delegate(*methods)
Class.new do
def initialize(*targets)
@targets = targets
end
methods.each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
class <<self
alias to new
end
end # new class
end # delegate
end
Dưới đây là một ví dụ về tình trạng ô nhiễm phương pháp với việc thực hiện ban đầu, tương phản với việc thực hiện chỉnh sửa:
tee = MultiDelegator.delegate(:write).to(STDOUT)
tee.respond_to? :write
# => true
tee.respond_to? :size
# => false
Tất cả là tốt ở trên. tee
có phương thức write
nhưng không có phương thức size
như mong đợi. Bây giờ, hãy xem xét khi chúng ta tạo ra một đại biểu:
tee2 = MultiDelegator.delegate(:size).to("bar")
tee2.respond_to? :size
# => true
tee2.respond_to? :write
# => true !!!!! Bad
tee.respond_to? :size
# => true !!!!! Bad
Ồ không, tee2
đáp ứng size
như mong đợi, nhưng nó cũng đáp ứng write
vì các đại biểu đầu tiên. Ngay cả tee
giờ phản hồi lại size
do ô nhiễm phương pháp.
Contrast này vào dung dịch lớp nặc danh, tất cả mọi thứ được như mong đợi:
see = BetterMultiDelegator.delegate(:write).to(STDOUT)
see.respond_to? :write
# => true
see.respond_to? :size
# => false
see2 = BetterMultiDelegator.delegate(:size).to("bar")
see2.respond_to? :size
# => true
see2.respond_to? :write
# => false
see.respond_to? :size
# => false
Thêm một lựa chọn ;-)
require 'logger'
class MultiDelegator
def initialize(*targets)
@targets = targets
end
def method_missing(method_sym, *arguments, &block)
@targets.each do |target|
target.send(method_sym, *arguments, &block) if target.respond_to?(method_sym)
end
end
end
log = MultiDelegator.new(Logger.new(STDOUT), Logger.new(File.open("debug.log", "a")))
log.info('Hello ...')
Nhanh chóng và bẩn (ref: https://coderwall.com/p/y_b3ra/log-to-stdout-and-a-file-at-the-same-time)
require 'logger'
ll=Logger.new('| tee script.log')
ll.info('test')
http://stackoverflow.com/a/34026278/322020 – Nakilon
Tôi thích phương thức tiếp cận MultiIO. Nó hoạt động tốt với Ruby Logger. Nếu bạn sử dụng IO thuần túy nó ngừng hoạt động vì thiếu một số phương thức mà đối tượng IO được mong đợi có. Ống đã được đề cập trước đây: How can I have ruby logger log output to stdout as well as file?. Đây là những gì làm việc tốt nhất cho tôi.
def watch(cmd)
output = StringIO.new
IO.popen(cmd) do |fd|
until fd.eof?
bit = fd.getc
output << bit
$stdout.putc bit
end
end
output.rewind
[output.read, $?.success?]
ensure
output.close
end
result, success = watch('./my/shell_command as a String')
Note Tôi biết điều này không trả lời câu hỏi trực tiếp nhưng nó có liên quan mạnh mẽ. Bất cứ khi nào tôi tìm kiếm đầu ra cho nhiều IO tôi đã đi qua thread.So này, tôi hy vọng bạn tìm thấy điều này hữu ích quá.
- 1. Tôi có thể tắt tiêu đề nhật ký cho trình ghi nhật ký ruby không?
- 2. Ghi nhật ký Java - cách chuyển hướng đầu ra sang tệp nhật ký tùy chỉnh cho trình ghi nhật ký?
- 3. Lỗi ghi nhật ký có lập trình Với Elmah: Ghi nhật ký thông tin cụ thể
- 4. Cách bật ghi nhật ký của Ehcache
- 5. Tôi có thể ghi tệp nhật ký log4net của mình vào dưới ClickOnce ở đâu?
- 6. Ghi nhật ký Java - tệp nhật ký của tôi ở đâu?
- 7. Sử dụng tính năng ghi nhật ký in đầu ra của pprint
- 8. Ghi nhật ký toàn cầu của Python
- 9. Làm cách nào để ghi trực tiếp vào luồng StdOut của chương trình trong C#?
- 10. Làm cách nào để có được google guice để chèn trình ghi nhật ký tùy chỉnh, hãy nói nhật ký ghi nhật ký hoặc log4j
- 11. Ghi nhật ký servcat của Tomcat
- 12. Làm cách nào tôi có thể bật ghi nhật ký WCF để ghi vào Cơ sở dữ liệu?
- 13. Ghi nhật ký Tomcat của Azure Java
- 14. PowerShell: đọc nhật ký Bản ghi PowerShell
- 15. Nhật ký bản ghi "chính" của TFS 2010 ở đâu?
- 16. Làm thế nào để lưu đầu ra bản ghi NAnt vào một tệp?
- 17. Cách thay đổi màu của đầu ra ghi nhật ký log4j
- 18. Làm thế nào để ghi đè lên stdout trong C
- 19. ghi đầu ra của print_r trong một tệp txt. PHP
- 20. Ghi nhật ký hệ thống tệp Android
- 21. Ghi nhật ký bằng Python - kiểm tra vị trí của tệp nhật ký?
- 22. Đầu ra nhật ký trùng lặp khi sử dụng mô-đun ghi nhật ký Python
- 23. ghi nhật ký log4net không tạo tệp nhật ký
- 24. Trình ghi nhật ký Ruby chuẩn có được định cấu hình để xóa sau mỗi thư không?
- 25. Ghi nhật ký Python.DEBUG không ghi nhật ký
- 26. Làm cách nào để ghi lại toàn bộ dấu vết của ngoại lệ Ruby bằng trình ghi nhật ký Rails mặc định?
- 27. Cách định cấu hình cấp nhật ký của lớp cụ thể trong ghi nhật ký GWT?
- 28. Làm cách nào để in ra các thông điệp tường trình cho một trình ghi nhật ký cụ thể?
- 29. đầu ra đường ống của quy trình con.Chọn các tệp
- 30. Ghi nhật ký xoắn
Thêm '| tee' trước khi các tập tin làm việc cho tôi, vì vậy 'Logger.new ("| tee test.log") '. ** Lưu ý đường ống. ** Đây là từ mẹo trên https://coderwall.com/p/y_b3ra/log-to-stdout-and-a-file-at-the-same-time – mjwatts