लकड़हारे में एक टी कार्यक्षमता की तरह।
लकड़हारे में एक टी कार्यक्षमता की तरह।
tee --append test.logओवरराइट को रोकने के लिए उपयोग करें ।
जवाबों:
आप एक छद्म IOवर्ग लिख सकते हैं जो कई IOवस्तुओं को लिखेगा । कुछ इस तरह:
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
फिर उसे अपनी लॉग फ़ाइल के रूप में सेट करें:
log_file = File.open("log/debug.log", "a")
Logger.new MultiIO.new(STDOUT, log_file)
हर बार अपनी वस्तु पर Loggerकॉल करने putsपर MultiIO, यह STDOUTआपकी और लॉग फाइल दोनों को लिख देगा ।
संपादित करें: मैंने आगे बढ़कर बाकी इंटरफ़ेस का पता लगाया। एक लॉग डिवाइस को जवाब देना चाहिए writeऔर close(नहीं puts)। जब तक MultiIOउन लोगों के प्रति प्रतिक्रिया और उन्हें असली IO ऑब्जेक्ट्स के लिए अनुमानित किया जाता है, तब तक यह काम करना चाहिए।
def initialize(log = nil, opt = {}) @dev = @filename = @shift_age = @shift_size = nil @mutex = LogDeviceMutex.new if log.respond_to?(:write) and log.respond_to?(:close) @dev = log else @dev = open_logfile(log) @dev.sync = true @filename = log @shift_age = opt[:shift_age] || 7 @shift_size = opt[:shift_size] || 1048576 end end
@targets.each(&:close)मूल्यह्रास है।
@ डेविड का समाधान बहुत अच्छा है। मैंने उनके कोड के आधार पर कई लक्ष्यों के लिए एक सामान्य प्रतिनिधि वर्ग बनाया है।
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)
यदि आप रेल 3 या 4 में हैं, जैसा कि इस ब्लॉग पोस्ट में बताया गया है, रेल 4 में यह कार्यक्षमता है । तो आप कर सकते हैं:
# config/environment/production.rb
file_logger = Logger.new(Rails.root.join("log/alternative-output.log"))
config.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
या यदि आप रेल 3 पर हैं, तो आप इसे वापस भेज सकते हैं:
# 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))
extendकिसी भी ActiveSupport::Loggerउदाहरण को ऊपर दिखाए गए अनुसार कर सकते हैं।
config.logger.extend()अंदर के पर्यावरण विन्यास का उपयोग करके मुझे कुछ अजीब लगा । इसके बजाय, मैंने अपने वातावरण में सेट config.loggerकिया STDOUT, फिर अलग-अलग इनिशियलाइज़र में लकड़हारा बढ़ाया।
उन लोगों के लिए जो इसे पसंद करते हैं:
log = Logger.new("| tee test.log") # note the pipe ( '|' )
log.info "hi" # will log to both STDOUT and test.log
या लकड़हारा फ़ॉर्मेटर में संदेश प्रिंट करें:
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
मैं वास्तव में इस तकनीक का उपयोग लॉग फ़ाइल, क्लाउड लॉगर सर्विस (लॉगराइट्स) में प्रिंट करने के लिए कर रहा हूं और अगर यह देव वातावरण है - तो STDOUT पर भी प्रिंट करें।
"| tee test.log"पुराने आउटपुट को अधिलेखित कर देगा, "| tee -a test.log"इसके बजाय हो सकता है
जबकि मैं अन्य सुझावों को काफी पसंद करता हूं, मैंने पाया कि मेरे पास यह एक ही मुद्दा था लेकिन एसटीडीआरआर और फ़ाइल के लिए अलग लॉगिंग स्तर रखने की क्षमता चाहता था।
मैंने एक राउटिंग रणनीति के साथ समाप्त किया जो आईओ स्तर के बजाय लकड़हारा स्तर पर मल्टीप्लेक्स करता है, ताकि प्रत्येक लकड़हारा तब स्वतंत्र लॉग-स्तर पर काम कर सके:
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)
MultiLoggerतरह @dsz का वर्णन करता है एक महान फिट है। साझा करने के लिए धन्यवाद!
आप कई डिवाइस लॉगिंग कार्यक्षमता को सीधे लकड़हारे में भी जोड़ सकते हैं:
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
उदाहरण के लिए:
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')
यहाँ एक और कार्यान्वयन, @ jonas054 के उत्तर से प्रेरित है ।
यह इसी तरह के पैटर्न का उपयोग करता है Delegator। इस तरह से आपको उन सभी तरीकों को सूचीबद्ध नहीं करना है, जिन्हें आप प्रतिनिधि बनाना चाहते हैं, क्योंकि यह उन सभी विधियों को निर्दिष्ट करेगा जो किसी लक्षित लक्ष्य में परिभाषित हैं:
class Tee < DelegateToAllClass(IO)
end
$stdout = Tee.new(STDOUT, File.open("#{__FILE__}.log", "a"))
आपको लॉगर के साथ भी इसका उपयोग करने में सक्षम होना चाहिए।
डेलीगेट_तो_लाल.आरबी यहाँ से उपलब्ध है: https://gist.github.com/TylerRick/4990898
त्वरित और गंदा (रेफरी: 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')
@ jonas054 का उत्तर ऊपर महान है, लेकिन यह MultiDelegatorहर नए प्रतिनिधि के साथ वर्ग को प्रदूषित करता है । यदि आप MultiDelegatorकई बार उपयोग करते हैं, तो यह कक्षा में तरीकों को जोड़ना जारी रखेगा, जो अवांछनीय है। (उदाहरण के लिए bellow देखें)
यहाँ एक ही कार्यान्वयन है, लेकिन अनाम वर्गों का उपयोग करना ताकि विधियाँ प्रतिनिधि वर्ग को प्रदूषित न करें।
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
संशोधित कार्यान्वयन के साथ मूल कार्यान्वयन के साथ विधि प्रदूषण का एक उदाहरण यहां दिया गया है:
tee = MultiDelegator.delegate(:write).to(STDOUT)
tee.respond_to? :write
# => true
tee.respond_to? :size
# => false
ऊपर सब अच्छा है। teeएक writeविधि है, लेकिन sizeउम्मीद के अनुसार कोई विधि नहीं है। अब, विचार करें कि जब हम एक और प्रतिनिधि बनाते हैं:
tee2 = MultiDelegator.delegate(:size).to("bar")
tee2.respond_to? :size
# => true
tee2.respond_to? :write
# => true !!!!! Bad
tee.respond_to? :size
# => true !!!!! Bad
अरे नहीं, tee2का जवाब sizeकी उम्मीद के रूप में, लेकिन यह भी का जवाब writeक्योंकि पहली प्रतिनिधि की। यहां तक कि teeअब sizeविधि प्रदूषण के कारण प्रतिक्रिया करता है ।
अनाम वर्ग समाधान के विपरीत, सब कुछ अपेक्षित है:
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
क्या आप मानक लकड़हारे तक सीमित हैं?
यदि आप 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
एक फायदा: आप स्टडआउट और फ़ाइल के लिए विभिन्न लॉग-लेवल को भी परिभाषित कर सकते हैं।
मैं "उप-तत्वों को सभी विधियों को सौंपने" के एक ही विचार पर गया था जो अन्य लोगों ने पहले से ही पता लगाया था, लेकिन उनमें से प्रत्येक के लिए विधि के अंतिम कॉल का रिटर्न मूल्य है। अगर मैं नहीं करता था, तो वह टूट गया logger-colorsजो उम्मीद कर रहा था कि Integerऔर नक्शा वापस आ रहा है 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
यह हर तरीके को सभी लक्ष्यों पर फिर से व्यवस्थित करेगा, और केवल अंतिम कॉल का रिटर्न वैल्यू लौटाएगा।
इसके अलावा, यदि आप रंग चाहते हैं, तो STDOUT या STDERR को अंतिम रूप दिया जाना चाहिए, क्योंकि यह केवल दो रंग हैं जिन्हें आउटपुट माना जाता है। लेकिन फिर, यह आपकी फ़ाइल में रंगों का उत्पादन भी करेगा।
logger = Logger.new MultiIO.new(File.open("log/test.log", 'w'), STDOUT)
logger.error "Roses are red"
logger.unknown "Violets are blue"
मैंने थोड़ा रूबीगेम लिखा है जो आपको इनमें से कई काम करने की अनुमति देता है:
# 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"
आप github: teerb पर कोड पा सकते हैं
एक और तरीका। यदि आप टैग किए गए लॉगिंग का उपयोग कर रहे हैं और अन्य लॉगफ़ाइल में भी टैग की आवश्यकता है, तो आप इसे इस तरह से कर सकते हैं
# 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))
इसके बाद आपको वैकल्पिक लॉगर में uuid टैग मिलेंगे
["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
आशा है कि किसी की मदद करता है।
ActiveSupport::Loggerइसके साथ बॉक्स से बाहर काम करता है - आपको बस इसके Rails.logger.extendसाथ उपयोग करने की आवश्यकता है ActiveSupport::Logger.broadcast(...)।
एक और विकल्प ;-)
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 ...')
मुझे मल्टीआईओ दृष्टिकोण पसंद है । यह रूबी लकड़हारे के साथ अच्छी तरह से काम करता है । यदि आप शुद्ध IO का उपयोग करते हैं तो यह काम करना बंद कर देता है क्योंकि इसमें कुछ तरीकों की कमी होती है जो IO वस्तुओं के पास होने की उम्मीद है। यहां से पहले पाइप का उल्लेख किया गया था: मैं रूबी लकड़हारा लॉग आउटपुट को स्टडआउट करने के साथ-साथ फ़ाइल कैसे कर सकता हूं? । यहाँ वह है जो मेरे लिए सबसे अच्छा काम करता है।
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')
नोट मुझे पता है कि यह सीधे सवाल का जवाब नहीं देता है लेकिन यह दृढ़ता से संबंधित है। जब भी मैंने कई IOs के आउटपुट की खोज की, मैं इस थ्रेड में आया। तो, मुझे आशा है कि आपको यह उपयोगी भी लगेगा।
यह @ राडो के समाधान का सरलीकरण है।
def delegator(*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 for new
end
end # new class
end # delegate
बाहरी वर्ग के आवरण की आवश्यकता के बिना उसके सभी लाभ समान हैं। एक अलग माणिक फ़ाइल में इसकी उपयोगी उपयोगिता है।
प्रतिनिधि के रूप में ऐसा करने के लिए एक लाइनर के रूप में उपयोग करें:
IO_delegator_instance = delegator(:write, :read).for(STDOUT, STDERR)
IO_delegator_instance.write("blah")
या इसे एक कारखाने के रूप में उपयोग करें:
logger_delegator_class = delegator(:log, :warn, :error)
secret_delegator = logger_delegator_class(main_logger, secret_logger)
secret_delegator.warn("secret")
general_delegator = logger_delegator_class(main_logger, debug_logger, other_logger)
general_delegator.log("message")
यदि आप उपयोग करने के साथ ठीक हैं ActiveSupport, तो मैं अत्यधिक जांच करने की सलाह दूंगा ActiveSupport::Logger.broadcast, जो एक लकड़हारे के लिए अतिरिक्त लॉग डेस्टिनेशंस को जोड़ने का एक उत्कृष्ट और बहुत ही संक्षिप्त तरीका है।
वास्तव में, यदि आप रेल 4+ ( इस कमिट के अनुसार ) का उपयोग कर रहे हैं, तो आपको वांछित व्यवहार प्राप्त करने के लिए कुछ भी करने की आवश्यकता नहीं है - कम से कम यदि आप उपयोग कर रहे हैं rails console। जब भी आप उपयोग करते हैं rails console, रेल स्वचालित रूप सेRails.logger ऐसे विस्तारित होती है कि यह दोनों को अपने सामान्य फ़ाइल गंतव्य ( log/production.logउदाहरण के लिए) और STDERR:
console do |app|
…
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
console = ActiveSupport::Logger.new(STDERR)
Rails.logger.extend ActiveSupport::Logger.broadcast console
end
ActiveRecord::Base.verbose_query_logs = false
end
किसी अज्ञात और दुर्भाग्यपूर्ण कारण के लिए, यह विधि अनिर्दिष्ट है लेकिन आप स्रोत कोड या ब्लॉग पोस्ट का उल्लेख कर सकते हैं कि यह कैसे काम करता है या उदाहरण देखें।
https://www.joshmcarthur.com/til/2018/08/16/log-to-multiple-destinations-using-activesupport-4.html का एक और उदाहरण है:
require "active_support/logger"
console_logger = ActiveSupport::Logger.new(STDOUT)
file_logger = ActiveSupport::Logger.new("my_log.log")
combined_logger = console_logger.extend(ActiveSupport::Logger.broadcast(file_logger))
combined_logger.debug "Debug level"
…
मुझे भी इसकी आवश्यकता है हाल ही में मैंने एक पुस्तकालय लागू किया है जो ऐसा करता है। मैंने अभी इस StackOverflow प्रश्न की खोज की है, इसलिए मैं इसे किसी ऐसे व्यक्ति के लिए बाहर रख रहा हूँ, जिसे इसकी आवश्यकता है: https://github.com/agis/multi_io ।
यहां उल्लिखित अन्य समाधानों की तुलना में, यह IOअपने आप में एक वस्तु होने का प्रयास करता है , इसलिए इसे अन्य नियमित IO ऑब्जेक्ट्स (फ़ाइलें, सॉकेट्स) के लिए ड्रॉप-इन प्रतिस्थापन के रूप में उपयोग किया जा सकता है।
उस ने कहा, मैंने अभी तक सभी मानक IO विधियों को लागू नहीं किया है, लेकिन जो हैं, IO शब्दार्थ का अनुसरण करते हैं (उदाहरण के लिए, #writeसभी अंतर्निहित IO लक्ष्य के लिए लिखी गई बाइट्स की संख्या का योग)।
मुझे लगता है कि आपके STDOUT का उपयोग महत्वपूर्ण रनटाइम जानकारी और त्रुटियों के लिए किया जाता है।
इसलिए मैं उपयोग करता हूं
$log = Logger.new('process.log', 'daily')
डीबग और नियमित लॉगिंग लॉग करने के लिए, और फिर कुछ लिखा
puts "doing stuff..."
जहाँ मुझे उन सूचनाओं को देखने की आवश्यकता है जो मेरी स्क्रिप्ट पर चल रही थीं!
बाह, बस मेरे 10 सेंट :-)
| teeफ़ाइल में जोड़ने से पहले मेरे लिए काम किया, इसलिएLogger.new("| tee test.log")। पाइप पर ध्यान दें। यह coderwall.com/p/y_b3ra/…