Ruby - share logger instance among module/classes

后端 未结 9 1717
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-04 06:45

Working on a little Ruby script that goes out to the web and crawls various services. I\'ve got a module with several classes inside:

module Crawler
  class          


        
相关标签:
9条回答
  • 2020-12-04 07:17

    Based on your comment

    All of this could be avoided if you could just dynamically change the output location of an already-instantiated Logger (similar to how you change the log level).

    If you are not restricted to the default logger you may use another log-gem.

    As an example with log4r:

    require 'log4r' 
    
    module Crawler
      LOGGER = Log4r::Logger.new('mylog')
      class Runner
        def initialize
            LOGGER.info('Created instance for %s' % self.class)
        end
      end
    end
    
    ARGV << 'test'  #testcode
    
    #...
    case ARGV.first
      when 'test'
        Crawler::LOGGER.outputters = Log4r::StdoutOutputter.new('stdout')
      when 'prod'
        Crawler::LOGGER.outputters = Log4r::FileOutputter.new('file', :filename => 'test.log') #append to existing log
    end
    #...
    Crawler::Runner.new
    

    In prod mode the logging data are stored in a file (attached to existing file, but there are options to create new log files or implement rolling log files).

    The result:

     INFO main: Created instance for Crawler::Runner
    

    If you use the inheritance mechanism of log4r (a), you may define a logger for each class (or in my following example for each instance) and share the outputter.

    The example:

    require 'log4r' 
    
    module Crawler
      LOGGER = Log4r::Logger.new('mylog')
      class Runner
        def initialize(id)
          @log = Log4r::Logger.new('%s::%s %s' % [LOGGER.fullname,self.class,id])
          @log.info('Created instance for %s with id %s' % [self.class, id])
        end
      end
    end
    
    ARGV << 'test'  #testcode
    
    #...
    case ARGV.first
      when 'test'
        Crawler::LOGGER.outputters = Log4r::StdoutOutputter.new('stdout')
      when 'prod'
        Crawler::LOGGER.outputters = Log4r::FileOutputter.new('file', :filename => 'test.log') #append to existing log
    end
    #...
    Crawler::Runner.new(1)
    Crawler::Runner.new(2)
    

    The result:

     INFO Runner 1: Created instance for Crawler::Runner with id 1
     INFO Runner 2: Created instance for Crawler::Runner with id 2
    

    (a) A logger name like A::B has the name B and is a child of the logger with the name A. As far as I know this is no object inheritance.

    One advantage of this approach: If you want to use a single logger for each class, you need only to change the name of the logger.

    0 讨论(0)
  • 2020-12-04 07:18

    I like to have a logger method available in my classes, but I don't like sprinkling @logger = Logging.logger in all my initializers. Usually, I do this:

    module Logging
      # This is the magical bit that gets mixed into your classes
      def logger
        Logging.logger
      end
    
      # Global, memoized, lazy initialized instance of a logger
      def self.logger
        @logger ||= Logger.new(STDOUT)
      end
    end
    

    Then, in your classes:

    class Widget
      # Mix in the ability to log stuff ...
      include Logging
    
      # ... and proceed to log with impunity:
      def discombobulate(whizbang)
        logger.warn "About to combobulate the whizbang"
        # commence discombobulation
      end
    end
    

    Because the Logging#logger method can access the instance that the module is mixed into, it is trivial to extend your logging module to record the classname with log messages:

    module Logging
      def logger
        @logger ||= Logging.logger_for(self.class.name)
      end
    
      # Use a hash class-ivar to cache a unique Logger per class:
      @loggers = {}
    
      class << self
        def logger_for(classname)
          @loggers[classname] ||= configure_logger_for(classname)
        end
    
        def configure_logger_for(classname)
          logger = Logger.new(STDOUT)
          logger.progname = classname
          logger
        end
      end
    end
    

    Your Widget now logs messages with its classname, and didn't need to change one bit :)

    0 讨论(0)
  • 2020-12-04 07:21

    How about wrapping the logger in a singleton then you could access it using MyLogger.instance

    0 讨论(0)
提交回复
热议问题