कैसे मैं रूबी लकड़हारा लॉग उत्पादन करने के लिए stdout फ़ाइल के रूप में अच्छी तरह से कर सकते हैं?


94

लकड़हारे में एक टी कार्यक्षमता की तरह।


1
| teeफ़ाइल में जोड़ने से पहले मेरे लिए काम किया, इसलिए Logger.new("| tee test.log")पाइप पर ध्यान दें। यह coderwall.com/p/y_b3ra/…
माइक डब्ल्यू

@mjwatts tee --append test.logओवरराइट को रोकने के लिए उपयोग करें ।
2

जवाबों:


124

आप एक छद्म 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 ऑब्जेक्ट्स के लिए अनुमानित किया जाता है, तब तक यह काम करना चाहिए।


यदि आप लकड़हारे के ctor को देखते हैं तो आप देखेंगे कि यह लॉग रोटेशन को गड़बड़ कर देगा। 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
जेफ़चैटर

3
रूबी 2.2 में नोट, @targets.each(&:close)मूल्यह्रास है।
एक्सिस

मेरे लिए तब तक काम किया जब तक मुझे एहसास नहीं हुआ कि मुझे समय-समय पर कॉल करने की आवश्यकता है: लॉग ऑन करने के लिए log_file प्राप्त करने के लिए log_file को बंद करें, जो लकड़हारे ने लॉग किया था (मूल रूप से एक "सेव")। STDOUT पसंद नहीं आया: उस पर कॉल किया जा रहा है, मल्टीओ विचार को पराजित करने का प्रकार। स्किप करने के लिए एक हैक जोड़ा: क्लास फाइल को छोड़कर, लेकिन काश मेरे पास एक और अधिक सुंदर समाधान होता।
किम मिलर

48

@ डेविड का समाधान बहुत अच्छा है। मैंने उनके कोड के आधार पर कई लक्ष्यों के लिए एक सामान्य प्रतिनिधि वर्ग बनाया है।

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)

क्या आप स्पष्ट कर सकते हैं कि यह कैसे बेहतर है या डेविड द्वारा सुझाए गए सादे की तुलना में इस दृष्टिकोण की बढ़ी हुई उपयोगिताएं क्या हैं
मनीष सपारिया

5
यह चिंताओं का पृथक्करण है। मल्टीडेगेटर केवल कॉल को कई लक्ष्यों को सौंपने के बारे में जानता है। तथ्य यह है कि एक लॉगिंग डिवाइस को एक लिखने की आवश्यकता होती है और कॉलर में एक करीबी तरीका लागू होता है। यह मल्टीडेगलेटर को लॉगिंग के अलावा अन्य स्थितियों में प्रयोग करने योग्य बनाता है।
जोनास ०५४

अच्छा समाधान है। मैंने अपने रेक कार्यों से लॉग फ़ाइल में आउटपुट को टी करने के लिए इसका उपयोग करने की कोशिश की। हालांकि, इसे पुट के साथ काम करने के लिए प्राप्त करने के लिए ("निजी विधि` पुट "कहा जाता है) प्राप्त किए बिना $ stdout.puts कॉल करने में सक्षम होने के लिए, मुझे कुछ और तरीकों को जोड़ना पड़ा: log_file = File.open (" tmp ") rake.log "," a ") $ stdout = MultiDelegator.delegate (: लिखना,: close,: put,: print) .to (STDOUT, log_file) अच्छा होगा यदि यह टी वर्ग बनाने के लिए संभव है जो विरासत में मिला है। MultiDelegator, जैसा कि आप stdlib में डेलीगेटर वर्ग के साथ कर सकते हैं ...
टायलर रिक

मैं इस तरह के एक प्रतिनिधिमंडल के कार्यान्वयन के साथ आया, जिसे मैंने DelegatorToAll कहा। इस तरह से आपको उन सभी तरीकों को सूचीबद्ध नहीं करना है, जिन्हें आप प्रतिनिधि करना चाहते हैं, क्योंकि यह सभी विधियों को प्रतिनिधि श्रेणी (IO) में परिभाषित करेगा: वर्ग Te <DelegateToAllClass (IO) अंत $ stoutout = Tee.new (STDOUT) , File.open ("# { FILE } .log", "a") अधिक जानकारी के लिए gist.github.com/TylerRick/4990898 देखें।
टायलर रिक

1
मैं वास्तव में आपके समाधान को पसंद करता हूं, लेकिन यह सामान्य प्रतिनिधि के रूप में अच्छा नहीं है जिसे कई बार इस्तेमाल किया जा सकता है क्योंकि प्रत्येक प्रतिनिधि नए तरीकों के साथ सभी उदाहरणों को प्रदूषित करता है। मैंने एक उत्तर बोला ( stackoverflow.com/a/36659911/123376 ) पोस्ट किया जो इस समस्या को हल करता है। मैंने एक संपादन के बजाय एक उत्तर पोस्ट किया क्योंकि यह दो कार्यान्वयन के बीच अंतर को देखने के लिए शैक्षिक हो सकता है क्योंकि मैंने भी उदाहरण पोस्ट किए हैं।
राडो

35

यदि आप रेल 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))

क्या यह रेल के बाहर लागू होता है, या केवल रेल से?
एड साइक्स

यह ActiveSupport पर आधारित है, इसलिए यदि आपके पास पहले से निर्भरता है, तो आप extendकिसी भी ActiveSupport::Loggerउदाहरण को ऊपर दिखाए गए अनुसार कर सकते हैं।
फिलिप्बर

धन्यवाद, यह मददगार था।
लुकास

मुझे लगता है कि यह सबसे सरल और सबसे प्रभावी उत्तर है, हालांकि मेरे config.logger.extend()अंदर के पर्यावरण विन्यास का उपयोग करके मुझे कुछ अजीब लगा । इसके बजाय, मैंने अपने वातावरण में सेट config.loggerकिया STDOUT, फिर अलग-अलग इनिशियलाइज़र में लकड़हारा बढ़ाया।
Mattsch

14

उन लोगों के लिए जो इसे पसंद करते हैं:

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 पर भी प्रिंट करें।


2
"| tee test.log"पुराने आउटपुट को अधिलेखित कर देगा, "| tee -a test.log"इसके बजाय हो सकता है
जुंग

13

जबकि मैं अन्य सुझावों को काफी पसंद करता हूं, मैंने पाया कि मेरे पास यह एक ही मुद्दा था लेकिन एसटीडीआरआर और फ़ाइल के लिए अलग लॉगिंग स्तर रखने की क्षमता चाहता था।

मैंने एक राउटिंग रणनीति के साथ समाप्त किया जो आईओ स्तर के बजाय लकड़हारा स्तर पर मल्टीप्लेक्स करता है, ताकि प्रत्येक लकड़हारा तब स्वतंत्र लॉग-स्तर पर काम कर सके:

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)

1
मुझे यह समाधान सबसे अच्छा लगता है क्योंकि यह (1) सरल है, और (2) आपको सब कुछ एक फ़ाइल में जाने के बजाय अपने लकड़हारा वर्गों को फिर से उपयोग करने के लिए प्रोत्साहित करता है। मेरे मामले में मैं ग्रेग्लॉग के लिए STDOUT और एक GELF परिशिष्ट में लॉग इन करना चाहूंगा। बीत रहा है एक MultiLoggerतरह @dsz का वर्णन करता है एक महान फिट है। साझा करने के लिए धन्यवाद!
एरिक क्रैमर

स्यूडोवेरिएबल्स (सेटर / गेटर्स) को संभालने के लिए जोड़ा गया खंड
एरिक क्रेमर

11

आप कई डिवाइस लॉगिंग कार्यक्षमता को सीधे लकड़हारे में भी जोड़ सकते हैं:

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')

9

यहाँ एक और कार्यान्वयन, @ jonas054 के उत्तर से प्रेरित है ।

यह इसी तरह के पैटर्न का उपयोग करता है Delegator। इस तरह से आपको उन सभी तरीकों को सूचीबद्ध नहीं करना है, जिन्हें आप प्रतिनिधि बनाना चाहते हैं, क्योंकि यह उन सभी विधियों को निर्दिष्ट करेगा जो किसी लक्षित लक्ष्य में परिभाषित हैं:

class Tee < DelegateToAllClass(IO)
end

$stdout = Tee.new(STDOUT, File.open("#{__FILE__}.log", "a"))

आपको लॉगर के साथ भी इसका उपयोग करने में सक्षम होना चाहिए।

डेलीगेट_तो_लाल.आरबी यहाँ से उपलब्ध है: https://gist.github.com/TylerRick/4990898



3

@ 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

2

क्या आप मानक लकड़हारे तक सीमित हैं?

यदि आप 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

एक फायदा: आप स्टडआउट और फ़ाइल के लिए विभिन्न लॉग-लेवल को भी परिभाषित कर सकते हैं।


1

मैं "उप-तत्वों को सभी विधियों को सौंपने" के एक ही विचार पर गया था जो अन्य लोगों ने पहले से ही पता लगाया था, लेकिन उनमें से प्रत्येक के लिए विधि के अंतिम कॉल का रिटर्न मूल्य है। अगर मैं नहीं करता था, तो वह टूट गया 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"

1

मैंने थोड़ा रूबीगेम लिखा है जो आपको इनमें से कई काम करने की अनुमति देता है:

# 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 पर कोड पा सकते हैं


1

एक और तरीका। यदि आप टैग किए गए लॉगिंग का उपयोग कर रहे हैं और अन्य लॉगफ़ाइल में भी टैग की आवश्यकता है, तो आप इसे इस तरह से कर सकते हैं

# 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(...)
XtraSimplicity

0

एक और विकल्प ;-)

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 ...')

0

मुझे मल्टीआईओ दृष्टिकोण पसंद है । यह रूबी लकड़हारे के साथ अच्छी तरह से काम करता है । यदि आप शुद्ध 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 के आउटपुट की खोज की, मैं इस थ्रेड में आया। तो, मुझे आशा है कि आपको यह उपयोगी भी लगेगा।


0

यह @ राडो के समाधान का सरलीकरण है।

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

0

आप मणि Loog::Teeसे वस्तु का उपयोग कर सकते हैं loog:

require 'loog'
logger = Loog::Tee.new(first, second)

बिल्कुल वही जो आप ढूंढ रहे हैं।


0

यदि आप उपयोग करने के साथ ठीक हैं 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"

0

मुझे भी इसकी आवश्यकता है हाल ही में मैंने एक पुस्तकालय लागू किया है जो ऐसा करता है। मैंने अभी इस StackOverflow प्रश्न की खोज की है, इसलिए मैं इसे किसी ऐसे व्यक्ति के लिए बाहर रख रहा हूँ, जिसे इसकी आवश्यकता है: https://github.com/agis/multi_io

यहां उल्लिखित अन्य समाधानों की तुलना में, यह IOअपने आप में एक वस्तु होने का प्रयास करता है , इसलिए इसे अन्य नियमित IO ऑब्जेक्ट्स (फ़ाइलें, सॉकेट्स) के लिए ड्रॉप-इन प्रतिस्थापन के रूप में उपयोग किया जा सकता है।

उस ने कहा, मैंने अभी तक सभी मानक IO विधियों को लागू नहीं किया है, लेकिन जो हैं, IO शब्दार्थ का अनुसरण करते हैं (उदाहरण के लिए, #writeसभी अंतर्निहित IO लक्ष्य के लिए लिखी गई बाइट्स की संख्या का योग)।


-3

मुझे लगता है कि आपके STDOUT का उपयोग महत्वपूर्ण रनटाइम जानकारी और त्रुटियों के लिए किया जाता है।

इसलिए मैं उपयोग करता हूं

  $log = Logger.new('process.log', 'daily')

डीबग और नियमित लॉगिंग लॉग करने के लिए, और फिर कुछ लिखा

  puts "doing stuff..."

जहाँ मुझे उन सूचनाओं को देखने की आवश्यकता है जो मेरी स्क्रिप्ट पर चल रही थीं!

बाह, बस मेरे 10 सेंट :-)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.