logging-2.0.0/0000755000175000017500000000000012551223063012506 5ustar globusglobuslogging-2.0.0/lib/0000755000175000017500000000000012551223063013254 5ustar globusglobuslogging-2.0.0/lib/logging.rb0000644000175000017500000004105112551223063015230 0ustar globusglobusrequire File.expand_path('../logging/utils', __FILE__) require 'yaml' require 'stringio' require 'fileutils' require 'little-plugger' require 'multi_json' begin require 'syslog' HAVE_SYSLOG = true rescue LoadError HAVE_SYSLOG = false end # # module Logging extend LittlePlugger # :stopdoc: LIBPATH = ::File.expand_path('..', __FILE__) + ::File::SEPARATOR PATH = ::File.expand_path('../..', __FILE__) + ::File::SEPARATOR LEVELS = {} LNAMES = [] module Plugins; end # :startdoc: class << self # call-seq: # Logging.logger( device, age = 7, size = 1048576 ) # Logging.logger( device, age = 'weekly' ) # # This convenience method returns a Logger instance configured to behave # similarly to a core Ruby Logger instance. # # The _device_ is the logging destination. This can be a filename # (String) or an IO object (STDERR, STDOUT, an open File, etc.). The # _age_ is the number of old log files to keep or the frequency of # rotation (+daily+, +weekly+, or +monthly+). The _size_ is the maximum # logfile size and is only used when _age_ is a number. # # Using the same _device_ twice will result in the same Logger instance # being returned. For example, if a Logger is created using STDOUT then # the same Logger instance will be returned the next time STDOUT is # used. A new Logger instance can be obtained by closing the previous # logger instance. # # log1 = Logging.logger(STDOUT) # log2 = Logging.logger(STDOUT) # log1.object_id == log2.object_id #=> true # # log1.close # log2 = Logging.logger(STDOUT) # log1.object_id == log2.object_id #=> false # # The format of the log messages can be changed using a few optional # parameters. The :pattern can be used to change the log # message format. The :date_pattern can be used to change how # timestamps are formatted. # # log = Logging.logger(STDOUT, # :pattern => "[%d] %-5l : %m\n", # :date_pattern => "%Y-%m-%d %H:%M:%S.%s") # # See the documentation for the Logging::Layouts::Pattern class for a # full description of the :pattern and :date_pattern formatting strings. # def logger( *args ) return ::Logging::Logger if args.empty? opts = args.pop if args.last.instance_of?(Hash) opts ||= Hash.new dev = args.shift keep = age = args.shift size = args.shift name = case dev when String; dev when File; dev.path else dev.object_id.to_s end repo = ::Logging::Repository.instance return repo[name] if repo.has_logger? name l_opts = { :pattern => "%.1l, [%d #%p] %#{::Logging::MAX_LEVEL_LENGTH}l : %m\n", :date_pattern => '%Y-%m-%dT%H:%M:%S.%s' } [:pattern, :date_pattern, :date_method].each do |o| l_opts[o] = opts.delete(o) if opts.has_key? o end layout = ::Logging::Layouts::Pattern.new(l_opts) a_opts = Hash.new a_opts[:size] = size if size.instance_of?(Fixnum) a_opts[:age] = age if age.instance_of?(String) a_opts[:keep] = keep if keep.instance_of?(Fixnum) a_opts[:filename] = dev if dev.instance_of?(String) a_opts[:layout] = layout a_opts.merge! opts appender = case dev when String ::Logging::Appenders::RollingFile.new(name, a_opts) else ::Logging::Appenders::IO.new(name, dev, a_opts) end logger = ::Logging::Logger.new(name) logger.add_appenders appender logger.additive = false class << logger def close @appenders.each {|a| a.close} h = ::Logging::Repository.instance.instance_variable_get :@h h.delete(@name) class << self; undef :close; end end end logger end # Access to the layouts. # def layouts ::Logging::Layouts end # Access to the appenders. # def appenders ::Logging::Appenders end # Returns the color scheme identified by the given _name_. If there is no # color scheme +nil+ is returned. # # If color scheme options are supplied then a new color scheme is created. # Any existing color scheme with the given _name_ will be replaced by the # new color scheme. # def color_scheme( name, opts = {} ) if opts.empty? ::Logging::ColorScheme[name] else ::Logging::ColorScheme.new(name, opts) end end # Reopen all appenders. This method should be called immediately after a # fork to ensure no conflict with file descriptors and calls to fcntl or # flock. # def reopen log_internal {'re-opening all appenders'} ::Logging::Appenders.each {|appender| appender.reopen} self end # call-seq: # include Logging.globally # include Logging.globally( :logger ) # # Add a "logger" method to the including context. If included from # Object or Kernel, the logger method will be available to all objects. # # Optionally, a method name can be given and that will be used to # provided access to the logger: # # include Logging.globally( :log ) # log.info "Just using a shorter method name" # # If you prefer to use the shorter "log" to access the logger. # # ==== Example # # include Logging.globally # # class Foo # logger.debug "Loading the Foo class" # def initialize # logger.info "Creating some new foo" # end # end # # logger.fatal "End of example" # def globally( name = :logger ) Module.new { eval "def #{name}() @_logging_logger ||= ::Logging::Logger[self] end" } end # call-seq: # Logging.init( levels ) # # Defines the levels available to the loggers. The _levels_ is an array # of strings and symbols. Each element in the array is downcased and # converted to a symbol; these symbols are used to create the logging # methods in the loggers. # # The first element in the array is the lowest logging level. Setting the # logging level to this value will enable all log messages. The last # element in the array is the highest logging level. Setting the logging # level to this value will disable all log messages except this highest # level. # # This method should be invoked only once to configure the logging # levels. It is automatically invoked with the default logging levels # when the first logger is created. # # The levels "all" and "off" are reserved and will be ignored if passed # to this method. # # Example: # # Logging.init :debug, :info, :warn, :error, :fatal # log = Logging::Logger['my logger'] # log.level = :warn # log.warn 'Danger! Danger! Will Robinson' # log.info 'Just FYI' # => not logged # # or # # Logging.init %w(DEBUG INFO NOTICE WARNING ERR CRIT ALERT EMERG) # log = Logging::Logger['syslog'] # log.level = :notice # log.warning 'This is your first warning' # log.info 'Just FYI' # => not logged # def init( *args ) args = %w(debug info warn error fatal) if args.empty? args.flatten! levels = LEVELS.clear names = LNAMES.clear id = 0 args.each do |lvl| lvl = levelify lvl unless levels.has_key?(lvl) or lvl == 'all' or lvl == 'off' levels[lvl] = id names[id] = lvl.upcase id += 1 end end longest = names.inject {|x,y| (x.length > y.length) ? x : y} longest = 'off' if longest.length < 3 module_eval "MAX_LEVEL_LENGTH = #{longest.length}", __FILE__, __LINE__ initialize_plugins levels.keys end # call-seq: # Logging.format_as( obj_format ) # # Defines the default _obj_format_ method to use when converting objects # into string representations for logging. _obj_format_ can be one of # :string, :inspect, or :yaml. These # formatting commands map to the following object methods # # * :string => to_s # * :inspect => inspect # * :yaml => to_yaml # * :json => MultiJson.encode(obj) # # An +ArgumentError+ is raised if anything other than +:string+, # +:inspect+, +:yaml+ is passed to this method. # def format_as( f ) f = f.intern if f.instance_of? String unless [:string, :inspect, :yaml, :json].include? f raise ArgumentError, "unknown object format '#{f}'" end module_eval "OBJ_FORMAT = :#{f}", __FILE__, __LINE__ self end # call-seq: # Logging.backtrace #=> true or false # Logging.backtrace( value ) #=> true or false # # Without any arguments, returns the global exception backtrace logging # value. When set to +true+ backtraces will be written to the logs; when # set to +false+ backtraces will be suppressed. # # When an argument is given the global exception backtrace setting will # be changed. Value values are "on", :on and +true+ to # turn on backtraces and "off", :off and +false+ to # turn off backtraces. # def backtrace( b = nil ) @backtrace = true unless defined? @backtrace return @backtrace if b.nil? @backtrace = case b when :on, 'on', true; true when :off, 'off', false; false else raise ArgumentError, "backtrace must be true or false" end end # Returns the library path for the module. If any arguments are given, # they will be joined to the end of the library path using # File.join. # def libpath( *args, &block ) rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten) if block begin $LOAD_PATH.unshift LIBPATH rv = block.call ensure $LOAD_PATH.shift end end return rv end # Returns the lpath for the module. If any arguments are given, # they will be joined to the end of the path using # File.join. # def path( *args, &block ) rv = args.empty? ? PATH : ::File.join(PATH, args.flatten) if block begin $LOAD_PATH.unshift PATH rv = block.call ensure $LOAD_PATH.shift end end return rv end # call-seq: # show_configuration( io = STDOUT, logger = 'root' ) # # This method is used to show the configuration of the logging # framework. The information is written to the given _io_ stream # (defaulting to stdout). Normally the configuration is dumped starting # with the root logger, but any logger name can be given. # # Each line contains information for a single logger and it's appenders. # A child logger is indented two spaces from it's parent logger. Each # line contains the logger name, level, additivity, and trace settings. # Here is a brief example: # # root ........................... *info -T # LoggerA ...................... info +A -T # LoggerA::LoggerB ........... info +A -T # LoggerA::LoggerC ........... *debug +A -T # LoggerD ...................... *warn -A +T # # The lines can be deciphered as follows: # # 1) name - the name of the logger # # 2) level - the logger level; if it is preceded by an # asterisk then the level was explicitly set for that # logger (as opposed to being inherited from the parent # logger) # # 3) additivity - a "+A" shows the logger is additive, and log events # will be passed up to the parent logger; "-A" shows # that the logger will *not* pass log events up to the # parent logger # # 4) tracing - a "+T" shows that the logger will include caller # tracing information in generated log events (this # includes filename and line number of the log # message); "-T" shows that the logger does not include # caller tracing information in the log events # # If a logger has appenders then they are listed, one per line, # immediately below the logger. Appender lines are pre-pended with a # single dash: # # root ........................... *info -T # - # LoggerA ...................... info +A -T # LoggerA::LoggerB ........... info +A -T # LoggerA::LoggerC ........... *debug +A -T # LoggerD ...................... *warn -A +T # - # # We can see in this configuration dump that all the loggers will append # to stdout via the Stdout appender configured in the root logger. All # the loggers are additive, and so their generated log events will be # passed up to the root logger. # # The exception in this configuration is LoggerD. Its additivity is set # to false. It uses its own appender to send messages to stderr. # def show_configuration( io = STDOUT, logger = 'root', indent = 0 ) logger = ::Logging::Logger[logger] unless ::Logging::Logger === logger io << logger._dump_configuration(indent) indent += 2 children = ::Logging::Repository.instance.children(logger.name) children.sort {|a,b| a.name <=> b.name}.each do |child| ::Logging.show_configuration(io, child, indent) end io end # :stopdoc: # Convert the given level into a canonical form - a lowercase string. def levelify( level ) case level when String; level.downcase when Symbol; level.to_s.downcase else raise ArgumentError, "levels must be a String or Symbol" end end # Convert the given level into a level number. def level_num( level ) l = levelify(level) rescue level case l when 'all'; 0 when 'off'; LEVELS.length else begin; Integer(l); rescue ArgumentError; LEVELS[l] end end end # Internal logging method for use by the framework. def log_internal( level = 1, &block ) ::Logging::Logger[::Logging].__send__(levelify(LNAMES[level]), &block) end # Internal logging method for handling exceptions. If the # `Thread#abort_on_exception` flag is set then the # exception will be raised again. def log_internal_error( err ) log_internal(-2) { err } raise err if Thread.abort_on_exception end # Close all appenders def shutdown( *args ) return unless initialized? log_internal {'shutdown called - closing all appenders'} ::Logging::Appenders.each {|appender| appender.close} nil end # Reset the Logging framework to it's uninitialized state def reset ::Logging::Repository.reset ::Logging::Appenders.reset ::Logging::ColorScheme.reset ::Logging.clear_diagnostic_contexts(true) LEVELS.clear LNAMES.clear remove_instance_variable :@backtrace if defined? @backtrace remove_const :MAX_LEVEL_LENGTH if const_defined? :MAX_LEVEL_LENGTH remove_const :OBJ_FORMAT if const_defined? :OBJ_FORMAT self end # Return +true+ if the Logging framework is initialized. def initialized? const_defined? :MAX_LEVEL_LENGTH end # :startdoc: end require libpath('logging/version') require libpath('logging/appender') require libpath('logging/layout') require libpath('logging/filter') require libpath('logging/log_event') require libpath('logging/logger') require libpath('logging/repository') require libpath('logging/root_logger') require libpath('logging/color_scheme') require libpath('logging/appenders') require libpath('logging/layouts') require libpath('logging/filters') require libpath('logging/proxy') require libpath('logging/diagnostic_context') require libpath('logging/rails_compat') end # module Logging # This finalizer will close all the appenders that exist in the system. # This is needed for closing IO streams and connections to the syslog server # or e-mail servers, etc. # # You can prevent the finalizer from running by calling `exit!` from your # application. This is required when daemonizing. # ObjectSpace.define_finalizer self, Logging.method(:shutdown) logging-2.0.0/lib/spec/0000755000175000017500000000000012551223063014206 5ustar globusglobuslogging-2.0.0/lib/spec/logging_helper.rb0000644000175000017500000000034612551223063017523 0ustar globusglobus require File.expand_path('../../rspec/logging_helper', __FILE__) Spec::LoggingHelper = RSpec::LoggingHelper if defined? Spec::Runner::Configuration class Spec::Runner::Configuration include Spec::LoggingHelper end end logging-2.0.0/lib/rspec/0000755000175000017500000000000012551223063014370 5ustar globusglobuslogging-2.0.0/lib/rspec/logging_helper.rb0000644000175000017500000000170012551223063017700 0ustar globusglobus module RSpec module LoggingHelper # Capture log messages from the Logging framework and make them # available via a @log_output instance variable. The @log_output # supports a readline method to access the log messages. # def capture_log_messages( opts = {} ) from = opts.fetch(:from, 'root') to = opts.fetch(:to, '__rspec__') exclusive = opts.fetch(:exclusive, true) appender = Logging::Appenders[to] || Logging::Appenders::StringIo.new(to) logger = Logging::Logger[from] if exclusive logger.appenders = appender else logger.add_appenders(appender) end before(:all) do @log_output = Logging::Appenders[to] end before(:each) do @log_output.reset end end end # module LoggingHelper end # module RSpec if defined? RSpec::Core::Configuration class RSpec::Core::Configuration include RSpec::LoggingHelper end end logging-2.0.0/lib/logging/0000755000175000017500000000000012551223063014702 5ustar globusglobuslogging-2.0.0/lib/logging/filters.rb0000644000175000017500000000012412551223063016674 0ustar globusglobusmodule Logging module Filters ; end require libpath('logging/filters/level') endlogging-2.0.0/lib/logging/appenders.rb0000644000175000017500000000272512551223063017216 0ustar globusglobus module Logging module Appenders # call-seq: # Appenders[name] # # Returns the appender instance stored in the appender hash under the # key _name_, or +nil+ if no appender has been created using that name. # def []( name ) @appenders[name] end # call-seq: # Appenders[name] = appender # # Stores the given _appender_ instance in the appender hash under the # key _name_. # def []=( name, value ) @appenders[name] = value end # call-seq: # Appenders.remove( name ) # # Removes the appender instance stored in the appender hash under the # key _name_. # def remove( name ) @appenders.delete(name) end # call-seq: # each {|appender| block} # # Yield each appender to the _block_. # def each( &block ) @appenders.values.each(&block) return nil end # :stopdoc: def reset @appenders.values.each {|appender| next if appender.nil? appender.close } @appenders.clear return nil end # :startdoc: extend self @appenders = Hash.new end # Appenders require libpath('logging/appenders/buffering') require libpath('logging/appenders/io') require libpath('logging/appenders/console') require libpath('logging/appenders/file') require libpath('logging/appenders/rolling_file') require libpath('logging/appenders/string_io') require libpath('logging/appenders/syslog') end # Logging logging-2.0.0/lib/logging/log_event.rb0000644000175000017500000000252012551223063017210 0ustar globusglobus module Logging # This class defines a logging event. # LogEvent = Struct.new( :logger, :level, :data, :time, :file, :line, :method ) { # :stopdoc: # Regular expression used to parse out caller information # # * $1 == filename # * $2 == line number # * $3 == method name (might be nil) CALLER_RGXP = %r/([-\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o #CALLER_INDEX = 2 CALLER_INDEX = ((defined? JRUBY_VERSION and JRUBY_VERSION > '1.6') or (defined? RUBY_ENGINE and RUBY_ENGINE[%r/^rbx/i])) ? 1 : 2 # :startdoc: # call-seq: # LogEvent.new( logger, level, [data], caller_tracing ) # # Creates a new log event with the given _logger_ name, numeric _level_, # array of _data_ from the user to be logged, and boolean _caller_tracing_ flag. # If the _caller_tracing_ flag is set to +true+ then Kernel::caller will be # invoked to get the execution trace of the logging method. # def initialize( logger, level, data, caller_tracing ) f = l = m = '' if caller_tracing stack = Kernel.caller[CALLER_INDEX] return if stack.nil? match = CALLER_RGXP.match(stack) f = match[1] l = Integer(match[2]) m = match[3] unless match[3].nil? end super(logger, level, data, Time.now, f, l, m) end } end # module Logging logging-2.0.0/lib/logging/logger.rb0000644000175000017500000003477112551223063016522 0ustar globusglobus module Logging # The +Logger+ class is the primary interface to the +Logging+ framework. # It provides the logging methods that will be called from user methods, # and it generates logging events that are sent to the appenders (the # appenders take care of sending the log events to the logging # destinations -- files, sockets, etc). # # +Logger+ instances are obtained from the +Repository+ and should # not be directly created by users. # # Example: # # log = Logging.logger['my logger'] # log.add_appenders( Logging.appenders.stdout ) # append to STDOUT # log.level = :info # log 'info' and above # # log.info 'starting foo operation' # ... # log.info 'finishing foo operation' # ... # log.fatal 'unknown exception', exception # class Logger @mutex = Mutex.new # :nodoc: class << self # Returns the root logger. def root ::Logging::Repository.instance[:root] end alias_method :instantiate, :new # the "real" new # Overrides the new method such that only one Logger will be created # for any given logger name. def new( *args ) args.empty? ? super : self[args.shift] end # Returns a logger instance for the given name. def []( name ) repo = ::Logging::Repository.instance name = repo.to_key(name) logger = repo[name] return logger unless logger.nil? @mutex.synchronize do logger = repo[name] return logger unless logger.nil? # thread-safe double checking logger = instantiate(name) repo[name] = logger repo.children(name).each { |c| c.__send__(:parent=, logger) } logger end end # This is where the actual logging methods are defined. Two methods # are created for each log level. The first is a query method used to # determine if that perticular logging level is enabled. The second is # the actual logging method that accepts a list of objects to be # logged or a block. If a block is given, then the object returned # from the block will be logged. # # Example # # log = Logging::Logger['my logger'] # log.level = :warn # # log.info? # => false # log.warn? # => true # log.warn 'this is your last warning' # log.fatal 'I die!', exception # # log.debug do # # expensive method to construct log message # msg # end # def define_log_methods( logger ) ::Logging::LEVELS.each do |name,num| code = "undef :#{name} if method_defined? :#{name}\n" code << "undef :#{name}? if method_defined? :#{name}?\n" if logger.level > num code << <<-CODE def #{name}?( ) false end def #{name}( data = nil ) false end CODE else code << <<-CODE def #{name}?( ) true end def #{name}( data = nil ) data = yield if block_given? log_event(::Logging::LogEvent.new(@name, #{num}, data, @caller_tracing)) true end CODE end logger._meta_eval(code, __FILE__, __LINE__) end logger end end # class << self attr_reader :name, :parent, :additive, :caller_tracing # call-seq: # Logger.new( name ) # Logger[name] # # Returns the logger identified by _name_. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # retrieve the logger. When _name_ is a +Class+ the class name will be # used to retrieve the logger. When _name_ is an object the name of the # object's class will be used to retrieve the logger. # # Example: # # obj = MyClass.new # # log1 = Logger.new(obj) # log2 = Logger.new(MyClass) # log3 = Logger['MyClass'] # # log1.object_id == log2.object_id # => true # log2.object_id == log3.object_id # => true # def initialize( name ) case name when String raise(ArgumentError, "logger must have a name") if name.empty? else raise(ArgumentError, "logger name must be a String") end repo = ::Logging::Repository.instance _setup(name, :parent => repo.parent(name)) end # call-seq: # log <=> other # # Compares this logger by name to another logger. The normal return codes # for +String+ objects apply. # def <=>( other ) case other when self; 0 when ::Logging::RootLogger; 1 when ::Logging::Logger; @name <=> other.name else raise ArgumentError, 'expecting a Logger instance' end end # call-seq: # log << "message" # # Log the given message without any formatting and without performing any # level checks. The message is logged to all appenders. The message is # passed up the logger tree if this logger's additivity is +true+. # def <<( msg ) @appenders.each {|a| a << msg} @parent << msg if @additive end alias_method :write, :<< # call-seq: # add( severity, message = nil ) {block} # # Log a message if the given severity is high enough. This is the generic # logging method. Users will be more inclined to use #debug, #info, #warn, # #error, and #fatal. # # Message format: +message+ can be any object, but it has to be # converted to a String in order to log it. The Logging::format_as # method is used to determine how objects chould be converted to # strings. Generally, +inspect+ is used. # # A special case is an +Exception+ object, which will be printed in # detail, including message, class, and backtrace. # # If a _message_ is not given, then the return value from the block is # used as the message to log. This is useful when creating the actual # message is an expensive operation. This allows the logger to check the # severity against the configured level before actually constructing the # message. # # This method returns +true+ if the message was logged, and +false+ is # returned if the message was not logged. # def add( lvl, data = nil, progname = nil ) lvl = Integer(lvl) return false if lvl < level data = yield if block_given? log_event(::Logging::LogEvent.new(@name, lvl, data, @caller_tracing)) true end # call-seq: # additive = true # # Sets the additivity of the logger. Acceptable values are +true+, # 'true', +false+, 'false', or +nil+. In this case +nil+ does not # change the additivity # def additive=( val ) @additive = case val when true, 'true'; true when false, 'false'; false when nil; @additive else raise ArgumentError, 'expecting a boolean' end end # call-seq: # caller_tracing = true # # Sets the caller tracing of the logger. Acceptable values are +true+, # 'true', +false+, 'false', or +nil+. In this case +nil+ does not change # the tracing. # def caller_tracing=( val ) @caller_tracing = case val when true, 'true'; true when false, 'false'; false when nil; @caller_tracing else raise ArgumentError, 'expecting a boolean' end end # call-seq: # level => integer # # Returns an integer which is the defined log level for this logger. # def level return @level unless @level.nil? @parent.level end # call-seq: # level = :all # # Set the level for this logger. The level can be either a +String+, a # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not # the case. # # There are two special levels -- "all" and "off". The former will # enable log messages from this logger. The latter will disable all log # messages from this logger. # # Setting the logger level to +nil+ will cause the parent's logger level # to be used. # # Example: # # log.level = :debug # log.level = "INFO" # log.level = 4 # log.level = 'off' # log.level = :all # # These produce an +ArgumentError+ # # log.level = Object # log.level = -1 # log.level = 1_000_000_000_000 # def level=( level ) @level = if level.nil? then level else lvl = case level when String, Symbol; ::Logging::level_num(level) when Fixnum; level else raise ArgumentError, "level must be a String, Symbol, or Integer" end if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length raise ArgumentError, "unknown level was given '#{level}'" end lvl end define_log_methods(true) self.level end # Returns the list of appenders. # def appenders @appenders.dup end # call-seq: # appenders = app # # Clears the current list of appenders and replaces them with _app_, # where _app_ can be either a single appender or an array of appenders. # def appenders=( args ) @appenders.clear add_appenders(*args) unless args.nil? end # call-seq: # add_appenders( appenders ) # # Add the given _appenders_ to the list of appenders, where _appenders_ # can be either a single appender or an array of appenders. # def add_appenders( *args ) args.flatten.each do |arg| o = arg.kind_of?(::Logging::Appender) ? arg : ::Logging::Appenders[arg.to_s] raise ArgumentError, "unknown appender #{arg.inspect}" if o.nil? @appenders << o unless @appenders.include?(o) end self end # call-seq: # remove_appenders( appenders ) # # Remove the given _appenders_ from the list of appenders. The appenders # to remove can be identified either by name using a +String+ or by # passing the appender instance. _appenders_ can be a single appender or # an array of appenders. # def remove_appenders( *args ) args.flatten.each do |arg| @appenders.delete_if do |a| case arg when String; arg == a.name when ::Logging::Appender; arg.object_id == a.object_id else raise ArgumentError, "#{arg.inspect} is not a 'Logging::Appender'" end end end self end # call-seq: # clear_appenders # # Remove all appenders from this logger. # def clear_appenders( ) @appenders.clear end # call-seq: # inspect => string # # Returns a string representation of the logger. # def inspect "<%s:0x%x name=\"%s\">" % [self.class.name, self.object_id, self.name] end protected # call-seq: # parent = ParentLogger # # Set the parent logger for this logger. This method will be invoked by # the +Repository+ class when a parent or child is added to the # hierarchy. # def parent=( parent ) @parent = parent end # call-seq: # log_event( event ) # # Send the given _event_ to the appenders for logging, and pass the # _event_ up to the parent if additive mode is enabled. The log level has # already been checked before this method is called. # def log_event( event ) @appenders.each {|a| a.append(event)} @parent.log_event(event) if @additive end # call-seq: # define_log_methods( force = false ) # # Define the logging methods for this logger based on the configured log # level. If the level is nil, then we will ask our parent for it's level # and define log levels accordingly. The force flag will skip this # check. # # Recursively call this method on all our children loggers. # def define_log_methods( force = false ) return if @level and !force ::Logging::Logger.define_log_methods(self) ::Logging::Repository.instance.children(name).each do |c| c.define_log_methods end self end # :stopdoc: public # call-seq: # _meta_eval( code ) # # Evaluates the given string of _code_ if the singleton class of this # Logger object. # def _meta_eval( code, file = nil, line = nil ) meta = class << self; self end meta.class_eval code, file, line end # call-seq: # _setup( name, opts = {} ) # # Configures internal variables for the logger. This method can be used # to avoid storing the logger in the repository. # def _setup( name, opts = {} ) @name = name @parent = opts.fetch(:parent, nil) @appenders = opts.fetch(:appenders, []) @additive = opts.fetch(:additive, true) @level = opts.fetch(:level, nil) @caller_tracing = opts.fetch(:caller_tracing, false) ::Logging::Logger.define_log_methods(self) end # call-seq: # _dump_configuration( io = STDOUT, indent = 0 ) # # An internal method that is used to dump this logger's configuration to # the given _io_ stream. The configuration includes the logger's name, # level, additivity, and caller_tracing settings. The configured appenders # are also printed to the _io_ stream. # def _dump_configuration( indent = 0 ) str, spacer, base = '', ' ', 50 indent_str = indent == 0 ? '' : ' ' * indent str << indent_str str << self.name.shrink(base - indent) if (str.length + spacer.length) < base str << spacer str << '.' * (base - str.length) end str = str.ljust(base) str << spacer level_str = @level.nil? ? '' : '*' level_str << if level < ::Logging::LEVELS.length ::Logging.levelify(::Logging::LNAMES[level]) else 'off' end level_len = ::Logging::MAX_LEVEL_LENGTH + 1 str << sprintf("%#{level_len}s" % level_str) str << spacer if self.respond_to?(:additive) str << (additive ? '+A' : '-A') else str << ' ' end str << spacer str << (caller_tracing ? '+T' : '-T') str << "\n" @appenders.each do |appender| str << indent_str str << '- ' str << appender.inspect str << "\n" end return str end # :startdoc: end # Logger end # Logging logging-2.0.0/lib/logging/appender.rb0000644000175000017500000002240712551223063017032 0ustar globusglobus module Logging # The +Appender+ class is provides methods for appending log events to a # logging destination. The log events are formatted into strings using a # Layout. # # All other Appenders inherit from this class which provides stub methods. # Each subclass should provide a +write+ method that will write log # messages to the logging destination. # # A private +sync+ method is provided for use by subclasses. It is used to # synchronize writes to the logging destination, and can be used by # subclasses to synchronize the closing or flushing of the logging # destination. # class Appender attr_reader :name, :layout, :level, :filters # call-seq: # Appender.new( name ) # Appender.new( name, :layout => layout ) # # Creates a new appender using the given name. If no Layout is specified, # then a Basic layout will be used. Any logging header supplied by the # layout will be written to the logging destination when the Appender is # created. # # Options: # # :layout => the layout to use when formatting log events # :level => the level at which to log # :encoding => encoding to use when writing messages (defaults to UTF-8) # :filters => filters to apply to events before processing # def initialize( name, opts = {} ) ::Logging.init unless ::Logging.initialized? @name = name.to_s @closed = false @filters = [] @mutex = ReentrantMutex.new self.layout = opts.fetch(:layout, ::Logging::Layouts::Basic.new) self.level = opts.fetch(:level, nil) self.encoding = opts.fetch(:encoding, self.encoding) self.filters = opts.fetch(:filters, nil) if opts.fetch(:header, true) header = @layout.header unless header.nil? || header.empty? begin write(header) rescue StandardError => err ::Logging.log_internal_error(err) end end end ::Logging::Appenders[@name] = self end # call-seq: # append( event ) # # Write the given _event_ to the logging destination. The log event will # be processed through the Layout associated with the Appender. # def append( event ) if @closed raise RuntimeError, "appender '<#{self.class.name}: #{@name}>' is closed" end # only append if the event level is less than or equal to the configured # appender level and the filter does not disallow it if event = allow(event) begin write(event) rescue StandardError => err ::Logging.log_internal_error(err) end end self end # call-seq: # appender << string # # Write the given _string_ to the logging destination "as is" -- no # layout formatting will be performed. # def <<( str ) if @closed raise RuntimeError, "appender '<#{self.class.name}: #{@name}>' is closed" end unless off? begin write(str) rescue StandardError => err ::Logging.log_internal_error(err) end end self end # call-seq: # level = :all # # Set the level for this appender; log events below this level will be # ignored by this appender. The level can be either a +String+, a # +Symbol+, or a +Fixnum+. An +ArgumentError+ is raised if this is not # the case. # # There are two special levels -- "all" and "off". The former will # enable recording of all log events. The latter will disable the # recording of all events. # # Example: # # appender.level = :debug # appender.level = "INFO" # appender.level = 4 # appender.level = 'off' # appender.level = :all # # These produce an +ArgumentError+ # # appender.level = Object # appender.level = -1 # appender.level = 1_000_000_000_000 # def level=( level ) lvl = case level when String, Symbol; ::Logging::level_num(level) when Fixnum; level when nil; 0 else raise ArgumentError, "level must be a String, Symbol, or Integer" end if lvl.nil? or lvl < 0 or lvl > ::Logging::LEVELS.length raise ArgumentError, "unknown level was given '#{level}'" end @level = lvl end # call-seq # appender.layout = Logging::Layouts::Basic.new # # Sets the layout to be used by this appender. # def layout=( layout ) unless layout.kind_of? ::Logging::Layout raise TypeError, "#{layout.inspect} is not a kind of 'Logging::Layout'" end @layout = layout end # Sets the filter(s) to be used by this appender. This method will clear the # current filter set and add those passed to this setter method. # # Examples # appender.filters = Logging::Filters::Level.new(:warn, :error) # def filters=( args ) @filters.clear add_filters(*args) end # Sets the filter(s) to be used by this appender. The filters will be # applied in the order that they are added to the appender. # # Examples # add_filters(Logging::Filters::Level.new(:warn, :error)) # # Returns this appender instance. def add_filters( *args ) args.flatten.each do |filter| next if filter.nil? unless filter.kind_of?(::Logging::Filter) raise TypeError, "#{filter.inspect} is not a kind of 'Logging::Filter'" end @filters << filter end self end # call-seq: # close( footer = true ) # # Close the appender and writes the layout footer to the logging # destination if the _footer_ flag is set to +true+. Log events will # no longer be written to the logging destination after the appender # is closed. # def close( footer = true ) return self if @closed ::Logging::Appenders.remove(@name) @closed = true flush if footer footer = @layout.footer unless footer.nil? || footer.empty? begin write(footer) rescue StandardError => err ::Logging.log_internal_error(err) end end end self end # call-seq: # closed? # # Returns +true+ if the appender has been closed; returns +false+ # otherwise. When an appender is closed, no more log events can be # written to the logging destination. # def closed? @closed end # Reopen the connection to the underlying logging destination. If the # connection is currently closed then it will be opened. If the connection # is currently open then it will be closed and immediately opened. # def reopen @closed = false self end # call-seq: # flush # # Call +flush+ to force an appender to write out any buffered log events. # Similar to IO#flush, so use in a similar fashion. # def flush self end # call-seq: # inspect => string # # Returns a string representation of the appender. # def inspect "<%s:0x%x name=\"%s\">" % [ self.class.name.sub(%r/^Logging::/, ''), self.object_id, self.name ] end # Returns the current Encoding for the appender or nil if an encoding has # not been set. # def encoding return @encoding if defined? @encoding @encoding = Object.const_defined?(:Encoding) ? Encoding.default_external : nil end # Set the appender encoding to the given value. The value can either be an # Encoding instance or a String or Symbol referring to a valid encoding. # # This method only applies to Ruby 1.9 or later. The encoding will always be # nil for older Rubies. # # value - The encoding as a String, Symbol, or Encoding instance. # # Raises ArgumentError if the value is not a valid encoding. def encoding=( value ) if value.nil? @encoding = nil else @encoding = Object.const_defined?(:Encoding) ? Encoding.find(value.to_s) : nil end end # Check to see if the event should be processed by the appender. An event will # be rejected if the event level is lower than the configured level for the # appender. Or it will be rejected if one of the filters rejects the event. # # event - The LogEvent to check # # Returns the event if it is allowed; returns `nil` if it is not allowed. def allow( event ) return nil if @level > event.level @filters.each do |filter| break unless event = filter.allow(event) end event end # Returns `true` if the appender has been turned off. This is useful for # appenders that write data to a remote location (such as syslog or email), # and that write encounters too many errors. The appender can turn itself off # to and log an error via the `Logging` logger. # # Set the appender's level to a valid value to turn it back on. def off? @level >= ::Logging::LEVELS.length end private # call-seq: # write( event ) # # Writes the given _event_ to the logging destination. Subclasses should # provide an implementation of this method. The _event_ can be either a # LogEvent or a String. If a LogEvent, then it will be formatted using # the layout given to the appender when it was created. # def write( event ) nil end # call-seq: # sync { block } # # Obtains an exclusive lock, runs the block, and releases the lock when # the block completes. This method is re-entrant so that a single thread # can call +sync+ multiple times without hanging the thread. # def sync( &block ) @mutex.synchronize(&block) end end # class Appender end # module Logging logging-2.0.0/lib/logging/layouts/0000755000175000017500000000000012551223063016402 5ustar globusglobuslogging-2.0.0/lib/logging/layouts/pattern.rb0000644000175000017500000005226312551223063020414 0ustar globusglobusmodule Logging::Layouts # Accessor / Factory for the Pattern layout. # # Returns a new Pattern layout instance def self.pattern( *args ) return ::Logging::Layouts::Pattern if args.empty? ::Logging::Layouts::Pattern.new(*args) end # A flexible layout configurable via a conversion pattern string. # # The goal of this class is to format a LogEvent and return the results as # a String. The results depend on the conversion pattern. # # The conversion pattern is closely related to the conversion pattern of # the sprintf function. A conversion pattern is composed of literal text # and format control expressions called conversion specifiers. # # You are free to insert any literal text within the conversion pattern. # # Each conversion specifier starts with a percent sign (%) and is followed # by optional format modifiers and a conversion character. The conversion # character specifies the type of data, e.g. logger, level, date, thread # ID. The format modifiers control such things as field width, padding, # left and right justification. The following is a simple example. # # Let the conversion pattern be "%-5l [%c]: %m\n" and assume that the # logging environment was set to use a Pattern layout. Then the statements # # root = Logging.logger[:root] # root.debug("Message 1") # root.warn("Message 2") # # would yield the output # # DEBUG [root]: Message 1 # WARN [root]: Message 2 # # Note that there is no explicit separator between text and conversion # specifiers. The pattern parser knows when it has reached the end of a # conversion specifier when it reads a conversion character. In the example # above the conversion specifier %-5l means the level of the logging event # should be left justified to a width of five characters. The recognized # conversion characters are # # [c] Used to output the name of the logger that generated the log # event. Supports an optional "precision" described further below. # [d] Used to output the date of the log event. The format of the # date is specified using the :date_pattern option when the Layout # is created. ISO8601 format is assumed if not date pattern is given. # [F] Used to output the file name where the logging request was issued. # [l] Used to output the level of the log event. # [L] Used to output the line number where the logging request was # issued. # [m] Used to output the application supplied message associated with # the log event. # [M] Used to output the method name where the logging request was # issued. # [h] Used to output the hostname # [p] Used to output the process ID of the currently running program. # [r] Used to output the number of milliseconds elapsed from the # construction of the Layout until creation of the log event. # [t] Used to output the object ID of the thread that generated the # log event. # [T] Used to output the name of the thread that generated the log event. # Name can be specified using Thread.current[:name] notation. Output # empty string if name not specified. This option helps to create # more human readable output for multi-threaded application logs. # [X] Used to output values from the Mapped Diagnostic Context. Requires # a key name to lookup the value from the context. More details are # listed below. # [x] Used to output values from the Nested Diagnostic Context. Supports # an optional context separator string. More details are listed below. # [%] The sequence '%%' outputs a single percent sign. # # The logger name directive 'c' accepts an optional precision that will # only print the rightmost number of name space identifiers for the logger. # By default the logger name is printed in full. For example, for the # logger name "Foo::Bar::Baz" the pattern %c{2} will output "Bar::Baz". # # The directives F, L, and M will only work if the Logger generating the # events is configured to generate tracing information. If this is not # the case these fields will always be empty. # # The directives for include diagnostic context information in the log # messages are X and x. For the Mapped Diagnostic Context the directive must # be accompanied by the key identifying the value to insert into the log # message. The X directive can appear multiple times to include multiple # values from the mapped context. # # %X{Cookie} Insert the current session cookie # %X{X-Session} Insert a session identifier # # For the Nested Diagnostic Context you need only include the directive # once. All contexts currently in the stack will be added to the log message # separated by spaces. If spaces are not your style, a separator string can # be given, too. # # %x Insert all contexts separated by spaces # %x{, } Insert all contexts separate by a comma and a space # # By default the relevant information is output as is. However, with the # aid of format modifiers it is possible to change the minimum field width, # the maximum field width and justification. # # The optional format modifier is placed between the percent sign and the # conversion character. # # The first optional format modifier is the left justification flag which # is just the minus (-) character. Then comes the optional minimum field # width modifier. This is a decimal constant that represents the minimum # number of characters to output. If the data item requires fewer # characters, it is padded on either the left or the right until the # minimum width is reached. The default is to pad on the left (right # justify) but you can specify right padding with the left justification # flag. The padding character is space. If the data item is larger than the # minimum field width, the field is expanded to accommodate the data. The # value is never truncated. # # This behavior can be changed using the maximum field width modifier which # is designated by a period followed by a decimal constant. If the data # item is longer than the maximum field, then the extra characters are # removed from the end of the data item. # # Below are various format modifier examples for the category conversion # specifier. # # %20c Left pad with spaces if the logger name is less than 20 # characters long # %-20c Right pad with spaces if the logger name is less than 20 # characters long # %.30c Truncates the logger name if it is longer than 30 characters # %20.30c Left pad with spaces if the logger name is shorter than # 20 characters. However, if the logger name is longer than # 30 characters, then truncate the name. # %-20.30c Right pad with spaces if the logger name is shorter than # 20 characters. However, if the logger name is longer than # 30 characters, then truncate the name. # # Below are examples of some conversion patterns. # # %.1l, [%d] %5l -- %c: %m\n # # This is how the Logger class in the Ruby standard library formats # messages. The main difference will be in the date format (the Pattern # Layout uses the ISO8601 date format). Set the :date_method on the # Pattern Layout to be 'to_s' and then the date formats will agree. # class Pattern < ::Logging::Layout # :stopdoc: # default date format ISO8601 = "%Y-%m-%dT%H:%M:%S".freeze # call-seq: # Pattern.create_date_format_methods( pl ) # # This method will create the +date_format+ method in the given Pattern # Layout _pl_ based on the configured date pattern and/or date method # specified by the user. # def self.create_date_format_methods( pl ) code = "undef :format_date if method_defined? :format_date\n" code << "def format_date( time )\n" if pl.date_method.nil? if pl.date_pattern =~ %r/%s/ code << <<-CODE dp = '#{pl.date_pattern}'.gsub('%s','%06d' % time.usec) time.strftime dp CODE else code << "time.strftime '#{pl.date_pattern}'\n" end else code << "time.#{pl.date_method}\n" end code << "end\n" ::Logging.log_internal(0) {code} pl._meta_eval(code, __FILE__, __LINE__) end # call-seq: # Pattern.create_format_method( pl ) # # This method will create the `format` method in the given Pattern # Layout `pl` based on the configured format pattern specified by the # user. # def self.create_format_method( pl ) builder = FormatMethodBuilder.new(pl) code = builder.build_code ::Logging.log_internal(0) { code } pl._meta_eval(code, __FILE__, __LINE__) end # :startdoc: # call-seq: # Pattern.new( opts ) # # Creates a new Pattern layout using the following options. # # :pattern => "[%d] %-5l -- %c : %m\n" # :date_pattern => "%Y-%m-%d %H:%M:%S" # :date_method => 'usec' or 'to_s' # :color_scheme => :default # # If used, :date_method will supersede :date_pattern. # # The :color_scheme is used to apply color formatting to the log messages. # Individual tokens can be colorized witch the level token [%l] receiving # distinct colors based on the level of the log event. The entire # generated log message can also be colorized based on the level of the # log event. See the ColorScheme documentation for more details. # def initialize( opts = {} ) super @created_at = Time.now @date_pattern = opts.fetch(:date_pattern, nil) @date_method = opts.fetch(:date_method, nil) @date_pattern = ISO8601 if @date_pattern.nil? && @date_method.nil? @pattern = opts.fetch(:pattern, "[%d] %-#{::Logging::MAX_LEVEL_LENGTH}l -- %c : %m\n") cs_name = opts.fetch(:color_scheme, nil) @color_scheme = case cs_name when false, nil; nil when true; ::Logging::ColorScheme[:default] else ::Logging::ColorScheme[cs_name] end self.class.create_date_format_methods(self) self.class.create_format_method(self) end attr_reader :pattern, :date_pattern, :date_method, :color_scheme # call-seq: # appender.pattern = "[%d] %-5l -- %c : %m\n" # # Set the message formatting pattern to be used by the layout. # def pattern=( var ) @pattern = var Pattern.create_format_method(self) end # call-seq: # appender.date_pattern = "%Y-%m-%d %H:%M:%S" # # Set the date formatting pattern to be used when outputting timestamps # in the log messages. # def date_pattern=( var ) @date_pattern = var Pattern.create_date_format_methods(self) end # call-seq: # appender.date_method = 'to_s' # appender.date_method = :usec # # Set the date method to be used when outputting timestamps in the log # messages. If a date method is configured, the output of that method # will be used in leu of the date pattern. # def date_method=( var ) @date_method = var Pattern.create_date_format_methods(self) end # :stopdoc: # Evaluates the given string of `code` if the singleton class of this # Pattern Layout object. # # Returns this Pattern Layout instance. def _meta_eval( code, file = nil, line = nil ) meta = class << self; self end meta.class_eval code, file, line self end # This class is used to build the `format` method for the Pattern layout. It # parses the user defined pattern and emits Ruby source code (as a string) # that can be `eval`d in the context of the Pattern layout instance. class FormatMethodBuilder # Matches the first directive encountered and the stuff around it. # # * $1 is the stuff before directive or "" if not applicable # * $2 is the %#.# match within directive group # * $3 is the directive letter # * $4 is the precision specifier for the logger name # * $5 is the stuff after the directive or "" if not applicable DIRECTIVE_RGXP = %r/([^%]*)(?:(%-?\d*(?:\.\d+)?)([a-zA-Z%])(?:\{([^\}]+)\})?)?(.*)/m # Arguments to sprintf keyed to directive letters DIRECTIVE_TABLE = { 'c' => 'event.logger'.freeze, 'd' => 'format_date(event.time)'.freeze, 'F' => 'event.file'.freeze, 'l' => '::Logging::LNAMES[event.level]'.freeze, 'L' => 'event.line'.freeze, 'm' => 'format_obj(event.data)'.freeze, 'M' => 'event.method'.freeze, 'h' => "'#{Socket.gethostname}'".freeze, 'p' => 'Process.pid'.freeze, 'r' => 'Integer((event.time-@created_at)*1000).to_s'.freeze, 't' => 'Thread.current.object_id.to_s'.freeze, 'T' => 'Thread.current[:name]'.freeze, 'X' => :placeholder, 'x' => :placeholder, '%' => :placeholder }.freeze # Human name aliases for directives - used for colorization of tokens COLOR_ALIAS_TABLE = { 'c' => :logger, 'd' => :date, 'm' => :message, 'h' => :hostname, 'p' => :pid, 'r' => :time, 'T' => :thread, 't' => :thread_id, 'F' => :file, 'L' => :line, 'M' => :method, 'X' => :mdc, 'x' => :ndc }.freeze attr_reader :layout attr_accessor :pattern attr_reader :color_scheme attr_reader :sprintf_args attr_reader :format_string attr_accessor :name_map_count # Creates the format method builder and initializes some variables from # the given Patter layout instance. # # pattern_layout - The Pattern Layout instance # def initialize( pattern_layout ) @layout = pattern_layout @pattern = layout.pattern.dup @color_scheme = layout.color_scheme @sprintf_args = [] @format_string = '"' @name_map_count = 0 end # Returns `true` if the log messages should be colorized. def colorize? color_scheme && !color_scheme.lines? end # Returns `true` if the log messages should be colorized by line. def colorize_lines? color_scheme && color_scheme.lines? end # Returns `true` if the log levels have special colorization defined. def colorize_levels? color_scheme && color_scheme.levels? end # This method returns a String which can be `eval`d in the context of the # Pattern layout. When it is `eval`d, a `format` method is defined in the # Pattern layout. # # At the heart of the format method is `sprintf`. The conversion pattern # specified in the Pattern layout is parsed and converted into a format # string and corresponding arguments list. The format string and arguments # are then processed by `sprintf` to format log events. # # Returns a Ruby code as a String. def build_code build_format_string sprintf = "sprintf(" sprintf << format_string sprintf << ', ' + sprintf_args.join(', ') unless sprintf_args.empty? sprintf << ")" if colorize_lines? sprintf = "color_scheme.color(#{sprintf}, ::Logging::LNAMES[event.level])" end code = "undef :format if method_defined? :format\n" code << "def format( event )\n#{sprintf}\nend\n" end # This method builds the format string used by `sprintf` to format log # events. The conversion pattern given by the user is iteratively parsed # by a regular expression into separate format directives. Each directive # builds up the format string and the corresponding arguments list that # will be formatted. # # The actual building of the format string is handled by separate # directive specific methods. Those handlers also populate the arguments # list passed to `sprintf`. # # Returns the format String. def build_format_string while true match = DIRECTIVE_RGXP.match(pattern) _, pre, format, directive, precision, post = *match format_string << pre unless pre.empty? case directive when '%'; format_string << '%%' when 'c'; handle_logger( format, directive, precision ) when 'l'; handle_level( format, directive, precision ) when 'X'; handle_mdc( format, directive, precision ) when 'x'; handle_ndc( format, directive, precision ) when *DIRECTIVE_TABLE.keys handle_directives(format, directive, precision) when nil; break else raise ArgumentError, "illegal format character - '#{directive}'" end break if post.empty? self.pattern = post end format_string << '"' end # Add the logger name to the `format_string` and the `sprintf_args`. The # `slice` argument is a little interesting - this is the number of logger # name segments to keep. If we have a logger named "Foo::Bar::Baz" and our # `slice` is 2, then "Bar::Baz" will appear in the generated log message. # So the `slice` selects the last two parts of the logger name. # # format - format String # directive - the directive character ('c') # slice - the number of name segments to keep # # Returns nil def handle_logger( format, directive, slice ) fmt = format + 's' fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize? format_string << fmt sprintf_args << DIRECTIVE_TABLE[directive].dup if slice numeric = Integer(slice) rescue nil if numeric raise ArgumentError, "logger name slice must be an integer greater than zero: #{numeric}" unless numeric > 0 sprintf_args.last << ".split(::Logging::Repository::PATH_DELIMITER)" \ ".last(#{slice}).join(::Logging::Repository::PATH_DELIMITER)" else format_string << "{#{slice}}" end end nil end # Add the log event level to the `format_string` and the `sprintf_args`. # The color scheme is taken into account when formatting the log event # level. # # format - format String # directive - the directive character ('l') # precision - added back to the format string # # Returns nil def handle_level( format, directive, precision ) if colorize_levels? name_map = ::Logging::LNAMES.map { |name| color_scheme.color(("#{format}s" % name), name) } var = "@name_map_#{name_map_count}" layout.instance_variable_set(var.to_sym, name_map) self.name_map_count += 1 format_string << '%s' format_string << "{#{precision}}" if precision sprintf_args << "#{var}[event.level]" else format_string << format + 's' format_string << "{#{precision}}" if precision sprintf_args << DIRECTIVE_TABLE[directive] end nil end # Add a Mapped Diagnostic Context to the `format_string` and the # `sprintf_args`. Only one MDC value is added at a time, so this directive # can appear multiple times using various keys. # # format - format String # directive - the directive character ('X') # key - which MDC value to add to the log message # # Returns nil def handle_mdc( format, directive, key ) raise ArgumentError, "MDC must have a key reference" unless key fmt = format + 's' fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize? format_string << fmt sprintf_args << "::Logging.mdc['#{key}']" nil end # Add a Nested Diagnostic Context to the `format_string` and the # `sprintf_args`. Since the NDC is an Array of values, the directive will # appear only once in the conversion pattern. A `separator` is inserted # between the values in generated log message. # # format - format String # directive - the directive character ('x') # separator - used to separate the values in the NDC array # # Returns nil def handle_ndc( format, directive, separator ) fmt = format + 's' fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize? format_string << fmt separator = separator.to_s separator = ' ' if separator.empty? sprintf_args << "::Logging.ndc.context.join('#{separator}')" nil end # Handles the rest of the directives; none of these need any special # handling. # # format - format String # directive - the directive character # precision - added back to the format string # # Returns nil def handle_directives( format, directive, precision ) fmt = format + 's' fmt = color_scheme.color(fmt, COLOR_ALIAS_TABLE[directive]) if colorize? format_string << fmt format_string << "{#{precision}}" if precision sprintf_args << DIRECTIVE_TABLE[directive] nil end end # :startdoc: end end logging-2.0.0/lib/logging/layouts/parseable.rb0000644000175000017500000002216512551223063020673 0ustar globusglobusrequire 'socket' module Logging::Layouts # Accessor for the Parseable layout. # def self.parseable ::Logging::Layouts::Parseable end # Factory for the Parseable layout using JSON formatting. # def self.json( *args ) ::Logging::Layouts::Parseable.json(*args) end # Factory for the Parseable layout using YAML formatting. # def self.yaml( *args ) ::Logging::Layouts::Parseable.yaml(*args) end # This layout will produce parseable log output in either JSON or YAML # format. This makes it much easier for machines to parse log files and # perform analysis on those logs. # # The information about the log event can be configured when the layout is # created. Any or all of the following labels can be set as the _items_ to # log: # # 'logger' Used to output the name of the logger that generated the # log event. # 'timestamp' Used to output the timestamp of the log event. # 'level' Used to output the level of the log event. # 'message' Used to output the application supplied message # associated with the log event. # 'file' Used to output the file name where the logging request # was issued. # 'line' Used to output the line number where the logging request # was issued. # 'method' Used to output the method name where the logging request # was issued. # 'hostname' Used to output the hostname # 'pid' Used to output the process ID of the currently running # program. # 'millis' Used to output the number of milliseconds elapsed from # the construction of the Layout until creation of the log # event. # 'thread_id' Used to output the object ID of the thread that generated # the log event. # 'thread' Used to output the name of the thread that generated the # log event. Name can be specified using Thread.current[:name] # notation. Output empty string if name not specified. This # option helps to create more human readable output for # multithread application logs. # # These items are supplied to the layout as an array of strings. The items # 'file', 'line', and 'method' will only work if the Logger generating the # events is configured to generate tracing information. If this is not the # case these fields will always be empty. # # When configured to output log events in YAML format, each log message # will be formatted as a hash in it's own YAML document. The hash keys are # the name of the item, and the value is what you would expect it to be. # Therefore, for the default set of times log message would appear as # follows: # # --- # timestamp: 2009-04-17T16:15:42 # level: INFO # logger: Foo::Bar # message: this is a log message # --- # timestamp: 2009-04-17T16:15:43 # level: ERROR # logger: Foo # message: Oooops!! # # The output order of the fields is not guaranteed to be the same as the # order specified in the _items_ list. This is because Ruby hashes are not # ordered by default (unless you're running this in Ruby 1.9). # # When configured to output log events in JSON format, each log message # will be formatted as an object (in the JSON sense of the word) on it's # own line in the log output. Therefore, to parse the output you must read # it line by line and parse the individual objects. Taking the same # example above the JSON output would be: # # {"timestamp":"2009-04-17T16:15:42","level":"INFO","logger":"Foo::Bar","message":"this is a log message"} # {"timestamp":"2009-04-17T16:15:43","level":"ERROR","logger":"Foo","message":" Oooops!!"} # # The output order of the fields is guaranteed to be the same as the order # specified in the _items_ list. # class Parseable < ::Logging::Layout # :stopdoc: # Arguments to sprintf keyed to directive letters DIRECTIVE_TABLE = { 'logger' => 'event.logger'.freeze, 'timestamp' => 'iso8601_format(event.time)'.freeze, 'level' => '::Logging::LNAMES[event.level]'.freeze, 'message' => 'format_obj(event.data)'.freeze, 'file' => 'event.file'.freeze, 'line' => 'event.line'.freeze, 'method' => 'event.method'.freeze, 'hostname' => "'#{Socket.gethostname}'".freeze, 'pid' => 'Process.pid'.freeze, 'millis' => 'Integer((event.time-@created_at)*1000)'.freeze, 'thread_id' => 'Thread.current.object_id'.freeze, 'thread' => 'Thread.current[:name]'.freeze, 'mdc' => 'Logging::MappedDiagnosticContext.context'.freeze, 'ndc' => 'Logging::NestedDiagnosticContext.context'.freeze } # call-seq: # Pattern.create_yaml_format_methods( layout ) # # This method will create the +format+ method in the given Parseable # _layout_ based on the configured items for the layout instance. # def self.create_yaml_format_method( layout ) code = "undef :format if method_defined? :format\n" code << "def format( event )\nstr = {\n" code << layout.items.map {|name| "'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}" }.join(",\n") code << "\n}.to_yaml\nreturn str\nend\n" (class << layout; self end).class_eval(code, __FILE__, __LINE__) end # call-seq: # Pattern.create_json_format_methods( layout ) # # This method will create the +format+ method in the given Parseable # _layout_ based on the configured items for the layout instance. # def self.create_json_format_method( layout ) code = "undef :format if method_defined? :format\n" code << "def format( event )\nh = {\n" code << layout.items.map {|name| "'#{name}' => #{Parseable::DIRECTIVE_TABLE[name]}" }.join(",\n") code << "\n}\nMultiJson.encode(h) << \"\\n\"\nend\n" (class << layout; self end).class_eval(code, __FILE__, __LINE__) end # :startdoc: # call-seq: # Parseable.json( opts ) # # Create a new Parseable layout that outputs log events using JSON style # formatting. See the initializer documentation for available options. # def self.json( opts = {} ) opts[:style] = 'json' new(opts) end # call-seq: # Parseable.yaml( opts ) # # Create a new Parseable layout that outputs log events using YAML style # formatting. See the initializer documentation for available options. # def self.yaml( opts = {} ) opts[:style] = 'yaml' new(opts) end # call-seq: # Parseable.new( opts ) # # Creates a new Parseable layout using the following options: # # :style => :json or :yaml # :items => %w[timestamp level logger message] # def initialize( opts = {} ) super @created_at = Time.now @style = opts.fetch(:style, 'json').to_s.intern self.items = opts.fetch(:items, %w[timestamp level logger message]) end attr_reader :items # call-seq: # layout.items = %w[timestamp level logger message] # # Set the log event items that will be formatted by this layout. These # items, and only these items, will appear in the log output. # def items=( ary ) @items = Array(ary).map {|name| name.to_s.downcase} valid = DIRECTIVE_TABLE.keys @items.each do |name| raise ArgumentError, "unknown item - #{name.inspect}" unless valid.include? name end create_format_method end # Public: Take a given object and convert it into a format suitable for # inclusion as a log message. The conversion allows the object to be more # easily expressed in YAML or JSON form. # # If the object is an Exception, then this method will return a Hash # containing the exception class name, message, and backtrace (if any). # # obj - The Object to format # # Returns the formatted Object. # def format_obj( obj ) case obj when Exception h = { :class => obj.class.name, :message => obj.message } h[:backtrace] = obj.backtrace if @backtrace && !obj.backtrace.nil? h when Time iso8601_format(obj) else obj end end private # Call the appropriate class level create format method based on the # style of this parseable layout. # def create_format_method case @style when :json; Parseable.create_json_format_method(self) when :yaml; Parseable.create_yaml_format_method(self) else raise ArgumentError, "unknown format style '#@style'" end end # Convert the given time _value_ into an ISO8601 formatted time string. # def iso8601_format( value ) str = value.strftime('%Y-%m-%dT%H:%M:%S') str << ('.%06d' % value.usec) offset = value.gmt_offset.abs return str << 'Z' if offset == 0 offset = sprintf('%02d:%02d', offset / 3600, offset % 3600 / 60) return str << (value.gmt_offset < 0 ? '-' : '+') << offset end end # Parseable end # Logging::Layouts logging-2.0.0/lib/logging/layouts/basic.rb0000644000175000017500000000223212551223063020007 0ustar globusglobus module Logging::Layouts # Accessor / Factory for the Basic layout. # def self.basic( *args ) return ::Logging::Layouts::Basic if args.empty? ::Logging::Layouts::Basic.new(*args) end # The +Basic+ layout class provides methods for simple formatting of log # events. The resulting string follows the format below. # # LEVEL LoggerName : log message # # _LEVEL_ is the log level of the event. _LoggerName_ is the name of the # logger that generated the event. log message is the message # or object that was passed to the logger. If multiple message or objects # were passed to the logger then each will be printed on its own line with # the format show above. # class Basic < ::Logging::Layout # call-seq: # format( event ) # # Returns a string representation of the given logging _event_. See the # class documentation for details about the formatting used. # def format( event ) obj = format_obj(event.data) sprintf("%*s %s : %s\n", ::Logging::MAX_LEVEL_LENGTH, ::Logging::LNAMES[event.level], event.logger, obj) end end # Basic end # Logging::Layouts logging-2.0.0/lib/logging/rails_compat.rb0000644000175000017500000000167312551223063017713 0ustar globusglobus if defined? ActiveSupport module Logging # Rails compatibility module. # # The ActiveSupport gem adds a few methods to the default Ruby logger, and # some Rails extensions expect these methods to exist. Those methods are # implemented in this module and included in the Logging::Logger class when # the ActiveSupport gem is present. # module RailsCompat # A no-op implementation of the +formatter+ method. def formatter; end # A no-op implementation of the +silence+ method. Setting of log levels # should be done during the Logging configuration. It is the author's # opinion that overriding the log level programmatically is a logical # error. # # Please see https://github.com/TwP/logging/issues/11 for a more detail # discussion of the issue. # def silence( *args ) yield self end end # RailsCompat Logger.send :include, RailsCompat end # Logging end # if defined? logging-2.0.0/lib/logging/utils.rb0000644000175000017500000001076212551223063016375 0ustar globusglobus require 'thread' require 'rbconfig' # -------------------------------------------------------------------------- class String # call-seq: # shrink( width, ellipses = '...' ) #=> string # # Shrink the size of the current string to the given _width_ by removing # characters from the middle of the string and replacing them with # _ellipses_. If the _width_ is greater than the length of the string, the # string is returned unchanged. If the _width_ is less than the length of # the _ellipses_, then the _ellipses_ are returned. # def shrink( width, ellipses = '...') raise ArgumentError, "width cannot be negative: #{width}" if width < 0 return self if length <= width remove = length - width + ellipses.length return ellipses.dup if remove >= length left_end = (length + 1 - remove) / 2 right_start = left_end + remove left = self[0,left_end] right = self[right_start,length-right_start] left << ellipses << right end end # -------------------------------------------------------------------------- class Module # call-seq: # logger_name #=> string # # Returns a predictable logger name for the current module or class. If # used within an anonymous class, the first non-anonymous class name will # be used as the logger name. If used within a meta-class, the name of the # actual class will be used as the logger name. If used within an # anonymous module, the string 'anonymous' will be returned. # def logger_name return name unless name.nil? or name.empty? # check if this is a metaclass (or eigenclass) if ancestors.include? Class inspect =~ %r/#]+)>/ return $1 end # see if we have a superclass if respond_to? :superclass return superclass.logger_name end # we are an anonymous module ::Logging.log_internal(-2) { 'cannot return a predictable, unique name for anonymous modules' } return 'anonymous' end end # -------------------------------------------------------------------------- class File # Returns true if another process holds an exclusive lock on the # file. Returns false if this is not the case. # # If a block of code is passed to this method, it will be run iff # this process can obtain an exclusive lock on the file. The block will be # run while this lock is held, and the exclusive lock will be released when # the method returns. # # The exclusive lock is requested in a non-blocking mode. This method will # return immediately (and the block will not be executed) if an exclusive # lock cannot be obtained. # def flock? status = flock(LOCK_EX|LOCK_NB) case status when false; true when 0; block_given? ? yield : false else raise SystemCallError, "flock failed with status: #{status}" end ensure flock LOCK_UN end # Execute a block in the context of a shared lock on this file. A # shared lock will be obtained on the file, the block executed, and the lock # released. # def flock_sh flock LOCK_SH yield ensure flock LOCK_UN end # :stopdoc: conf = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG if conf['host_os'] =~ /mswin|windows|cygwin|mingw/i # don't lock files on windows undef :flock?, :flock_sh def flock?() yield; end def flock_sh() yield; end end # :startdoc: end # -------------------------------------------------------------------------- module FileUtils # Concatenate the contents of the _src_ file to the end of the _dest_ file. # If the _dest_ file does not exist, then the _src_ file is copied to the # _dest_ file using +copy_file+. # def concat( src, dest ) if File.exist?(dest) bufsize = File.stat(dest).blksize || 8192 buffer = String.new File.open(dest, 'a') { |d| File.open(src, 'r') { |r| while bytes = r.read(bufsize, buffer) d.syswrite bytes end } } else copy_file(src, dest) end end module_function :concat end # -------------------------------------------------------------------------- class ReentrantMutex < Mutex def initialize super @locker = nil end alias_method :original_synchronize, :synchronize def synchronize if @locker == Thread.current yield else original_synchronize { begin @locker = Thread.current yield ensure @locker = nil end } end end end # ReentrantMutex logging-2.0.0/lib/logging/proxy.rb0000644000175000017500000000417112551223063016413 0ustar globusglobus module Logging # Defines a Proxy that will log all method calls on the proxied object. This # class uses +method_missing+ on a "blank slate" object to intercept all # method calls. The method name being called and the arguments are all # logged to the proxied object's logger instance. The log level and other # settings for the proxied object are honored by the Proxy instance. # # If you want, you can also supply your own +method_missing+ code as a block # to the constructor. # # Proxy.new(object) do |name, *args, &block| # # code to be executed before the proxied method # result = @object.send(name, *args, &block) # # code to be executed after the proxied method # result # <-- always return the result # end # # The proxied object is available as the "@object" variable. The logger is # available as the "@logger" variable. # class Proxy # :stopdoc: KEEPERS = %r/^__|^object_id$|^initialize$/ instance_methods(true).each { |m| undef_method m unless m[KEEPERS] } private_instance_methods(true).each { |m| undef_method m unless m[KEEPERS] } # :startdoc: # Create a new proxy for the given _object_. If an optional _block_ is # given it will be called before the proxied method. This _block_ will # replace the +method_missing+ implementation # def initialize( object, &block ) Kernel.raise ArgumentError, "Cannot proxy nil" if nil.equal? object @object = object @leader = @object.is_a?(Class) ? "#{@object.name}." : "#{@object.class.name}#" @logger = Logging.logger[object] if block eigenclass = class << self; self; end eigenclass.__send__(:define_method, :method_missing, &block) end end # All hail the magic of method missing. Here is where we are going to log # the method call and then forward to the proxied object. The return value # from the proxied objet method call is passed back. # def method_missing( name, *args, &block ) @logger << "#@leader#{name}(#{args.inspect[1..-2]})\n" @object.send(name, *args, &block) end end # Proxy end # Logging logging-2.0.0/lib/logging/filter.rb0000644000175000017500000000121212551223063016510 0ustar globusglobusmodule Logging # The `Filter` class allows for filtering messages based on event # properties independently of the standard minimum-level restriction. # # All other Filters inherit from this class, and must override the # `allow` method to return the event if it should be allowed into the log. # Otherwise the `allow` method should return `nil`. class Filter # Returns the event if it should be allowed into the log. Returns `nil` if # the event should _not_ be allowed into the log. Subclasses should override # this method and provide their own filtering semantics. def allow( event ) event end end end logging-2.0.0/lib/logging/layouts.rb0000644000175000017500000000027612551223063016734 0ustar globusglobus module Logging module Layouts; end require libpath('logging/layouts/basic') require libpath('logging/layouts/parseable') require libpath('logging/layouts/pattern') end # Logging logging-2.0.0/lib/logging/appenders/0000755000175000017500000000000012551223063016663 5ustar globusglobuslogging-2.0.0/lib/logging/appenders/io.rb0000644000175000017500000000461612551223063017626 0ustar globusglobus module Logging::Appenders # Accessor / Factory for the IO appender. # def self.io( *args ) return ::Logging::Appenders::IO if args.empty? ::Logging::Appenders::IO.new(*args) end # This class provides an Appender that can write to any IO stream # configured for writing. # class IO < ::Logging::Appender include Buffering # The method that will be used to close the IO stream. Defaults to :close # but can be :close_read, :close_write or nil. When nil, the IO stream # will not be closed when the appender's close method is called. # attr_accessor :close_method # call-seq: # IO.new( name, io ) # IO.new( name, io, :layout => layout ) # # Creates a new IO Appender using the given name that will use the _io_ # stream as the logging destination. # def initialize( name, io, opts = {} ) unless io.respond_to? :syswrite raise TypeError, "expecting an IO object but got '#{io.class.name}'" end @io = io @io.sync = true if io.respond_to? :sync= # syswrite complains if the IO stream is buffered @io.flush rescue nil # syswrite also complains if in unbuffered mode and buffer isn't empty @close_method = :close super(name, opts) configure_buffering(opts) end # call-seq: # close( footer = true ) # # Close the appender and writes the layout footer to the logging # destination if the _footer_ flag is set to +true+. Log events will # no longer be written to the logging destination after the appender # is closed. # def close( *args ) return self if @io.nil? super io, @io = @io, nil unless [STDIN, STDERR, STDOUT].include?(io) io.send(@close_method) if @close_method and io.respond_to? @close_method end rescue IOError ensure return self end private # This method is called by the buffering code when messages need to be # written to the logging destination. # def canonical_write( str ) return self if @io.nil? str = str.force_encoding(encoding) if encoding and str.encoding != encoding @io.syswrite str self rescue StandardError => err self.level = :off ::Logging.log_internal {"appender #{name.inspect} has been disabled"} ::Logging.log_internal_error(err) end end # IO end # Logging::Appenders logging-2.0.0/lib/logging/appenders/string_io.rb0000644000175000017500000000402212551223063021203 0ustar globusglobus module Logging::Appenders # Accessor / Factory for the StringIo appender. # def self.string_io( *args ) return ::Logging::Appenders::StringIo if args.empty? ::Logging::Appenders::StringIo.new(*args) end # This class provides an Appender that can write to a StringIO instance. # This is very useful for testing log message output. # class StringIo < ::Logging::Appenders::IO # The StringIO instance the appender is writing to. attr_reader :sio # call-seq: # StringIo.new( name, opts = {} ) # # Creates a new StringIo appender that will append log messages to a # StringIO instance. # def initialize( name, opts = {} ) @sio = StringIO.new @sio.extend IoToS @pos = 0 super(name, @sio, opts) end # Reopen the underlying StringIO instance. If the instance is currently # closed then it will be opened. If the instance is currently open then it # will be closed and immediately opened. # def reopen @mutex.synchronize { if defined? @io and @io flush @io.close rescue nil end @io = @sio = StringIO.new @sio.extend IoToS @pos = 0 } super self end # Clears the internal StringIO instance. All log messages are removed # from the buffer. # def clear @mutex.synchronize { @pos = 0 @sio.seek 0 @sio.truncate 0 } end alias_method :reset, :clear %w[read readline readlines].each do|m| class_eval <<-CODE, __FILE__, __LINE__+1 def #{m}( *args ) sync { begin @sio.seek @pos rv = @sio.#{m}(*args) @pos = @sio.tell rv rescue EOFError nil end } end CODE end # :stopdoc: module IoToS def to_s seek 0 str = read seek 0 return str end end # :startdoc: end # StringIo end # Logging::Appenders logging-2.0.0/lib/logging/appenders/buffering.rb0000644000175000017500000003212412551223063021161 0ustar globusglobus module Logging::Appenders # The Buffering module is used to implement buffering of the log messages # in a given appender. The size of the buffer can be specified, and the # buffer can be configured to auto-flush at a given threshold. The # threshold can be a single message or a very large number of messages. # # Log messages of a certain level can cause the buffer to be flushed # immediately. If an error occurs, all previous messages and the error # message will be written immediately to the logging destination if the # buffer is configured to do so. # module Buffering # Default buffer size DEFAULT_BUFFER_SIZE = 500; # The buffer holding the log messages attr_reader :buffer # The auto-flushing setting. When the buffer reaches this size, all # messages will be be flushed automatically. attr_reader :auto_flushing # When set, the buffer will be flushed at regular intervals defined by the # flush_period. attr_reader :flush_period # Messages will be written in chunks. This controls the number of messages # to pull from the buffer for each write operation. The default is to pull # all messages from the buffer at once. attr_accessor :write_size # Setup the message buffer and other variables for automatically and # periodically flushing the buffer. # def initialize( *args, &block ) @buffer = [] @immediate = [] @auto_flushing = 1 @flush_period = @periodic_flusher = nil super(*args, &block) end # Close the message buffer by flushing all log events to the appender. If a # periodic flusher thread is running, shut it down and allow it to exit. # def close( *args ) flush if @periodic_flusher @periodic_flusher.stop @periodic_flusher = nil Thread.pass end super(*args) end # Reopen the connection to the underlying logging destination. In addition # if the appender is configured for periodic flushing, then the flushing # thread will be stopped and restarted. # def reopen _setup_periodic_flusher super end # Call `flush` to force an appender to write out any buffered log events. # Similar to `IO#flush`, so use in a similar fashion. def flush return self if @buffer.empty? ary = nil sync { ary = @buffer.dup @buffer.clear } if ary.length <= write_size str = ary.join canonical_write str unless str.empty? else ary.each_slice(write_size) do |a| str = a.join canonical_write str unless str.empty? end end self end # Clear the underlying buffer of all log events. These events will not be # appended to the logging destination; they will be lost. def clear! sync { @buffer.clear } end # Configure the levels that will trigger an immediate flush of the # logging buffer. When a log event of the given level is seen, the # buffer will be flushed immediately. Only the levels explicitly given # in this assignment will flush the buffer; if an "error" message is # configured to immediately flush the buffer, a "fatal" message will not # even though it is a higher level. Both must be explicitly passed to # this assignment. # # You can pass in a single level name or number, an array of level # names or numbers, or a string containing a comma separated list of level # names or numbers. # # immediate_at = :error # immediate_at = [:error, :fatal] # immediate_at = "warn, error" # def immediate_at=( level ) @immediate.clear # get the immediate levels -- no buffering occurs at these levels, and # a log message is written to the logging destination immediately immediate_at = case level when String; level.split(',').map {|x| x.strip} when Array; level else Array(level) end immediate_at.each do |lvl| num = ::Logging.level_num(lvl) next if num.nil? @immediate[num] = true end end # Configure the auto-flushing threshold. Auto-flushing is used to flush # the contents of the logging buffer to the logging destination # automatically when the buffer reaches a certain threshold. # # By default, the auto-flushing will be configured to flush after each # log message. # # The allowed settings are as follows: # # N : flush after every N messages (N is an integer) # true : flush after each log message # false OR # nil OR # 0 : only flush when the buffer is full (500 messages) # # If the default buffer size of 500 is too small, then you can manually # configure it to be as large as you want. This will consume more memory. # # auto_flushing = 42_000 # def auto_flushing=( period ) @auto_flushing = case period when true; 1 when false, nil, 0; DEFAULT_BUFFER_SIZE when Integer; period when String; Integer(period) else raise ArgumentError, "unrecognized auto_flushing period: #{period.inspect}" end if @auto_flushing <= 0 raise ArgumentError, "auto_flushing period must be greater than zero: #{period.inspect}" end @auto_flushing = DEFAULT_BUFFER_SIZE if @flush_period && @auto_flushing <= 1 end # Configure periodic flushing of the message buffer. Periodic flushing is # used to flush the contents of the logging buffer at some regular # interval. Periodic flushing is disabled by default. # # When enabling periodic flushing the flush period should be set using one # of the following formats: "HH:MM:SS" or seconds as an numeric or string. # # "01:00:00" : every hour # "00:05:00" : every 5 minutes # "00:00:30" : every 30 seconds # 60 : every 60 seconds (1 minute) # "120" : every 120 seconds (2 minutes) # # For the periodic flusher to work properly, the auto-flushing threshold # will be set to the default value of 500. The auto-flushing threshold can # be changed, but it must be greater than 1. # # To disable the periodic flusher simply set the flush period to +nil+. # The auto-flushing threshold will not be changed; it must be disabled # manually if so desired. # def flush_period=( period ) period = case period when Integer, Float, nil; period when String; num = _parse_hours_minutes_seconds(period) || _parse_numeric(period) num = ArgumentError.new("unrecognized flush period: #{period.inspect}") if num.nil? num else ArgumentError.new("unrecognized flush period: #{period.inspect}") end raise period if Exception === period @flush_period = period _setup_periodic_flusher end protected # Configure the buffering using the arguments found in the give options # hash. This method must be called in order to use the message buffer. # The supported options are "immediate_at" and "auto_flushing". Please # refer to the documentation for those methods to see the allowed # options. # def configure_buffering( opts ) ::Logging.init unless ::Logging.initialized? self.immediate_at = opts.fetch(:immediate_at, '') self.auto_flushing = opts.fetch(:auto_flushing, true) self.flush_period = opts.fetch(:flush_period, nil) self.write_size = opts.fetch(:write_size, DEFAULT_BUFFER_SIZE) end # Returns true if the _event_ level matches one of the configured # immediate logging levels. Otherwise returns false. # def immediate?( event ) return false unless event.respond_to? :level @immediate[event.level] end private # call-seq: # write( event ) # # Writes the given _event_ to the logging destination. The _event_ can # be either a LogEvent or a String. If a LogEvent, then it will be # formatted using the layout given to the appender when it was created. # # The _event_ will be formatted and then buffered until the # "auto_flushing" level has been reached. At this time the canonical_write # method will be used to log all events stored in the buffer. # def write( event ) str = event.instance_of?(::Logging::LogEvent) ? layout.format(event) : event.to_s return if str.empty? if @auto_flushing == 1 canonical_write(str) else sync { str = str.force_encoding(encoding) if encoding && str.encoding != encoding @buffer << str } @periodic_flusher.signal if @periodic_flusher flush if @buffer.length >= @auto_flushing || immediate?(event) end self end # Attempt to parse an hours/minutes/seconds value from the string and return # an integer number of seconds. # # _parse_hours_minutes_seconds("14:12:42") #=> 51162 # _parse_hours_minutes_seconds("foo") #=> nil # def _parse_hours_minutes_seconds( str ) m = %r/^\s*(\d{2,}):(\d{2}):(\d{2}(?:\.\d+)?)\s*$/.match(str) return if m.nil? (3600 * m[1].to_i) + (60 * m[2].to_i) + (m[3].to_f) end # Convert the string into a numeric value. If the string does not # represent a valid Integer or Float then +nil+ is returned. # # _parse_numeric("10") #=> 10 # _parse_numeric("1.0") #=> 1.0 # _parse_numeric("foo") #=> nil # def _parse_numeric( str ) Integer(str) rescue (Float(str) rescue nil) end # Using the flush_period, create a new PeriodicFlusher attached to this # appender. If the flush_period is nil, then no action will be taken. If a # PeriodicFlusher already exists, it will be stopped and a new one will be # created. # def _setup_periodic_flusher # stop and remove any existing periodic flusher instance if @periodic_flusher @periodic_flusher.stop @periodic_flusher = nil Thread.pass end # create a new periodic flusher if we have a valid flush period if @flush_period @auto_flushing = DEFAULT_BUFFER_SIZE unless @auto_flushing > 1 @periodic_flusher = PeriodicFlusher.new(self, @flush_period) @periodic_flusher.start end end # :stopdoc: # The PeriodicFlusher contains an internal run loop that will periodically # wake up and flush any log events contained in the message buffer of the # owning appender instance. The PeriodicFlusher relies on a _signal_ from # the appender in order to wakeup and perform the flush on the appender. # class PeriodicFlusher # Create a new PeriodicFlusher instance that will call the +flush+ # method on the given _appender_. The +flush+ method will be called # every _period_ seconds, but only when the message buffer is non-empty. # def initialize( appender, period ) @appender = appender @period = period @mutex = Mutex.new @cv = ConditionVariable.new @thread = nil @waiting = nil @signaled = false end # Start the periodic flusher's internal run loop. # def start return if @thread @thread = Thread.new { loop { begin break if Thread.current[:stop] _wait_for_signal sleep @period unless Thread.current[:stop] @appender.flush rescue => err ::Logging.log_internal {"PeriodicFlusher for appender #{@appender.inspect} encountered an error"} ::Logging.log_internal_error(err) end }; @thread = nil } self end # Stop the periodic flusher's internal run loop. # def stop return if @thread.nil? @thread[:stop] = true signal if waiting? self end # Signal the periodic flusher. This will wake up the run loop if it is # currently waiting for something to do. If the signal method is never # called, the periodic flusher will never perform the flush action on # the appender. # def signal return if Thread.current == @thread # don't signal ourselves return if @signaled # don't need to signal again @mutex.synchronize { @signaled = true @cv.signal } self end # Returns +true+ if the flusher is waiting for a signal. Returns +false+ # if the flusher is somewhere in the processing loop. # def waiting? @waiting end private def _wait_for_signal @mutex.synchronize { begin # wait on the condition variable only if we have NOT been signaled unless @signaled @waiting = true @cv.wait(@mutex) @waiting = false end ensure @signaled = false end } ensure @waiting = false end end # class PeriodicFlusher # :startdoc: end # Buffering end # Logging::Appenders logging-2.0.0/lib/logging/appenders/rolling_file.rb0000644000175000017500000003305112551223063021657 0ustar globusglobusmodule Logging::Appenders # Accessor / Factory for the RollingFile appender. def self.rolling_file( *args ) return ::Logging::Appenders::RollingFile if args.empty? ::Logging::Appenders::RollingFile.new(*args) end # An appender that writes to a file and ensures that the file size or age # never exceeds some user specified level. # # The goal of this class is to write log messages to a file. When the file # age or size exceeds a given limit then the log file is copied and then # truncated. The name of the copy indicates it is an older log file. # # The name of the log file is changed by inserting the age of the log file # (as a single number) between the log file name and the extension. If the # file has no extension then the number is appended to the filename. Here # is a simple example: # # /var/log/ruby.log => /var/log/ruby.1.log # # New log messages will continue to be appended to the same log file # (`/var/log/ruby.log` in our example above). The age number for all older # log files is incremented when the log file is rolled. The number of older # log files to keep can be given, otherwise all the log files are kept. # # The actual process of rolling all the log file names can be expensive if # there are many, many older log files to process. # # If you do not wish to use numbered files when rolling, you can specify the # :roll_by option as 'date'. This will use a date/time stamp to # differentiate the older files from one another. If you configure your # rolling file appender to roll daily and ignore the file size: # # /var/log/ruby.log => /var/log/ruby.20091225.log # # Where the date is expressed as `%Y%m%d` in the Time#strftime format. # # NOTE: this class is not safe to use when log messages are written to files # on NFS mounts or other remote file system. It should only be used for log # files on the local file system. The exception to this is when a single # process is writing to the log file; remote file systems are safe to # use in this case but still not recommended. class RollingFile < ::Logging::Appenders::IO # call-seq: # RollingFile.new( name, opts ) # # Creates a new Rolling File Appender. The _name_ is the unique Appender # name used to retrieve this appender from the Appender hash. The only # required option is the filename to use for creating log files. # # [:filename] The base filename to use when constructing new log # filenames. # # The "rolling" portion of the filename can be configured via some simple # pattern templates. For numbered rolling, you can use {{.%d}} # # "logname{{.%d}}.log" => ["logname.log", "logname.1.log", "logname.2.log" ...] # "logname.log{{-%d}}" => ["logname.log", "logname.log-1", "logname.log-2" ...] # # And for date rolling you can use `strftime` patterns: # # "logname{{.%Y%m%d}}.log" => ["logname.log, "logname.20130626.log" ...] # "logname{{.%Y-%m-%dT%H:%M:%S}}.log" => ["logname.log, "logname.2013-06-26T22:03:31.log" ...] # # If the defaults suit you fine, just pass in the :roll_by option and use # your normal log filename without any pattern template. # # The following options are optional: # # [:layout] The Layout that will be used by this appender. The Basic # layout will be used if none is given. # [:truncate] When set to true any existing log files will be rolled # immediately and a new, empty log file will be created. # [:size] The maximum allowed size (in bytes) of a log file before # it is rolled. # [:age] The maximum age (in seconds) of a log file before it is # rolled. The age can also be given as 'daily', 'weekly', # or 'monthly'. # [:keep] The number of rolled log files to keep. # [:roll_by] How to name the rolled log files. This can be 'number' or # 'date'. # def initialize( name, opts = {} ) @roller = Roller.new name, opts # grab our options @size = opts.fetch(:size, nil) @size = Integer(@size) unless @size.nil? @age_fn = filename + '.age' @age_fn_mtime = nil @age = opts.fetch(:age, nil) # create our `sufficiently_aged?` method build_singleton_methods FileUtils.touch(@age_fn) if @age && !test(?f, @age_fn) # we are opening the file in read/write mode so that a shared lock can # be used on the file descriptor => http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html @mode = encoding ? "a+:#{encoding}" : 'a+' super(name, ::File.new(filename, @mode), opts) # if the truncate flag was set to true, then roll roll_now = opts.fetch(:truncate, false) if roll_now copy_truncate @roller.roll_files end end # Returns the path to the logfile. def filename @roller.filename end # Reopen the connection to the underlying logging destination. If the # connection is currently closed then it will be opened. If the connection # is currently open then it will be closed and immediately opened. def reopen @mutex.synchronize { if defined?(@io) && @io flush @io.close rescue nil end @io = ::File.new(filename, @mode) } super self end private # Returns the file name to use as the temporary copy location. We are # using copy-and-truncate semantics for rolling files so that the IO # file descriptor remains valid during rolling. def copy_file @roller.copy_file end # Write the given _event_ to the log file. The log file will be rolled # if the maximum file size is exceeded or if the file is older than the # maximum age. def canonical_write( str ) return self if @io.nil? str = str.force_encoding(encoding) if encoding && str.encoding != encoding @io.flock_sh { @io.syswrite str } if roll_required? @io.flock? { @age_fn_mtime = nil copy_truncate if roll_required? } @roller.roll_files end self rescue StandardError => err self.level = :off ::Logging.log_internal {"appender #{name.inspect} has been disabled"} ::Logging.log_internal_error(err) end # Returns +true+ if the log file needs to be rolled. def roll_required? return false if ::File.exist?(copy_file) && (Time.now - ::File.mtime(copy_file)) < 180 # check if max size has been exceeded s = @size ? ::File.size(filename) > @size : false # check if max age has been exceeded a = sufficiently_aged? return (s || a) end # Copy the contents of the logfile to another file. Truncate the logfile # to zero length. This method will set the roll flag so that all the # current logfiles will be rolled along with the copied file. def copy_truncate return unless ::File.exist?(filename) FileUtils.concat filename, copy_file @io.truncate 0 # touch the age file if needed if @age FileUtils.touch @age_fn @age_fn_mtime = nil end @roller.roll = true end # Returns the modification time of the age file. def age_fn_mtime @age_fn_mtime ||= ::File.mtime(@age_fn) end # We use meta-programming here to define the `sufficiently_aged?` method for # the rolling appender. The `sufficiently_aged?` method is responsible for # determining if the current log file is older than the rolling criteria - # daily, weekly, etc. # # Returns this rolling file appender instance def build_singleton_methods method = case @age when 'daily' -> { now = Time.now (now.day != age_fn_mtime.day) || (now - age_fn_mtime) > 86400 } when 'weekly' -> { (Time.now - age_fn_mtime) > 604800 } when 'monthly' -> { now = Time.now (now.month != age_fn_mtime.month) || (now - age_fn_mtime) > 2678400 } when Integer, String @age = Integer(@age) -> { (Time.now - age_fn_mtime) > @age } else -> { false } end self.define_singleton_method(:sufficiently_aged?, method) end # Not intended for general consumption, but the Roller class is used # internally by the RollingFile appender to roll dem log files according # to the user's desires. class Roller # The magic regex for finding user-defined roller patterns. RGXP = %r/{{(([^%]+)?.*?)}}/ # Create a new roller. See the RollingFile#initialize documentation for # the list of options. # # name - The appender name as a String # opts - The options Hash # def initialize( name, opts ) # raise an error if a filename was not given @fn = opts.fetch(:filename, name) raise ArgumentError, 'no filename was given' if @fn.nil? if (m = RGXP.match @fn) @roll_by = ("#{m[2]}%d" == m[1]) ? :number : :date else age = opts.fetch(:age, nil) size = opts.fetch(:size, nil) @roll_by = case opts.fetch(:roll_by, nil) when 'number'; :number when 'date'; :date else (age && !size) ? :date : :number end ext = ::File.extname(@fn) bn = ::File.join(::File.dirname(@fn), ::File.basename(@fn, ext)) @fn = if :date == @roll_by && %w[daily weekly monthly].include?(age) "#{bn}{{.%Y%m%d}}#{ext}" elsif :date == @roll_by "#{bn}{{.%Y%m%d-%H%M%S}}#{ext}" else "#{bn}{{.%d}}#{ext}" end end @fn = ::File.expand_path(@fn) ::Logging::Appenders::File.assert_valid_logfile(filename) @roll = false @keep = opts.fetch(:keep, nil) @keep = Integer(keep) unless keep.nil? end attr_reader :keep, :roll_by attr_accessor :roll # Returns the regular log file name without any roller text. def filename return @filename if defined? @filename @filename = (@fn =~ RGXP ? @fn.sub(RGXP, '') : @fn.dup) @filename.freeze end # Returns the file name to use as the temporary copy location. We are # using copy-and-truncate semantics for rolling files so that the IO # file descriptor remains valid during rolling. def copy_file return @copy_file if defined? @copy_file @copy_file = filename + '._copy_' @copy_file.freeze end # Returns the glob pattern used to find rolled log files. We use this # list for pruning older log files and doing the numbered rolling. def glob return @glob if defined? @glob m = RGXP.match @fn @glob = @fn.sub(RGXP, (m[2] ? "#{m[2]}*" : '*')) @glob.freeze end # Returns the format String used to generate rolled file names. # Depending upon the `roll_by` type (:date or :number), this String will # be processed by `sprintf` or `Time#strftime`. def format return @format if defined? @format m = RGXP.match @fn @format = @fn.sub(RGXP, m[1]) @format.freeze end # Roll the log files. This method will collect the list of rolled files # and then pass that list to either `roll_by_number` or `roll_by_date` # to perform the actual rolling. # # Returns nil def roll_files return unless roll && ::File.exist?(copy_file) files = Dir.glob(glob) files.delete copy_file self.send "roll_by_#{roll_by}", files nil ensure self.roll = false end # Roll the list of log files optionally removing older files. The "older # files" are determined by extracting the number from the log file name # and order by the number. # # files - The Array of filename Strings # # Returns nil def roll_by_number( files ) @number_rgxp ||= Regexp.new(@fn.sub(RGXP, '\2(\d+)')) # sort the files in reverse order based on their count number files = files.sort do |a,b| a = Integer(@number_rgxp.match(a)[1]) b = Integer(@number_rgxp.match(b)[1]) b <=> a end # for each file, roll its count number one higher files.each do |fn| cnt = Integer(@number_rgxp.match(fn)[1]) if keep && cnt >= keep ::File.delete fn next end ::File.rename fn, sprintf(format, cnt+1) end # finally rename the copied log file ::File.rename(copy_file, sprintf(format, 1)) end # Roll the list of log files optionally removing older files. The "older # files" are determined by the mtime of the log files. So touching log # files or otherwise messing with them will screw this up. # # files - The Array of filename Strings # # Returns nil def roll_by_date( files ) length = files.length if keep && length >= keep files = files.sort do |a,b| a = ::File.mtime(a) b = ::File.mtime(b) b <=> a end files.last(length-keep+1).each { |fn| ::File.delete fn } end # rename the copied log file ::File.rename(copy_file, Time.now.strftime(format)) end end end end logging-2.0.0/lib/logging/appenders/file.rb0000644000175000017500000000517612551223063020140 0ustar globusglobus module Logging::Appenders # Accessor / Factory for the File appender. # def self.file( *args ) return ::Logging::Appenders::File if args.empty? ::Logging::Appenders::File.new(*args) end # This class provides an Appender that can write to a File. # class File < ::Logging::Appenders::IO # call-seq: # File.assert_valid_logfile( filename ) => true # # Asserts that the given _filename_ can be used as a log file by ensuring # that if the file exists it is a regular file and it is writable. If # the file does not exist, then the directory is checked to see if it is # writable. # # An +ArgumentError+ is raised if any of these assertions fail. # def self.assert_valid_logfile( fn ) if ::File.exist?(fn) if not ::File.file?(fn) raise ArgumentError, "#{fn} is not a regular file" elsif not ::File.writable?(fn) raise ArgumentError, "#{fn} is not writeable" end elsif not ::File.writable?(::File.dirname(fn)) raise ArgumentError, "#{::File.dirname(fn)} is not writable" end true end # call-seq: # File.new( name, :filename => 'file' ) # File.new( name, :filename => 'file', :truncate => true ) # File.new( name, :filename => 'file', :layout => layout ) # # Creates a new File Appender that will use the given filename as the # logging destination. If the file does not already exist it will be # created. If the :truncate option is set to +true+ then the file will # be truncated before writing begins; otherwise, log messages will be # appended to the file. # def initialize( name, opts = {} ) @fn = opts.fetch(:filename, name) raise ArgumentError, 'no filename was given' if @fn.nil? @fn = ::File.expand_path(@fn) self.class.assert_valid_logfile(@fn) @mode = opts.fetch(:truncate, false) ? 'w' : 'a' self.encoding = opts.fetch(:encoding, self.encoding) @mode = "#{@mode}:#{self.encoding}" if self.encoding super(name, ::File.new(@fn, @mode), opts) end # Returns the path to the logfile. # def filename() @fn.dup end # Reopen the connection to the underlying logging destination. If the # connection is currently closed then it will be opened. If the connection # is currently open then it will be closed and immediately opened. # def reopen @mutex.synchronize { if defined? @io and @io flush @io.close rescue nil end @io = ::File.new(@fn, @mode) } super self end end # FileAppender end # Logging::Appenders logging-2.0.0/lib/logging/appenders/console.rb0000644000175000017500000000450512551223063020656 0ustar globusglobus module Logging::Appenders # Accessor / Factory for the Stdout appender. # def self.stdout( *args ) if args.empty? return self['stdout'] || ::Logging::Appenders::Stdout.new end ::Logging::Appenders::Stdout.new(*args) end # This class provides an Appender that can write to STDOUT. # class Stdout < ::Logging::Appenders::IO # call-seq: # Stdout.new( name = 'stdout' ) # Stdout.new( :layout => layout ) # Stdout.new( name = 'stdout', :level => 'info' ) # # Creates a new Stdout Appender. The name 'stdout' will be used unless # another is given. Optionally, a layout can be given for the appender # to use (otherwise a basic appender will be created) and a log level # can be specified. # # Options: # # :layout => the layout to use when formatting log events # :level => the level at which to log # def initialize( *args ) opts = Hash === args.last ? args.pop : {} name = args.empty? ? 'stdout' : args.shift opts[:encoding] = STDOUT.external_encoding if STDOUT.respond_to? :external_encoding super(name, STDOUT, opts) end end # Stdout # Accessor / Factory for the Stderr appender. # def self.stderr( *args ) if args.empty? return self['stderr'] || ::Logging::Appenders::Stderr.new end ::Logging::Appenders::Stderr.new(*args) end # This class provides an Appender that can write to STDERR. # class Stderr < ::Logging::Appenders::IO # call-seq: # Stderr.new( name = 'stderr' ) # Stderr.new( :layout => layout ) # Stderr.new( name = 'stderr', :level => 'warn' ) # # Creates a new Stderr Appender. The name 'stderr' will be used unless # another is given. Optionally, a layout can be given for the appender # to use (otherwise a basic appender will be created) and a log level # can be specified. # # Options: # # :layout => the layout to use when formatting log events # :level => the level at which to log # def initialize( *args ) opts = Hash === args.last ? args.pop : {} name = args.empty? ? 'stderr' : args.shift opts[:encoding] = STDERR.external_encoding if STDERR.respond_to? :external_encoding super(name, STDERR, opts) end end # Stderr end # Logging::Appenders logging-2.0.0/lib/logging/appenders/syslog.rb0000644000175000017500000001517212551223063020536 0ustar globusglobus # only load this class if we have the syslog library # Windows does not have syslog # if HAVE_SYSLOG module Logging::Appenders # Accessor / Factory for the Syslog appender. # def self.syslog( *args ) return ::Logging::Appenders::Syslog if args.empty? ::Logging::Appenders::Syslog.new(*args) end # This class provides an Appender that can write to the UNIX syslog # daemon. # class Syslog < ::Logging::Appender include ::Syslog::Constants # call-seq: # Syslog.new( name, opts = {} ) # # Create an appender that will log messages to the system message # logger. The message is then written to the system console, log files, # logged-in users, or forwarded to other machines as appropriate. The # options that can be used to configure the appender are as follows: # # :ident => identifier string (name is used by default) # :logopt => options used when opening the connection # :facility => the syslog facility to use # # The parameter :ident is a string that will be prepended to every # message. The :logopt argument is a bit field specifying logging # options, which is formed by OR'ing one or more of the following # values: # # LOG_CONS If syslog() cannot pass the message to syslogd(8) it # wil attempt to write the message to the console # ('/dev/console'). # # LOG_NDELAY Open the connection to syslogd(8) immediately. Normally # the open is delayed until the first message is logged. # Useful for programs that need to manage the order in # which file descriptors are allocated. # # LOG_PERROR Write the message to standard error output as well to # the system log. Not available on Solaris. # # LOG_PID Log the process id with each message: useful for # identifying instantiations of daemons. # # The :facility parameter encodes a default facility to be assigned to # all messages that do not have an explicit facility encoded: # # LOG_AUTH The authorization system: login(1), su(1), getty(8), # etc. # # LOG_AUTHPRIV The same as LOG_AUTH, but logged to a file readable # only by selected individuals. # # LOG_CONSOLE Messages written to /dev/console by the kernel console # output driver. # # LOG_CRON The cron daemon: cron(8). # # LOG_DAEMON System daemons, such as routed(8), that are not # provided for explicitly by other facilities. # # LOG_FTP The file transfer protocol daemons: ftpd(8), tftpd(8). # # LOG_KERN Messages generated by the kernel. These cannot be # generated by any user processes. # # LOG_LPR The line printer spooling system: lpr(1), lpc(8), # lpd(8), etc. # # LOG_MAIL The mail system. # # LOG_NEWS The network news system. # # LOG_SECURITY Security subsystems, such as ipfw(4). # # LOG_SYSLOG Messages generated internally by syslogd(8). # # LOG_USER Messages generated by random user processes. This is # the default facility identifier if none is specified. # # LOG_UUCP The uucp system. # # LOG_LOCAL0 Reserved for local use. Similarly for LOG_LOCAL1 # through LOG_LOCAL7. # def initialize( name, opts = {} ) @ident = opts.fetch(:ident, name) @logopt = Integer(opts.fetch(:logopt, (LOG_PID | LOG_CONS))) @facility = Integer(opts.fetch(:facility, LOG_USER)) @syslog = ::Syslog.open(@ident, @logopt, @facility) # provides a mapping from the default Logging levels # to the syslog levels @map = [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT] map = opts.fetch(:map, nil) self.map = map unless map.nil? super end # call-seq: # map = { logging_levels => syslog_levels } # # Configure the mapping from the Logging levels to the syslog levels. # This is needed in order to log events at the proper syslog level. # # Without any configuration, the following mapping will be used: # # :debug => LOG_DEBUG # :info => LOG_INFO # :warn => LOG_WARNING # :error => LOG_ERR # :fatal => LOG_CRIT # def map=( levels ) map = [] levels.keys.each do |lvl| num = ::Logging.level_num(lvl) map[num] = syslog_level_num(levels[lvl]) end @map = map end # call-seq: # close # # Closes the connection to the syslog facility. # def close( footer = true ) super @syslog.close if @syslog.opened? self end # call-seq: # closed? => true or false # # Queries the connection to the syslog facility and returns +true+ if # the connection is closed. # def closed? !@syslog.opened? end # Reopen the connection to the underlying logging destination. If the # connection is currently closed then it will be opened. If the connection # is currently open then it will be closed and immediately opened. # def reopen @mutex.synchronize { if @syslog.opened? flush @syslog.close end @syslog = ::Syslog.open(@ident, @logopt, @facility) } super self end private # call-seq: # write( event ) # # Write the given _event_ to the syslog facility. The log event will be # processed through the Layout associated with this appender. The message # will be logged at the level specified by the event. # def write( event ) pri = LOG_DEBUG message = if event.instance_of?(::Logging::LogEvent) pri = @map[event.level] @layout.format(event) else event.to_s end return if message.empty? @syslog.log(pri, '%s', message) self end # call-seq: # syslog_level_num( level ) => integer # # Takes the given _level_ as a string, symbol, or integer and returns # the corresponding syslog level number. # def syslog_level_num( level ) case level when Integer; level when String, Symbol level = level.to_s.upcase self.class.const_get level else raise ArgumentError, "unkonwn level '#{level}'" end end end # Syslog end # Logging::Appenders end # HAVE_SYSLOG logging-2.0.0/lib/logging/diagnostic_context.rb0000644000175000017500000003511412551223063021123 0ustar globusglobus module Logging # A Mapped Diagnostic Context, or MDC in short, is an instrument used to # distinguish interleaved log output from different sources. Log output is # typically interleaved when a server handles multiple clients # near-simultaneously. # # Interleaved log output can still be meaningful if each log entry from # different contexts had a distinctive stamp. This is where MDCs come into # play. # # The MDC provides a hash of contextual messages that are identified by # unique keys. These unique keys are set by the application and appended # to log messages to identify groups of log events. One use of the Mapped # Diagnostic Context is to store HTTP request headers associated with a Rack # request. These headers can be included with all log messages emitted while # generating the HTTP response. # # When configured to do so, PatternLayout instances will automatically # retrieve the mapped diagnostic context for the current thread with out any # user intervention. This context information can be used to track user # sessions in a Rails application, for example. # # Note that MDCs are managed on a per thread basis. MDC operations such as # `[]`, `[]=`, and `clear` affect the MDC of the current thread only. MDCs # of other threads remain unaffected. # # By default, when a new thread is created it will inherit the context of # its parent thread. However, the `inherit` method may be used to inherit # context for any other thread in the application. # module MappedDiagnosticContext extend self # The name used to retrieve the MDC from thread-local storage. NAME = :logging_mapped_diagnostic_context # The name used to retrieve the MDC stack from thread-local storage. STACK_NAME = :logging_mapped_diagnostic_context_stack # Public: Put a context value as identified with the key parameter into # the current thread's context map. # # key - The String identifier for the context. # value - The String value to store. # # Returns the value. # def []=( key, value ) clear_context peek.store(key.to_s, value) end # Public: Get the context value identified with the key parameter. # # key - The String identifier for the context. # # Returns the value associated with the key or nil if there is no value # present. # def []( key ) context.fetch(key.to_s, nil) end # Public: Remove the context value identified with the key parameter. # # key - The String identifier for the context. # # Returns the value associated with the key or nil if there is no value # present. # def delete( key ) clear_context peek.delete(key.to_s) end # Public: Add all the key/value pairs from the given hash to the current # mapped diagnostic context. The keys will be converted to strings. # Existing keys of the same name will be overwritten. # # hash - The Hash of values to add to the current context. # # Returns this context. # def update( hash ) clear_context sanitize(hash, peek) self end # Public: Push a new Hash of key/value pairs onto the stack of contexts. # # hash - The Hash of values to push onto the context stack. # # Returns this context. # Raises an ArgumentError if hash is not a Hash. # def push( hash ) clear_context stack << sanitize(hash) self end # Public: Remove the most recently pushed Hash from the stack of contexts. # If no contexts have been pushed then no action will be taken. The # default context cannot be popped off the stack; please use the `clear` # method if you want to remove all key/value pairs from the context. # # Returns nil or the Hash removed from the stack. # def pop return unless Thread.current[STACK_NAME] return unless stack.length > 1 clear_context stack.pop end # Public: Clear all mapped diagnostic information if any. This method is # useful in cases where the same thread can be potentially used over and # over in different unrelated contexts. # # Returns the MappedDiagnosticContext. # def clear clear_context Thread.current[STACK_NAME] = nil self end # Public: Inherit the diagnostic context of another thread. In the vast # majority of cases the other thread will the parent that spawned the # current thread. The diagnostic context from the parent thread is cloned # before being inherited; the two diagnostic contexts can be changed # independently. # # Returns the MappedDiagnosticContext. # def inherit( obj ) case obj when Hash Thread.current[STACK_NAME] = [obj.dup] when Thread return if Thread.current == obj Thread.exclusive { if obj[STACK_NAME] hash = flatten(obj[STACK_NAME]) Thread.current[STACK_NAME] = [hash] end } end self end # Returns the Hash acting as the storage for this MappedDiagnosticContext. # A new storage Hash is created for each Thread running in the # application. # def context c = Thread.current[NAME] return c unless c.nil? return Thread.current[NAME] = {} unless Thread.current[STACK_NAME] Thread.current[NAME] = flatten(stack) end # Returns the stack of Hash objects that are storing the diagnostic # context information. This stack is guarnteed to always contain at least # one Hash. # def stack Thread.current[STACK_NAME] ||= [{}] end # Returns the most current Hash from the stack of contexts. # def peek stack.last end # Remove the flattened context. # def clear_context Thread.current[NAME] = nil self end # Given a Hash convert all keys into Strings. The values are not altered # in any way. The converted keys and their values are stored in the target # Hash if provided. Otherwise a new Hash is created and returned. # # hash - The Hash of values to push onto the context stack. # target - The target Hash to store the key value pairs. # # Returns a new Hash with all keys converted to Strings. # Raises an ArgumentError if hash is not a Hash. # def sanitize( hash, target = {} ) unless Hash === hash raise ArgumentError, "Expecting a Hash but received a #{hash.class.name}" end hash.each { |k,v| target[k.to_s] = v } return target end # Given an Array of Hash objects, flatten all the key/value pairs from the # Hash objects in the ary into a single Hash. The flattening occurs left # to right. So that the key/value in the very last Hash overrides any # other key from the previous Hash objcts. # # ary - An Array of Hash objects. # # Returns a Hash. # def flatten( ary ) return ary.first.dup if ary.length == 1 hash = {} ary.each { |h| hash.update h } return hash end end # MappedDiagnosticContext # A Nested Diagnostic Context, or NDC in short, is an instrument to # distinguish interleaved log output from different sources. Log output is # typically interleaved when a server handles multiple clients # near-simultaneously. # # Interleaved log output can still be meaningful if each log entry from # different contexts had a distinctive stamp. This is where NDCs come into # play. # # The NDC is a stack of contextual messages that are pushed and popped by # the client as different contexts are encountered in the application. When a # new context is entered, the client will `push` a new message onto the NDC # stack. This message appears in all log messages. When this context is # exited, the client will call `pop` to remove the message. # # * Contexts can be nested # * When entering a context, call `Logging.ndc.push` # * When leaving a context, call `Logging.ndc.pop` # * Configure the PatternLayout to log context information # # There is no penalty for forgetting to match each push operation with a # corresponding pop, except the obvious mismatch between the real # application context and the context set in the NDC. # # When configured to do so, PatternLayout instance will automatically # retrieve the nested diagnostic context for the current thread with out any # user intervention. This context information can be used to track user # sessions in a Rails application, for example. # # Note that NDCs are managed on a per thread basis. NDC operations such as # `push`, `pop`, and `clear` affect the NDC of the current thread only. NDCs # of other threads remain unaffected. # # By default, when a new thread is created it will inherit the context of # its parent thread. However, the `inherit` method may be used to inherit # context for any other thread in the application. # module NestedDiagnosticContext extend self # The name used to retrieve the NDC from thread-local storage. NAME = :logging_nested_diagnostic_context # Public: Push new diagnostic context information for the current thread. # The contents of the message parameter is determined solely by the # client. # # message - The message String to add to the current context. # # Returns the current NestedDiagnosticContext. # def push( message ) context.push(message) if block_given? begin yield ensure context.pop end end self end alias_method :<<, :push # Public: Clients should call this method before leaving a diagnostic # context. The returned value is the last pushed message. If no # context is available then `nil` is returned. # # Returns the last pushed diagnostic message String or nil if no messages # exist. # def pop context.pop end # Public: Looks at the last diagnostic context at the top of this NDC # without removing it. The returned value is the last pushed message. If # no context is available then `nil` is returned. # # Returns the last pushed diagnostic message String or nil if no messages # exist. # def peek context.last end # Public: Clear all nested diagnostic information if any. This method is # useful in cases where the same thread can be potentially used over and # over in different unrelated contexts. # # Returns the NestedDiagnosticContext. # def clear Thread.current[NAME] = nil self end # Public: Inherit the diagnostic context of another thread. In the vast # majority of cases the other thread will the parent that spawned the # current thread. The diagnostic context from the parent thread is cloned # before being inherited; the two diagnostic contexts can be changed # independently. # # Returns the NestedDiagnosticContext. # def inherit( obj ) case obj when Array Thread.current[NAME] = obj.dup when Thread return if Thread.current == obj Thread.exclusive { Thread.current[NAME] = obj[NAME].dup if obj[NAME] } end self end # Returns the Array acting as the storage stack for this # NestedDiagnosticContext. A new storage Array is created for each Thread # running in the application. # def context Thread.current[NAME] ||= Array.new end end # NestedDiagnosticContext # Public: Accessor method for getting the current Thread's # MappedDiagnosticContext. # # Returns MappedDiagnosticContext # def self.mdc() MappedDiagnosticContext end # Public: Accessor method for getting the current Thread's # NestedDiagnosticContext. # # Returns NestedDiagnosticContext # def self.ndc() NestedDiagnosticContext end # Public: Convenience method that will clear both the Mapped Diagnostic # Context and the Nested Diagnostic Context of the current thread. If the # `all` flag passed to this method is true, then the diagnostic contexts for # _every_ thread in the application will be cleared. # # all - Boolean flag used to clear the context of every Thread (default is false) # # Returns the Logging module. # def self.clear_diagnostic_contexts( all = false ) if all Thread.exclusive { Thread.list.each { |thread| thread[MappedDiagnosticContext::NAME] = nil if thread[MappedDiagnosticContext::NAME] thread[NestedDiagnosticContext::NAME] = nil if thread[NestedDiagnosticContext::NAME] thread[MappedDiagnosticContext::STACK_NAME] = nil if thread[MappedDiagnosticContext::STACK_NAME] } } else MappedDiagnosticContext.clear NestedDiagnosticContext.clear end self end end # module Logging # :stopdoc: class Thread class << self %w[new start fork].each do |m| class_eval <<-__, __FILE__, __LINE__ alias_method :_orig_#{m}, :#{m} private :_orig_#{m} def #{m}( *a, &b ) create_with_logging_context(:_orig_#{m}, *a ,&b) end __ end private # In order for the diagnostic contexts to behave properly we need to # inherit state from the parent thread. The only way I have found to do # this in Ruby is to override `new` and capture the contexts from the # parent Thread at the time the child Thread is created. The code below does # just this. If there is a more idiomatic way of accomplishing this in Ruby, # please let me know! # # Also, great care is taken in this code to ensure that a reference to the # parent thread does not exist in the binding associated with the block # being executed in the child thread. The same is true for the parent # thread's mdc and ndc. If any of those references end up in the binding, # then they cannot be garbage collected until the child thread exits. # def create_with_logging_context( m, *a, &b ) mdc, ndc = nil if Thread.current[Logging::MappedDiagnosticContext::STACK_NAME] mdc = Logging::MappedDiagnosticContext.context.dup end if Thread.current[Logging::NestedDiagnosticContext::NAME] ndc = Logging::NestedDiagnosticContext.context.dup end # This calls the actual `Thread#new` method to create the Thread instance. # If your memory profiling tool says this method is leaking memory, then # you are leaking Thread instances somewhere. self.send(m, *a) { |*args| Logging::MappedDiagnosticContext.inherit(mdc) Logging::NestedDiagnosticContext.inherit(ndc) b.call(*args) } end end end # Thread # :startdoc: logging-2.0.0/lib/logging/repository.rb0000644000175000017500000001514412551223063017453 0ustar globusglobus require 'singleton' module Logging # The Repository is a hash that stores references to all Loggers # that have been created. It provides methods to determine parent/child # relationships between Loggers and to retrieve Loggers from the hash. # class Repository include Singleton PATH_DELIMITER = '::' # :nodoc: # nodoc: # # This is a singleton class -- use the +instance+ method to obtain the # +Repository+ instance. # def initialize @h = {:root => ::Logging::RootLogger.new} # configures the internal logger which is disabled by default logger = ::Logging::Logger.allocate logger._setup( to_key(::Logging), :parent => @h[:root], :additive => false, :level => ::Logging::LEVELS.length # turns this logger off ) @h[logger.name] = logger end # call-seq: # instance[name] # # Returns the +Logger+ named _name_. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # retrieve the logger. When _name_ is a +Class+ the class name will be # used to retrieve the logger. When _name_ is an object the name of the # object's class will be used to retrieve the logger. # # Example: # # repo = Repository.instance # obj = MyClass.new # # log1 = repo[obj] # log2 = repo[MyClass] # log3 = repo['MyClass'] # # log1.object_id == log2.object_id # => true # log2.object_id == log3.object_id # => true # def []( key ) @h[to_key(key)] end # call-seq: # instance[name] = logger # # Stores the _logger_ under the given _name_. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # store the logger. When _name_ is a +Class+ the class name will be # used to store the logger. When _name_ is an object the name of the # object's class will be used to store the logger. # def []=( key, val ) @h[to_key(key)] = val end # call-seq: # fetch( name ) # # Returns the +Logger+ named _name_. An +KeyError+ will be raised if # the logger does not exist. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # retrieve the logger. When _name_ is a +Class+ the class name will be # used to retrieve the logger. When _name_ is an object the name of the # object's class will be used to retrieve the logger. # def fetch( key ) @h.fetch(to_key(key)) end # call-seq: # has_logger?( name ) # # Returns +true+ if the given logger exists in the repository. Returns # +false+ if this is not the case. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # retrieve the logger. When _name_ is a +Class+ the class name will be # used to retrieve the logger. When _name_ is an object the name of the # object's class will be used to retrieve the logger. # def has_logger?( key ) @h.has_key?(to_key(key)) end # call-seq: # delete( name ) # # Deletes the named logger from the repository. All direct children of the # logger will have their parent reassigned. So the parent of the logger # being deleted becomes the new parent of the children. # # When _name_ is a +String+ or a +Symbol+ it will be used "as is" to # remove the logger. When _name_ is a +Class+ the class name will be # used to remove the logger. When _name_ is an object the name of the # object's class will be used to remove the logger. # # Raises a RuntimeError if you try to delete the root logger. # Raises an KeyError if the named logger is not found. def delete( key ) key = to_key(key) raise 'the :root logger cannot be deleted' if :root == key parent = @h.fetch(key).parent children(key).each {|c| c.__send__(:parent=, parent)} @h.delete(key) end # call-seq: # parent( key ) # # Returns the parent logger for the logger identified by _key_ where # _key_ follows the same identification rules described in # Repository#[]. A parent is returned regardless of the # existence of the logger referenced by _key_. # # A note about parents - # # If you have a class A::B::C, then the parent of C is B, and the parent # of B is A. Parents are determined by namespace. # def parent( key ) name = parent_name(to_key(key)) return if name.nil? @h[name] end # call-seq: # children( key ) # # Returns an array of the children loggers for the logger identified by # _key_ where _key_ follows the same identification rules described in # +Repository#[]+. Children are returned regardless of the # existence of the logger referenced by _key_. # def children( parent ) ary = [] parent = to_key(parent) @h.each_pair do |child,logger| next if :root == child ary << logger if parent == parent_name(child) end return ary.sort end # call-seq: # to_key( key ) # # Takes the given _key_ and converts it into a form that can be used to # retrieve a logger from the +Repository+ hash. # # When _key_ is a +String+ or a +Symbol+ it will be returned "as is". # When _key_ is a +Class+ the class name will be returned. When _key_ is # an object the name of the object's class will be returned. # def to_key( key ) case key when :root, 'root'; :root when String; key when Symbol; key.to_s when Module; key.logger_name when Object; key.class.logger_name end end # Returns the name of the parent for the logger identified by the given # _key_. If the _key_ is for the root logger, then +nil+ is returned. # def parent_name( key ) return if :root == key a = key.split PATH_DELIMITER p = :root while a.slice!(-1) and !a.empty? k = a.join PATH_DELIMITER if @h.has_key? k then p = k; break end end p end # :stopdoc: def self.reset if defined?(@singleton__instance__) @singleton__mutex__.synchronize { @singleton__instance__ = nil } else @__instance__ = nil class << self nonce = class << Singleton; self; end if defined?(nonce::FirstInstanceCall) define_method(:instance, nonce::FirstInstanceCall) else remove_method(:instance) Singleton.__init__(::Logging::Repository) end end end return nil end # :startdoc: end # class Repository end # module Logging logging-2.0.0/lib/logging/layout.rb0000644000175000017500000000700212551223063016543 0ustar globusglobus module Logging # The +Layout+ class provides methods for formatting log events into a # string representation. Layouts are used by Appenders to format log # events before writing them to the logging destination. # # All other Layouts inherit from this class which provides stub methods. # Each subclass should provide a +format+ method. A layout can be used by # more than one +Appender+ so all the methods need to be thread safe. # class Layout # call-seq: # Layout.new( :format_as => :string ) # # Creates a new layout that will format objects as strings using the # given :format_as style. This can be one of :string, # :inspect, or :yaml. These formatting commands map to # the following object methods: # # * :string => to_s # * :inspect => inspect # * :yaml => to_yaml # * :json => MultiJson.encode(obj) # # If the format is not specified then the global object format is used # (see Logging#format_as). If the global object format is not specified # then :string is used. # def initialize( opts = {} ) ::Logging.init unless ::Logging.initialized? default = ::Logging.const_defined?('OBJ_FORMAT') ? ::Logging::OBJ_FORMAT : nil f = opts.fetch(:format_as, default) f = f.intern if f.instance_of? String @obj_format = case f when :inspect, :yaml, :json; f else :string end b = opts.fetch(:backtrace, ::Logging.backtrace) @backtrace = case b when :on, 'on', true; true when :off, 'off', false; false else raise ArgumentError, "backtrace must be true or false" end end # call-seq: # format( event ) # # Returns a string representation of the given logging _event_. It is # up to subclasses to implement this method. # def format( event ) nil end # call-seq: # header # # Returns a header string to be used at the beginning of a logging # appender. # def header( ) '' end # call-seq: # footer # # Returns a footer string to be used at the end of a logging appender. # def footer( ) '' end # call-seq: # format_obj( obj ) # # Return a string representation of the given object. Depending upon # the configuration of the logger system the format will be an +inspect+ # based representation or a +yaml+ based representation. # def format_obj( obj ) case obj when String; obj when Exception str = "<#{obj.class.name}> #{obj.message}" if @backtrace && !obj.backtrace.nil? str << "\n\t" << obj.backtrace.join("\n\t") end str when nil; "<#{obj.class.name}> nil" else str = "<#{obj.class.name}> " str << case @obj_format when :inspect; obj.inspect when :yaml; try_yaml(obj) when :json; try_json(obj) else obj.to_s end str end end # Attempt to format the _obj_ using yaml, but fall back to inspect style # formatting if yaml fails. # # obj - The Object to format. # # Returns a String representation of the object. # def try_yaml( obj ) "\n#{obj.to_yaml}" rescue TypeError obj.inspect end # Attempt to format the given object as a JSON string, but fall back to # inspect formatting if JSON encoding fails. # # obj - The Object to format. # # Returns a String representation of the object. # def try_json( obj ) MultiJson.encode(obj) rescue StandardError obj.inspect end end # class Layout end # module Logging logging-2.0.0/lib/logging/root_logger.rb0000644000175000017500000000303112551223063017546 0ustar globusglobus module Logging # The root logger exists to ensure that all loggers have a parent and a # defined logging level. If a logger is additive, eventually its log # events will propagate up to the root logger. # class RootLogger < Logger # undefine the methods that the root logger does not need %w(additive additive= parent parent=).each do |m| undef_method m.intern end attr_reader :level # call-seq: # RootLogger.new # # Returns a new root logger instance. This method will be called only # once when the +Repository+ singleton instance is created. # def initialize( ) ::Logging.init unless ::Logging.initialized? @name = 'root' @appenders = [] @additive = false @caller_tracing = false @level = 0 ::Logging::Logger.define_log_methods(self) end # call-seq: # log <=> other # # Compares this logger by name to another logger. The normal return codes # for +String+ objects apply. # def <=>( other ) case other when self; 0 when ::Logging::Logger; -1 else raise ArgumentError, 'expecting a Logger instance' end end # call-seq: # level = :all # # Set the level for the root logger. The functionality of this method is # the same as +Logger#level=+, but setting the level to +nil+ for the # root logger is not allowed. The level is silently set to :all. # def level=( level ) super(level || 0) end end # class RootLogger end # module Logging logging-2.0.0/lib/logging/version.rb0000644000175000017500000000020412551223063016710 0ustar globusglobusmodule Logging VERSION = "2.0.0".freeze # Returns the version string for the library. def self.version VERSION end end logging-2.0.0/lib/logging/filters/0000755000175000017500000000000012551223063016352 5ustar globusglobuslogging-2.0.0/lib/logging/filters/level.rb0000644000175000017500000000142612551223063020011 0ustar globusglobusrequire 'set' module Logging module Filters # The `Level` filter class provides a simple level-based filtering mechanism # that filters messages to only include those from an enumerated list of # levels to log. class Level < ::Logging::Filter # Creates a new level filter that will only allow the given _levels_ to # propagate through to the logging destination. The _levels_ should be # given in symbolic form. # # Examples # Logging::Filters::Level.new(:debug, :info) # def initialize( *levels ) levels = levels.map { |level| ::Logging::level_num(level) } @levels = Set.new levels end def allow( event ) @levels.include?(event.level) ? event : nil end end end end logging-2.0.0/lib/logging/color_scheme.rb0000644000175000017500000002537012551223063017700 0ustar globusglobus# color_scheme.rb # # Created by Jeremy Hinegardner on 2007-01-24 # Copyright 2007. All rights reserved # # This file is licensed under the terms of the MIT License. # See the README for licensing details. # module Logging # ColorScheme objects encapsulate a named set of colors to be used in the # colors() method call. For example, by applying a ColorScheme that # has a :warning color then the following could be used: # # scheme.color("This is a warning", :warning) # # ColorScheme objects are used by the Pattern layout code to colorize log # messages. Each color scheme is given a unique name which is used by the # Pattern layout to lookup the appropriate color scheme to use. Please # refer to the Pattern layout documentation for more details - specifically # the initializer documentation. # # The color scheme can be applied to the Pattern layout in several ways. # Each token in the log pattern can be colorized with the log level (debug, # info, warn, etc) receiving unique colors based on the level itself. # Another option is to colorize the entire log message based on the log # level; in this mode tokens do not get their own colors. Please see the # ColorScheme initializer for the list of colorization options. # class ColorScheme class << self # Retrieve a color scheme by name. # def []( name ) @color_schemes[name.to_s] end # Store a color scheme by name. # def []=( name, value ) raise ArgumentError, "Silly! That's not a ColorSchmeme!" unless ColorScheme === value @color_schemes[name.to_s] = value end # Clear all color schemes and setup a default color scheme. # def reset @color_schemes ||= {} @color_schemes.clear new(:default, :levels => { :info => :green, :warn => :yellow, :error => :red, :fatal => [:white, :on_red] }) end end # Create a ColorScheme instance that can be accessed using the given # _name_. If a color scheme already exists with the given _name_ it will # be replaced by the new color scheme. # # The color names are passed as options to the method with each name # mapping to one or more color codes. For example: # # ColorScheme.new('example', :logger => [:white, :on_green], :message => :magenta) # # The color codes are the lowercase names of the constants defined at the # end of this file. Multiple color codes can be aliased by grouping them # in an array as shown in the example above. # # Since color schemes are primarily intended to be used with the Pattern # layout, there are a few special options of note. First the log levels # are enumerated in their own hash: # # :levels => { # :debug => :blue, # :info => :cyan, # :warn => :yellow, # :error => :red, # :fatal => [:white, :on_red] # } # # The log level token will be colorized differently based on the value of # the log level itself. Similarly the entire log message can be colorized # based on the value of the log level. A different option should be given # for this behavior: # # :lines => { # :debug => :blue, # :info => :cyan, # :warn => :yellow, # :error => :red, # :fatal => [:white, :on_red] # } # # The :levels and :lines options cannot be used together; only one or the # other should be given. # # The remaining tokens defined in the Pattern layout can be colorized # using the following aliases. Their meaning in the Pattern layout are # repeated here for sake of clarity. # # :logger [%c] name of the logger that generate the log event # :date [%d] datestamp # :message [%m] the user supplied log message # :pid [%p] PID of the current process # :time [%r] the time in milliseconds since the program started # :thread [%T] the name of the thread Thread.current[:name] # :thread_id [%t] object_id of the thread # :file [%F] filename where the logging request was issued # :line [%L] line number where the logging request was issued # :method [%M] method name where the logging request was issued # # Please refer to the "examples/colorization.rb" file for a working # example of log colorization. # def initialize( name, opts = {} ) @scheme = Hash.new @lines = opts.key? :lines @levels = opts.key? :levels raise ArgumentError, "Found both :lines and :levels - only one can be used." if lines? and levels? lines = opts.delete :lines levels = opts.delete :levels load_from_hash(opts) load_from_hash(lines) if lines? load_from_hash(levels) if levels? ::Logging::ColorScheme[name] = self end # Load multiple colors from key/value pairs. # def load_from_hash( h ) h.each_pair do |color_tag, constants| self[color_tag] = constants end end # Returns +true+ if the :lines option was passed to the constructor. # def lines? @lines end # Returns +true+ if the :levels option was passed to the constructor. # def levels? @levels end # Does this color scheme include the given tag name? # def include?( color_tag ) @scheme.key?(to_key(color_tag)) end # Allow the scheme to be accessed like a Hash. # def []( color_tag ) @scheme[to_key(color_tag)] end # Allow the scheme to be set like a Hash. # def []=( color_tag, constants ) @scheme[to_key(color_tag)] = constants.respond_to?(:map) ? constants.map { |c| to_constant(c) }.join : to_constant(constants) end # This method provides easy access to ANSI color sequences, without the user # needing to remember to CLEAR at the end of each sequence. Just pass the # _string_ to color, followed by a list of _colors_ you would like it to be # affected by. The _colors_ can be ColorScheme class constants, or symbols # (:blue for BLUE, for example). A CLEAR will automatically be embedded to # the end of the returned String. # def color( string, *colors ) colors.map! { |color| color_tag = to_key(color) @scheme.key?(color_tag) ? @scheme[color_tag] : to_constant(color) } colors.compact! return string if colors.empty? "#{colors.join}#{string}#{CLEAR}" end private # Return a normalized representation of a color name. # def to_key( t ) t.to_s.downcase end # Return a normalized representation of a color setting. # def to_constant( v ) v = v.to_s.upcase ColorScheme.const_get(v) if (ColorScheme.const_defined?(v, false) rescue ColorScheme.const_defined?(v)) end # Embed in a String to clear all previous ANSI sequences. This *MUST* be # done before the program exits! CLEAR = "\e[0m".freeze RESET = CLEAR # An alias for CLEAR. ERASE_LINE = "\e[K".freeze # Erase the current line of terminal output. ERASE_CHAR = "\e[P".freeze # Erase the character under the cursor. BOLD = "\e[1m".freeze # The start of an ANSI bold sequence. DARK = "\e[2m".freeze # The start of an ANSI dark sequence. (Terminal support uncommon.) UNDERLINE = "\e[4m".freeze # The start of an ANSI underline sequence. UNDERSCORE = UNDERLINE # An alias for UNDERLINE. BLINK = "\e[5m".freeze # The start of an ANSI blink sequence. (Terminal support uncommon.) REVERSE = "\e[7m".freeze # The start of an ANSI reverse sequence. CONCEALED = "\e[8m".freeze # The start of an ANSI concealed sequence. (Terminal support uncommon.) BLACK = "\e[30m".freeze # Set the terminal's foreground ANSI color to black. RED = "\e[31m".freeze # Set the terminal's foreground ANSI color to red. GREEN = "\e[32m".freeze # Set the terminal's foreground ANSI color to green. YELLOW = "\e[33m".freeze # Set the terminal's foreground ANSI color to yellow. BLUE = "\e[34m".freeze # Set the terminal's foreground ANSI color to blue. MAGENTA = "\e[35m".freeze # Set the terminal's foreground ANSI color to magenta. CYAN = "\e[36m".freeze # Set the terminal's foreground ANSI color to cyan. WHITE = "\e[37m".freeze # Set the terminal's foreground ANSI color to white. ON_BLACK = "\e[40m".freeze # Set the terminal's background ANSI color to black. ON_RED = "\e[41m".freeze # Set the terminal's background ANSI color to red. ON_GREEN = "\e[42m".freeze # Set the terminal's background ANSI color to green. ON_YELLOW = "\e[43m".freeze # Set the terminal's background ANSI color to yellow. ON_BLUE = "\e[44m".freeze # Set the terminal's background ANSI color to blue. ON_MAGENTA = "\e[45m".freeze # Set the terminal's background ANSI color to magenta. ON_CYAN = "\e[46m".freeze # Set the terminal's background ANSI color to cyan. ON_WHITE = "\e[47m".freeze # Set the terminal's background ANSI color to white. BRIGHT_RED = "\e[1;31m".freeze # Set the terminal's foreground ANSI color to bright red. BRIGHT_GREEN = "\e[1;32m".freeze # Set the terminal's foreground ANSI color to bright green. BRIGHT_YELLOW = "\e[1;33m".freeze # Set the terminal's foreground ANSI color to bright yellow. BRIGHT_BLUE = "\e[1;34m".freeze # Set the terminal's foreground ANSI color to bright blue. BRIGHT_MAGENTA = "\e[1;35m".freeze # Set the terminal's foreground ANSI color to bright magenta. BRIGHT_CYAN = "\e[1;36m".freeze # Set the terminal's foreground ANSI color to bright cyan. BRIGHT_WHITE = "\e[1;37m".freeze # Set the terminal's foreground ANSI color to bright white. ON_BRIGHT_RED = "\e[1;41m".freeze # Set the terminal's background ANSI color to bright red. ON_BRIGHT_GREEN = "\e[1;42m".freeze # Set the terminal's background ANSI color to bright green. ON_BRIGHT_YELLOW = "\e[1;43m".freeze # Set the terminal's background ANSI color to bright yellow. ON_BRIGHT_BLUE = "\e[1;44m".freeze # Set the terminal's background ANSI color to bright blue. ON_BRIGHT_MAGENTA = "\e[1;45m".freeze # Set the terminal's background ANSI color to bright magenta. ON_BRIGHT_CYAN = "\e[1;46m".freeze # Set the terminal's background ANSI color to bright cyan. ON_BRIGHT_WHITE = "\e[1;47m".freeze # Set the terminal's background ANSI color to bright white. end # ColorScheme # setup the default color scheme ColorScheme.reset end # Logging logging-2.0.0/History.txt0000644000175000017500000002276512551223063014724 0ustar globusglobus== 2.0.0 / 2015-03-28 Enhancements - added event filtering via the Filter framework - improvements to buffered logging - code readability improvements around meta-programming Bug Fixes - fixed an `already initialized error` - avoid unnecessary mutex-ing in logger lookup Deprecations - dropped Ruby 1.8 support - removed logger consolidation - removed YAML style configuration - removed the Logging::Stats module - removed the Hash#getopt method - removed the Growl appender - moved the Email appender to the logging-email plugin gem == 1.8.2 / 2014-01-29 Bug Fixes - Adding a `formatter` method for Rails 4 compatibility == 1.8.1 / 2013-01-02 Bug Fixes - Diagnostic context thread inheritance [issue #56] - Fixing trace reporting in JRuby 1.7 == 1.8.0 / 2012-09-13 Enhancements - Appenders handle string encodings [issue #46] - Support for diagnostic contexts [issues #23, #32, #42] - Enable JSON formatting of log message [issue #34] Bug Fixes - Fix clash with ActiveSupport autoloader (chewie) [issue #39] == 1.7.2 / 2012-04-03 Bug Fixes - Fixed segmentation fault on exit [issue #30] - Fixed syswrite warning when IO contains unflushed data in buffer [issue #31] - Added "mingw" to the list of Windows host versions == 1.7.1 / 2012-03-05 Bug Fixes - Fixed deprecated use of Config::* [issue #29] == 1.7.0 / 2012-02-18 Enhancements - Move appender factories [issue #28] - ActionMail compatible options in the email appender [issue #27] - Add TLS support to the email appender [issue #25] - Refactoring appender shutdown [issue #20] Bug Fixes - File locking fails on windows using JRuby [issue #22] == 1.6.2 / 2012-01-05 Bug Fixes - Fix typo in the Readme [issue #14] - Fix spelling in a variety of places [issue #15] - Solaris does not have Syslog#LOG_PERROR defined [issue #17] - Fix failing tests for Ruby 1.9.3 [issue #18] - Check for RUBY_ENGINE for Ruby 1.8.7 [issue #19] - Whitespace and '# EOF' cleanup - Support for Rubinious == 1.6.1 / 2011-09-09 Bug Fixes - Rails compatibility methods [issue #11] - Blocked rolling file appender [issue #12] == 1.6.0 / 2011-08-22 Enhancements - Adding periodic flushing of buffered messages [issue #10] - Accessor for a logger's appenders [issue #9] - Better support for capturing log messages in RSpec version 1 & 2 == 1.5.2 / 2011-07-07 Bug Fixes - Changing working directory breaks rolling file appenders [issue #8] == 1.5.1 / 2011-06-03 Bug Fixes - IO streams cannot be buffered when using syswrite - JRuby does not allow shared locks on write only file descriptors - Fixing tests for JRuby 1.6.X == 1.5.0 / 2011-03-22 Minor Enhancements - removed mutexes in favor of IO#syswrite - no round tripping through the buffer array when auto_flushing is true - added a Proxy object that will log all methods called on it - colorization of log messages == 1.4.3 / 2010-05-31 Bug Fixes - rolling file appender calling "super" within a block [Sebastian Georgi] == 1.4.2 / 2010-04-18 Bug Fixes - missing require for fileutils == 1.4.1 / 2010-03-23 Bug Fixes - flock(LOCK_UN) was causing errors on windows [issue 4] == 1.4.0 / 2010-03-16 Minor Enhancements - Compatibility with the Rack::CommonLogger == 1.3.0 / 2009-12-18 Minor Enhancements - Using copy/truncate semantics for a faster RollingFile appender - Global reopen method for using Logging in forked environments - RollingFile appender can use date stamps instead of numbers == 1.2.3 / 2009-12-01 1 bug fix - Using a reentrant mutex to avoid deadlock in some Rails applications == 1.2.2 / 2009-08-18 1 minor enhancement - Created a reset method for the logging framework == 1.2.1 / 2009-08-14 1 bug fix - Using a bug-fix version of little-plugger == 1.2.0 / 2009-08-14 2 minor enhancements - Added a gem based plugin system - LogEvent is now a struct (slightly faster) == 1.1.4 / 2009-05-28 1 minor enhancement - Added a Logging.shutdown method to close out all appenders == 1.1.3 / 2009-05-13 1 bug fix - Fixing an alias bug on the JRuby platform == 1.1.2 / 2009-05-05 1 minor enhancement - Added two new require methods to Kernel == 1.1.1 / 2009-04-30 1 minor enhancement - Added a 'filename' method to the file appenders == 1.1.0 / 2009-04-21 3 minor enhancements - Added a "global" logger method - Loggers can be consolidated on a per-namespace basis - Added a precision to the logger name specifier in the pattern layout [addresses http://github.com/TwP/logging/issues#issue/1] == 1.0.0 / 2009-04-17 2 major enhancements - Refactored access to the appenders - Created a much cleaner way to initialize the logging framework 3 minor enhancements - Added a YAML layout option - Added a JSON layout option - Cration of an "examples" directory 1 bug fix - Logging initialization happens implicitly when a logger, layout, or appender is created == 0.9.8 / 2009-04-11 2 minor enhancements - Adding a to_s method to the StringIo appender's StringIO object - Added a Spec::LoggingHelper class that will capture log messages when using rspec style testing == 0.9.7 / 2009-03-17 1 minor enhancement - Added a StringIO appender 1 bug fix - Handling objects that cannot be dumped via YAML [Tim Galeckas] == 0.9.6 / 2009-02-02 2 minor enhancements - Ruby 1.9.1 compatability - JRuby 1.1.5 compatability == 0.9.5 / 2009-01-25 2 minor enhancements - The pattern layout can output the current thread name if set using Thread.current[:name] [valodzka] - Added buffered logging to all IO based loggers (console, file, rolling file) 1 bug fix - Uncaught TimeoutError in the e-mail appender == 0.9.4 / 2008-10-04 2 minor enhancements - Flag to suppress exception backtraces from being logged - Cleaning up color codes on Growl output 4 bug fixes - Child loggers were not being found in some cases - RollingFileAppender fails to reopen the log file if the log file is deleted. - Fixed a copy/paste error in the YAML configurator - Bug in the configurator where a nil object was being used == 0.9.3 / 2008-09-12 2 minor enhancement - Added a class for tracking basic statistics - Will use the 'fastthread' gem if availble == 0.9.2 / 2008-09-03 2 bug fixes - Properly generates logger names for anonymous classes and modules and meta-classes - Fixed the rescue clause when 'turn' cannot be required == 0.9.1 / 2008-08-14 1 minor enhancement - added a method to show the logging configuration 2 bug fixes - checking for sync method on the IO streams before calling - fixed the internal logging levels == 0.9.0 / 2008-07-16 2 minor enhancement - Exceptions from appenders are captured and logged - Internal logger for the Logging framework (disabled by default) - Added a DSL configuration format (more readable than YAML) 1 bug fix - Modules could not have their own logger instance == 0.8.0 / 2008-07-02 1 minor enhancement - Setting the log level of a parent will cause this level to be propagated to the children 1 bug fix - Fixed error with the e-mail appender and missing hostname == 0.7.1 / 2008-02-25 1 minor enhancement - Removed dependency on the Lockfile gem (brought the ruby file into the logging/stelan directory) 1 bug fix - Fixed bug with age based rolling: was not multi-process safe == 0.7.0 / 2008-02-12 1 major enhancement - Rails compatibility * renamed Logger#add method to Logger#add_appenders * renamed Logger#remove method to Logger#remove_appenders * renamed Logger#clear method to Logger#clear_appenders * added a new Logger#add method that conforms to the calling semantics of the Ruby stdlib Logger 2 minor enhancements - Speed improvements and test coverage - Created a top-level Logging.init method that is used to define the default logging levels 1 bug fix - Tweaked windows detection code == 0.6.3 / 2008-02-08 2 minor enhancements - YAML configuration now supports multiple keys -- i.e. development or production or whatever - Reorganized a lot of files so that requiring files is cleaner and more deterministic == 0.6.2 / 2008-02-06 2 bug fixes - An extra e-mail was being pushed out when the e-mail appender was closed - Created an at_exit handler to close all appenders == 0.6.1 / 2008-01-01 1 bug fix - Fixed include order to avoid double loading when testing == 0.6.0 / 2007-12-26 * Using the new 'getopt' method for handling option hashes * Rolling file appender is safe for multiple processes * Added an e-mail appender from Jeremy Hinegardner * Updated tests for the appenders == 0.5.3 / 2007-12-08 * Fixed the quoting for messages sent to the growl appender == 0.5.2 / 2007-11-28 * Updated the library to work with Ruby 1.9 * Fixed coalescing with the growl appender == 0.5.1 / 2007-11-18 * Fixed a bug on Windows when attempting to load the syslog library == 0.5.0 / 2007-11-18 * Added the ability to log via the syslog daemon * Can send messages to the Growl notification system on Mac OS X * The Growl appender can coalesce messages of the same title/priority == 0.4.0 / 2007-03-21 * Added a microsecond flag to the Pattern layout * All appenders write immediately upon receipt of a logging event * Added a basic logging method that returns a logger object configured in the same manner as the standard Ruby logger * Fixed a bug caused by nil log messages == 0.3.1 / 2007-02-08 * Bugfix Release == 0.3.0 / 2007-02-01 * Remove the ability to log multiple objects from a single log method call == 0.2.0 / 2007-01-29 * The "once every four years" release * Storage and retrieval of appenders by name * YAML configuration support * Rolling file appender == 0.1.0 / 2007-01-12 * Birthday! logging-2.0.0/.gitignore0000644000175000017500000000045612551223063014503 0ustar globusglobus# git-ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ announcement.txt coverage/ doc/ pkg/ tmp/ vendor/ .rbx .rvmrc logging-2.0.0/Rakefile0000644000175000017500000000157212551223063014160 0ustar globusglobusbegin require 'bones' rescue LoadError abort '### please install the "bones" gem ###' end lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'logging/version' task :default => 'test:run' task 'gem:release' => 'test:run' Bones { name 'logging' summary 'A flexible and extendable logging library for Ruby' authors 'Tim Pease' email 'tim.pease@gmail.com' url 'http://rubygems.org/gems/logging' version Logging::VERSION rdoc.exclude << '^data' rdoc.include << '^examples/.*\.rb' #rcov.opts << '-x' << '~/.rvm/' use_gmail depend_on 'little-plugger', '~> 1.1' depend_on 'multi_json', '~> 1.10' depend_on 'flexmock', '~> 1.0', :development => true depend_on 'bones-git', '~> 1.3', :development => true #depend_on 'bones-rcov', :development => true } logging-2.0.0/script/0000755000175000017500000000000012551223063014012 5ustar globusglobuslogging-2.0.0/script/bootstrap0000755000175000017500000000033712551223063015760 0ustar globusglobus#!/usr/bin/env sh gem list -i bones >/dev/null 2>&1 rc=$? if [[ $rc != 0 ]]; then gem install bones fi gem list -i rdoc >/dev/null 2>&1 rc=$? if [[ $rc != 0 ]]; then gem install rdoc fi rake gem:install_dependencies logging-2.0.0/examples/0000755000175000017500000000000012551223063014324 5ustar globusglobuslogging-2.0.0/examples/appenders.rb0000644000175000017500000000247512551223063016642 0ustar globusglobus# :stopdoc: # # Appenders are used to output log events to some logging destination. The # same log event can be sent to multiple desitnations by associating # multiple appenders with the logger. # # The following is a list of all the available appenders and a brief # description of each. Please refer to the documentation for specific # configuration options available for each. # # File writes to a regular file # IO generic IO appender # RollingFile writes to a file and rolls based on size or age # Stdout appends to STDOUT # Stderr appends to STDERR # StringIo writes to a StringIO instance (useful for testing) # Syslog outputs to syslogd (not available on all systems) # # And you can access these appenders: # # Logging.appenders.file # Logging.appenders.io # Logging.appenders.rolling_file # Logging.appenders.stdout # Logging.appenders.stderr # Logging.appenders.string_io # Logging.appenders.syslog # require 'logging' log = Logging.logger['example'] log.add_appenders( Logging.appenders.stdout, Logging.appenders.file('development.log') ) log.level = :debug # These messages will be logged to both the log file and to STDOUT log.debug "a very nice little debug message" log.warn "this is your last warning" # :startdoc: logging-2.0.0/examples/formatting.rb0000644000175000017500000000277612551223063017037 0ustar globusglobus# :stopdoc: # # Any Ruby object can be passed to the log methods of a logger. How these # objects are formatted by the Logging framework is controlled by a global # "format_as" option and a global "backtrace" option. # # The format_as option allows objects to be converted to a string using the # standard "to_s" method, the "inspect" method, the "to_json" method, or the # "to_yaml" method (this is independent of the YAML layout). The format_as # option can be overridden by each layout as desired. # # Logging.format_as :string # or :inspect or :json or :yaml # # Exceptions are treated differently by the logging framework. The Exception # class is printed along with the message. Optionally, the exception backtrace # can be included in the logging output; this option is enabled by default. # # Logging.backtrace false # # The backtrace can be enabled or disabled for each layout as needed. # require 'logging' Logging.format_as :inspect Logging.backtrace false Logging.appenders.stdout( :layout => Logging.layouts.basic(:format_as => :yaml) ) Logging.appenders.stderr( :layout => Logging.layouts.basic(:backtrace => true) ) log = Logging.logger['foo'] log.appenders = %w[stdout stderr] # these log messages will all appear twice because of the two appenders - # STDOUT and STDERR - but the interesting thing is the difference in the # output log.info %w[An Array Of Strings] log.info({"one"=>1, "two"=>2}) begin 1 / 0 rescue => err log.error err end # :startdoc: logging-2.0.0/examples/classes.rb0000644000175000017500000000213512551223063016307 0ustar globusglobus# :stopdoc: # # The Logging framework is very good about figuring out predictable names # for loggers regardless of what object is used to create them. The name is # the class name or module name of whatever is passed to the logger bracket # method. The following lines all return the exact same logger instance: # # ary = Array.new # Logging.logger[ary] # Logging.logger[Array] # Logging.logger['Array'] # Logging.logger[:Array] # # So, if you want each class to have it's own logger this is very easy to # do. # require 'logging' Logging.logger.root.appenders = Logging.appenders.stdout Logging.logger.root.level = :info class Foo attr_reader :log def initialize() @log = Logging.logger[self]; end end class Foo::Bar attr_reader :log def initialize() @log = Logging.logger[self]; end end foo = Foo.new.log bar = Foo::Bar.new.log # you'll notice in these log messages that the logger names were taken # from the class names of the Foo and Foo::Bar instances foo.info 'this message came from Foo' bar.warn 'this is a warning from Foo::Bar' # :startdoc: logging-2.0.0/examples/loggers.rb0000644000175000017500000000163612551223063016321 0ustar globusglobus# :stopdoc: # # Multiple loggers can be created and each can be configured with it's own # log level and appenders. So one logger can be configured to output debug # messages, and all the others can be left at the info or warn level. This # makes it easier to debug specific portions of your code. # require 'logging' # all loggers inherit the log level of the "root" logger # but specific loggers can be given their own level Logging.logger.root.level = :warn # similarly, the root appender will be used by all loggers Logging.logger.root.appenders = Logging.appenders.file('output.log') log1 = Logging.logger['Log1'] log2 = Logging.logger['Log2'] log3 = Logging.logger['Log3'] # you can use strings or symbols to set the log level log3.level = 'debug' log1.info "this message will not get logged" log2.info "nor will this message" log3.info "but this message will get logged" # :startdoc: logging-2.0.0/examples/fork.rb0000644000175000017500000000252112551223063015612 0ustar globusglobus# :stopdoc: # # Because of the global interpreter lock, Kernel#fork is the best way # to achieve true concurrency in Ruby scripts. However, there are peculiarities # when using fork and passing file descriptors between process. These # peculiarities affect the logging framework. # # In short, always reopen file descriptors in the child process after fork has # been called. The RollingFile appender uses flock to safely coordinate the # rolling of the log file when multiple processes are writing to the same # file. If the file descriptor is opened in the parent and multiple children # are forked, then each child will use the same file descriptor lock; when one # child locks the file any other child will also have the lock. This creates a # race condition in the rolling code. The solution is to reopen the file to # obtain a new file descriptor in each of the children. # require 'logging' log = Logging.logger['example'] log.add_appenders( Logging.appenders.rolling_file('roller.log', :age => 'daily') ) log.level = :debug # Create four child processes and reopen the "roller.log" file descriptor in # each child. Now log rolling will work safely. 4.times do fork { Logging.reopen log.info "This is child process #{Process.pid}" } end log.info "This is the parent process #{Process.pid}" # :startdoc: logging-2.0.0/examples/simple.rb0000644000175000017500000000074012551223063016143 0ustar globusglobus# :stopdoc: # # Logging provides a simple, default logger configured in the same manner as # the default Ruby Logger class -- i.e. the output of the two will be the # same. All log messages at "warn" or higher are printed to STDOUT; any # message below the "warn" level are discarded. # require 'logging' log = Logging.logger(STDOUT) log.level = :warn log.debug "this debug message will not be output by the logger" log.warn "this is your last warning" # :startdoc: logging-2.0.0/examples/mdc.rb0000644000175000017500000000541512551223063015421 0ustar globusglobus# :stopdoc: # # A diagnostic context allows us to attach state information to every log # message. This is useful for applications that serve client requests - # information about the client can be included in the log messages for the # duration of the request processing. This allows you to identify related log # messages in concurrent system. # # The Mapped Diagnostic Context tracks state information in a collection of # key/value pairs. In this example we are creating a few threads that will log # quotes from famous people. Each thread has its own diagnostic context # containing the name of the famous person. # # Our PatternLayout is configured to attach the "first" and the "last" name of # our famous person to each log message. # require 'logging' # log the first and last names of the celebrity with each quote Logging.appenders.stdout( :layout => Logging.layouts.pattern(:pattern => '%X{first} %X{last}: %m\n') ) log = Logging.logger['User'] log.add_appenders 'stdout' log.level = :debug Logging.mdc['first'] = 'John' Logging.mdc['last'] = 'Doe' # in this first thread we will log some quotes by Allan Rickman t1 = Thread.new { Logging.mdc['first'] = 'Allan' Logging.mdc['last'] = 'Rickman' [ %q{I've never been able to plan my life. I just lurch from indecision to indecision.}, %q{If only life could be a little more tender and art a little more robust.}, %q{I do take my work seriously and the way to do that is not to take yourself too seriously.}, %q{I'm a quite serious actor who doesn't mind being ridiculously comic.} ].each { |quote| sleep rand log.info quote } } # in this second thread we will log some quotes by William Butler Yeats t2 = Thread.new { Logging.mdc['first'] = 'William' Logging.mdc['middle'] = 'Butler' Logging.mdc['last'] = 'Yeats' [ %q{Tread softly because you tread on my dreams.}, %q{The best lack all conviction, while the worst are full of passionate intensity.}, %q{Education is not the filling of a pail, but the lighting of a fire.}, %q{Do not wait to strike till the iron is hot; but make it hot by striking.}, %q{People who lean on logic and philosophy and rational exposition end by starving the best part of the mind.} ].each { |quote| sleep rand log.info quote } } # and in this third thread we will log some quotes by Bono t3 = Thread.new { Logging.mdc.clear # otherwise we inherit the last name "Doe" Logging.mdc['first'] = 'Bono' [ %q{Music can change the world because it can change people.}, %q{The less you know, the more you believe.} ].each { |quote| sleep rand log.info quote } } t1.join t2.join t3.join log.info %q{and now we are done} # :startdoc: logging-2.0.0/examples/hierarchies.rb0000644000175000017500000000456212551223063017146 0ustar globusglobus# :stopdoc: # # Loggers exist in a hierarchical relationship defined by their names. Each # logger has a parent (except for the root logger). A logger can zero or # more children. This parent/child relationship is determined by the Ruby # namespace separator '::'. # # root # |-- Foo # | |-- Foo::Bar # | `-- Foo::Baz # |-- ActiveRecord # | `-- ActiveRecord::Base # |-- ActiveSupport # | `-- ActiveSupport::Base # `-- Rails # # A logger inherits its log level from its parent. This level can be set for # each logger in the system. Setting the level on a logger affects all it's # children and grandchildren, etc. unless the child has it's own level set. # # Loggers also have a property called "additivity", and by default it is set # to true for all loggers. This property enables a logger to pass log events # up to its parent. # # If a logger does not have an appender and its additivity is true, it will # pass all log events up to its parent who will then try to send the log # event to its appenders. The parent will do the same thing, passing the log # event up the chain till the root logger is reached or some parent logger # has its additivity set to false. # # So, if the root logger is the only one with an appender, all loggers can # still output log events to the appender because of additivity. A logger # will ALWAYS send log events to its own appenders regardless of its # additivity. # # The show_configuration method can be used to dump the logging hierarchy. # require 'logging' Logging.logger.root.level = :debug foo = Logging.logger['Foo'] bar = Logging.logger['Foo::Bar'] baz = Logging.logger['Foo::Baz'] # configure the Foo logger foo.level = 'warn' foo.appenders = Logging.appenders.stdout # since Foo is the parent of Foo::Bar and Foo::Baz, these loggers all have # their level set to warn foo.warn 'this is a warning, not a ticket' bar.info 'this message will not be logged' baz.info 'nor will this message' bar.error 'but this error message will be logged' # let's demonstrate additivity of loggers Logging.logger.root.appenders = Logging.appenders.stdout baz.warn 'this message will be logged twice - once by Foo and once by root' foo.additive = false bar.warn "foo is no longer passing log events up to it's parent" # let's look at the logger hierarchy puts '='*76 Logging.show_configuration # :startdoc: logging-2.0.0/examples/names.rb0000644000175000017500000000307612551223063015762 0ustar globusglobus# :stopdoc: # # Loggers and appenders can be looked up by name. The bracket notation is # used to find these objects: # # Logging.logger['foo'] # Logging.appenders['bar'] # # A logger will be created if a new name is used. Appenders are different; # nil is returned when an unknown appender name is used. The reason for this # is that appenders come in many different flavors (so it is unclear which # type should be created), but there is only one type of logger. # # So it is useful to be able to create an appender and then reference it by # name to add it to multiple loggers. When the same name is used, the same # object will be returned by the bracket methods. # # Layouts do not have names. Some are stateful, and none are threadsafe. So # each appender is configured with it's own layout. # require 'logging' Logging.appenders.file('Debug File', :filename => 'debug.log') Logging.appenders.stderr('Standard Error', :level => :error) # configure the root logger Logging.logger.root.appenders = 'Debug File' Logging.logger.root.level = :debug # add the Standard Error appender to the Critical logger (it will use it's # own appender and the root logger's appender, too) Logging.logger['Critical'].appenders = 'Standard Error' # if you'll notice above, assigning appenders using just the name is valid # the logger is smart enough to figure out it was given a string and then # go lookup the appender by name # and now log some messages Logging.logger['Critical'].info 'just keeping you informed' Logging.logger['Critical'].fatal 'WTF!!' # :startdoc: logging-2.0.0/examples/rspec_integration.rb0000644000175000017500000000242512551223063020373 0ustar globusglobus# :stopdoc: # # One useful feature of log messages in your code is that they provide a # convenient instrumentation point for testing. Through log messages you can # confirm that internal methods were called or that certain code paths were # executed. This example demonstrates how to capture log output during testing # for later analysis. # # The Logging framework provides an RSpec helper that will direct log output # to a StringIO appender. Log lines can be read from this IO destination # during tests. # require 'rspec' require 'logging' require 'rspec/logging_helper' # Configure RSpec to capture log messages for each test. The output from the # logs will be stored in the @log_output variable. It is a StringIO instance. RSpec.configure do |config| include RSpec::LoggingHelper config.capture_log_messages end # Now within your specs you can check that various log events were generated. describe 'SuperLogger' do it 'should be able to read a log message' do logger = Logging.logger['SuperLogger'] logger.debug 'foo bar' logger.warn 'just a little warning' @log_output.readline.should be == 'DEBUG SuperLogger: foo bar' @log_output.readline.should be == 'WARN SuperLogger: just a little warning' end end # :startdoc: logging-2.0.0/examples/layouts.rb0000644000175000017500000000227212551223063016354 0ustar globusglobus# :stopdoc: # # The formatting of log messages is controlled by the layout given to the # appender. By default all appenders use the Basic layout. It's pretty # basic. However, a more sophisticated Pattern layout can be used or one of # the Parseable layouts -- JSON or YAML. # # The available layouts are: # # Logging.layouts.basic # Logging.layouts.pattern # Logging.layouts.json # Logging.layouts.yaml # # In this example we'll demonstrate use of different layouts and setting log # levels in the appenders to filter out events. # require 'logging' # only show "info" or higher messages on STDOUT using the Basic layout Logging.appenders.stdout(:level => :info) # send all log events to the development log (including debug) as JSON Logging.appenders.rolling_file( 'development.log', :age => 'daily', :layout => Logging.layouts.json ) log = Logging.logger['Foo::Bar'] log.add_appenders 'stdout', 'development.log' log.level = :debug log.debug "a very nice little debug message" log.info "things are operating nominally" log.warn "this is your last warning" log.error StandardError.new("something went horribly wrong") log.fatal "I Die!" # :startdoc: logging-2.0.0/examples/lazy.rb0000644000175000017500000000324312551223063015632 0ustar globusglobus# :stopdoc: # # It happens sometimes that it is very expensive to construct a logging # message; for example, if a large object structure has to be traversed # during executing of an `object.to_s` method. It would be convenient to # delay creation of the message until the log event actually takes place. # # For example, with a logger configured only to show WARN messages and higher, # creating the log message for an INFO message would be wasteful. The INFO log # event would never be generated in this case. # # Log message creation can be performed lazily by wrapping the expensive # message generation code in a block and passing that to the logging method. require 'logging' Logging.logger.root.appenders = Logging.appenders.stdout Logging.logger.root.level = :info # We use this dummy method in order to see if the method gets called, but in practice, # this method might do complicated string operations to construct a log message. def expensive_method puts "Called!" "Expensive message" end log = Logging.logger['Lazy'] # If you log this message the usual way, expensive_method gets called before # debug, hence the Logging framework has no chance to stop it from being executed # immediately. log.info("Normal") log.debug(expensive_method) # If we put the message into a block, then the block is not executed, if # the message is not needed with the current log level. log.info("Block unused") log.debug { expensive_method } # If the log message is needed with the current log level, then the block is of # course executed and the log message appears as expected. log.info("Block used") log.warn { expensive_method } # :startdoc: logging-2.0.0/examples/colorization.rb0000644000175000017500000000376712551223063017402 0ustar globusglobus# :stopdoc: # # The Pattern layout can colorize log events based on a provided color scheme. # The configuration is a two part process. First the color scheme is defined # with the level colors and any pattern token colors. This color scheme is # then passed by name to the Pattern layout when it is created. # # The color scheme defines colors to be applied to the level token found in # the pattern layout. So that the "info" level will have one color, and the # "fatal" level will have a separate color. This applies only to the level # token in the Pattern layout. # # Common tokens can have their own color, too. The date token can be colored # blue, and the message token can be colored magenta. # # Colorization should only be applied to TTY logging destinations like STDOUT # and STDERR. Inserting color codes into a log file is generally considered # bad form; these color codes cause issues for some command line programs like # "less" and "more". # # A 'default" color scheme is provided with the Logging framework. In the # example below we create our own color scheme called 'bright' and apply it to # the 'stdout' appender. # require 'logging' # here we setup a color scheme called 'bright' Logging.color_scheme( 'bright', :levels => { :info => :green, :warn => :yellow, :error => :red, :fatal => [:white, :on_red] }, :date => :blue, :logger => :cyan, :message => :magenta ) Logging.appenders.stdout( 'stdout', :layout => Logging.layouts.pattern( :pattern => '[%d] %-5l %c: %m\n', :color_scheme => 'bright' ) ) log = Logging.logger['Happy::Colors'] log.add_appenders 'stdout' log.level = :debug # these log messages will be nicely colored # the level will be colored differently for each message # log.debug "a very nice little debug message" log.info "things are operating nominally" log.warn "this is your last warning" log.error StandardError.new("something went horribly wrong") log.fatal "I Die!" # :startdoc: logging-2.0.0/examples/custom_log_levels.rb0000644000175000017500000000267312551223063020406 0ustar globusglobus# :stopdoc: # # It's useful to define custom log levels that denote success, or otherwise # meaningful events that happen to not be negative (more than 50% of the # levels are given to warn, error, fail - quite a pessimistic view of one's # application's chances of success, no? ;-) ) # # Here, we define two new levels, 'happy' and 'success' and make them soothing # colours. # require 'logging' # https://github.com/TwP/logging/blob/master/lib/logging.rb#L250-285 # The levels run from lowest level to highest level. Logging.init :debug, :info, :happy, :warn, :success, :error, :fatal Logging.color_scheme( 'soothing_ish', :levels => { :info => :cyan, :happy => :green, :warn => :yellow, :success => [:blue], :error => :red, :fatal => [:white, :on_red] }, :date => :cyan, :logger => :cyan, :message => :orange ) Logging.appenders.stdout( 'stdout', :layout => Logging.layouts.pattern( :pattern => '[%d] %-7l %c: %m\n', :color_scheme => 'soothing_ish' ) ) log = Logging.logger['Soothing::Colors'] log.add_appenders 'stdout' log.level = :debug log.debug 'a very nice little debug message' log.info 'things are operating nominally' log.happy 'What a beautiful day' log.warn 'this is your last warning' log.success 'I am INWEENCIBLE!!' log.error StandardError.new('something went horribly wrong') log.fatal 'I Die!' # :startdoc: logging-2.0.0/logging.gemspec0000644000175000017500000001144112551223063015502 0ustar globusglobus# -*- encoding: utf-8 -*- # stub: logging 2.0.0 ruby lib Gem::Specification.new do |s| s.name = "logging" s.version = "2.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.require_paths = ["lib"] s.authors = ["Tim Pease"] s.date = "2015-03-29" s.description = "Logging is a flexible logging library for use in Ruby programs based on the\ndesign of Java's log4j library. It features a hierarchical logging system,\ncustom level names, multiple output destinations per log event, custom\nformatting, and more." s.email = "tim.pease@gmail.com" s.extra_rdoc_files = ["History.txt"] s.files = [".gitignore", ".travis.yml", "History.txt", "README.md", "Rakefile", "examples/appenders.rb", "examples/classes.rb", "examples/colorization.rb", "examples/custom_log_levels.rb", "examples/fork.rb", "examples/formatting.rb", "examples/hierarchies.rb", "examples/layouts.rb", "examples/lazy.rb", "examples/loggers.rb", "examples/mdc.rb", "examples/names.rb", "examples/rspec_integration.rb", "examples/simple.rb", "lib/logging.rb", "lib/logging/appender.rb", "lib/logging/appenders.rb", "lib/logging/appenders/buffering.rb", "lib/logging/appenders/console.rb", "lib/logging/appenders/file.rb", "lib/logging/appenders/io.rb", "lib/logging/appenders/rolling_file.rb", "lib/logging/appenders/string_io.rb", "lib/logging/appenders/syslog.rb", "lib/logging/color_scheme.rb", "lib/logging/diagnostic_context.rb", "lib/logging/filter.rb", "lib/logging/filters.rb", "lib/logging/filters/level.rb", "lib/logging/layout.rb", "lib/logging/layouts.rb", "lib/logging/layouts/basic.rb", "lib/logging/layouts/parseable.rb", "lib/logging/layouts/pattern.rb", "lib/logging/log_event.rb", "lib/logging/logger.rb", "lib/logging/proxy.rb", "lib/logging/rails_compat.rb", "lib/logging/repository.rb", "lib/logging/root_logger.rb", "lib/logging/utils.rb", "lib/logging/version.rb", "lib/rspec/logging_helper.rb", "lib/spec/logging_helper.rb", "logging.gemspec", "script/bootstrap", "test/appenders/test_buffered_io.rb", "test/appenders/test_console.rb", "test/appenders/test_file.rb", "test/appenders/test_io.rb", "test/appenders/test_periodic_flushing.rb", "test/appenders/test_rolling_file.rb", "test/appenders/test_string_io.rb", "test/appenders/test_syslog.rb", "test/benchmark.rb", "test/layouts/test_basic.rb", "test/layouts/test_color_pattern.rb", "test/layouts/test_json.rb", "test/layouts/test_pattern.rb", "test/layouts/test_yaml.rb", "test/setup.rb", "test/test_appender.rb", "test/test_color_scheme.rb", "test/test_filter.rb", "test/test_layout.rb", "test/test_log_event.rb", "test/test_logger.rb", "test/test_logging.rb", "test/test_mapped_diagnostic_context.rb", "test/test_nested_diagnostic_context.rb", "test/test_proxy.rb", "test/test_repository.rb", "test/test_root_logger.rb", "test/test_utils.rb"] s.homepage = "http://rubygems.org/gems/logging" s.rdoc_options = ["--main", "README.md"] s.rubyforge_project = "logging" s.rubygems_version = "2.2.2" s.summary = "A flexible and extendable logging library for Ruby" s.test_files = ["test/appenders/test_buffered_io.rb", "test/appenders/test_console.rb", "test/appenders/test_file.rb", "test/appenders/test_io.rb", "test/appenders/test_periodic_flushing.rb", "test/appenders/test_rolling_file.rb", "test/appenders/test_string_io.rb", "test/appenders/test_syslog.rb", "test/layouts/test_basic.rb", "test/layouts/test_color_pattern.rb", "test/layouts/test_json.rb", "test/layouts/test_pattern.rb", "test/layouts/test_yaml.rb", "test/test_appender.rb", "test/test_color_scheme.rb", "test/test_filter.rb", "test/test_layout.rb", "test/test_log_event.rb", "test/test_logger.rb", "test/test_logging.rb", "test/test_mapped_diagnostic_context.rb", "test/test_nested_diagnostic_context.rb", "test/test_proxy.rb", "test/test_repository.rb", "test/test_root_logger.rb", "test/test_utils.rb"] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, ["~> 1.1"]) s.add_runtime_dependency(%q, ["~> 1.10"]) s.add_development_dependency(%q, ["~> 1.0"]) s.add_development_dependency(%q, ["~> 1.3"]) s.add_development_dependency(%q, [">= 3.8.3"]) else s.add_dependency(%q, ["~> 1.1"]) s.add_dependency(%q, ["~> 1.10"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.3"]) s.add_dependency(%q, [">= 3.8.3"]) end else s.add_dependency(%q, ["~> 1.1"]) s.add_dependency(%q, ["~> 1.10"]) s.add_dependency(%q, ["~> 1.0"]) s.add_dependency(%q, ["~> 1.3"]) s.add_dependency(%q, [">= 3.8.3"]) end end logging-2.0.0/README.md0000644000175000017500000001336212551223063013772 0ustar globusglobus## Logging by Tim Pease [![](https://secure.travis-ci.org/TwP/logging.png)](https://travis-ci.org/TwP/logging) * [Homepage](http://rubygems.org/gems/logging) * [Github Project](https://github.com/TwP/logging) ### Description Logging is a flexible logging library for use in Ruby programs based on the design of Java's log4j library. It features a hierarchical logging system, custom level names, multiple output destinations per log event, custom formatting, and more. ### Installation ``` gem install logging ``` ### Examples This example configures a logger to output messages in a format similar to the core ruby Logger class. Only log messages that are warnings or higher will be logged. ```ruby require 'logging' logger = Logging.logger(STDOUT) logger.level = :warn logger.debug "this debug message will not be output by the logger" logger.warn "this is your last warning" ``` In this example, a single logger is created that will append to STDOUT and to a file. Only log messages that are informational or higher will be logged. ```ruby require 'logging' logger = Logging.logger['example_logger'] logger.level = :info logger.add_appenders \ Logging.appenders.stdout, Logging.appenders.file('example.log') logger.debug "this debug message will not be output by the logger" logger.info "just some friendly advice" ``` The Logging library was created to allow each class in a program to have its own configurable logger. The logging level for a particular class can be changed independently of all other loggers in the system. This example shows the recommended way of accomplishing this. ```ruby require 'logging' Logging.logger['FirstClass'].level = :warn Logging.logger['SecondClass'].level = :debug class FirstClass def initialize @logger = Logging.logger[self] end def some_method @logger.debug "some method was called on #{self.inspect}" end end class SecondClass def initialize @logger = Logging.logger[self] end def another_method @logger.debug "another method was called on #{self.inspect}" end end ``` There are many more examples in the [examples folder](https://github.com/TwP/logging/tree/master/examples) of the logging package. The recommended reading order is the following: * [simple.rb](https://github.com/TwP/logging/blob/master/examples/simple.rb) * [rspec_integration.rb](https://github.com/TwP/logging/blob/master/examples/rspec_integration.rb) * [loggers.rb](https://github.com/TwP/logging/blob/master/examples/loggers.rb) * [classes.rb](https://github.com/TwP/logging/blob/master/examples/classes.rb) * [hierarchies.rb](https://github.com/TwP/logging/blob/master/examples/hierarchies.rb) * [names.rb](https://github.com/TwP/logging/blob/master/examples/names.rb) * [lazy.rb](https://github.com/TwP/logging/blob/master/examples/lazy.rb) * [appenders.rb](https://github.com/TwP/logging/blob/master/examples/appenders.rb) * [layouts.rb](https://github.com/TwP/logging/blob/master/examples/layouts.rb) * [formatting.rb](https://github.com/TwP/logging/blob/master/examples/formatting.rb) * [colorization.rb](https://github.com/TwP/logging/blob/master/examples/colorization.rb) * [consolidation.rb](https://github.com/TwP/logging/blob/master/examples/consolidation.rb) * [fork.rb](https://github.com/TwP/logging/blob/master/examples/fork.rb) * [mdc.rb](https://github.com/TwP/logging/blob/master/examples/mdc.rb) ### Extending The Logging framework is extensible via the [little-plugger](https://github.com/twp/little-plugger) gem based plugin system. New appenders or formatters can be released as ruby gems. When installed locally, the Logging framework will automatically detect these gems as plugins and make them available for use. The [logging-email](https://github.com/twp/logging-email) plugin is a good example to follow. It includes a [`lib/logging/plugins/email.rb`](https://github.com/twp/logging-email/tree/master/lib/logging/plugins/email.rb) file which is detected by the plugin framework. This file declares a `Logging::Plugins::Email.initialize_email` method that is called when the plugin is loaded. The three steps for creating a plugin are: * create a new Ruby gem: `logging-` * include a plugin file: `lib/logging/plugins/.rb` * definie a plugin initializer: `Logging::Plugins::.initialize_` ### Development The Logging source code relies on the Mr Bones project for default rake tasks. You will need to install the Mr Bones gem if you want to build or test the logging gem. Conveniently there is a bootstrap script that you can run to setup your development environment. ``` script/bootstrap ``` This will install the Mr Bones gem and the required Ruby gems for development. After this is done you can rake `rake -T` to see the available rake tasks. ### License The MIT License Copyright (c) 2015 Tim Pease Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. logging-2.0.0/test/0000755000175000017500000000000012551223063013465 5ustar globusglobuslogging-2.0.0/test/test_root_logger.rb0000644000175000017500000000342512551223063017377 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestRootLogger < Test::Unit::TestCase include LoggingTestCase def setup super @root = ::Logging::Logger.root end def test_additive assert_raise(NoMethodError) {@root.additive} end def test_additive_eq assert_raise(NoMethodError) {@root.additive = true} end def test_level_eq assert_equal 0, @root.level assert_raise(ArgumentError) {@root.level = -1} assert_raise(ArgumentError) {@root.level = 6} assert_raise(ArgumentError) {@root.level = Object} assert_raise(ArgumentError) {@root.level = 'bob'} assert_raise(ArgumentError) {@root.level = :wtf} @root.level = 'INFO' assert_equal 1, @root.level @root.level = :warn assert_equal 2, @root.level @root.level = 'error' assert_equal 3, @root.level @root.level = 4 assert_equal 4, @root.level @root.level = :all assert_equal 0, @root.level @root.level = 'OFF' assert_equal 5, @root.level @root.level = nil assert_equal 0, @root.level end def test_name assert_equal 'root', @root.name end def test_parent assert_raise(NoMethodError) {@root.parent} end def test_parent_eq assert_raise(NoMethodError) {@root.parent = nil} end def test_spaceship logs = %w( A A::B A::B::C A::B::C::D A::B::C::E A::B::C::E::G A::B::C::F ).map {|x| ::Logging::Logger[x]} logs.each do |log| assert_equal(-1, @root <=> log, "'root' <=> '#{log.name}'") end assert_equal 0, @root <=> @root assert_raise(ArgumentError) {@root <=> 'string'} end end # class TestRootLogger end # module TestLogging logging-2.0.0/test/test_layout.rb0000644000175000017500000000642212551223063016372 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestLayout < Test::Unit::TestCase include LoggingTestCase def setup super @layout = ::Logging::Layout.new end def test_header assert_equal '', @layout.header end def test_initialize obj_format = lambda {|l| l.instance_variable_get :@obj_format} assert_equal :string, obj_format[@layout] @layout = ::Logging::Layout.new :format_as => 'blah' assert_equal :string, obj_format[@layout] @layout = ::Logging::Layout.new :format_as => :inspect assert_equal :inspect, obj_format[@layout] @layout = ::Logging::Layout.new :format_as => :json assert_equal :json, obj_format[@layout] @layout = ::Logging::Layout.new :format_as => :yaml assert_equal :yaml, obj_format[@layout] @layout = ::Logging::Layout.new assert_equal :string, obj_format[@layout] ::Logging.format_as :yaml @layout = ::Logging::Layout.new assert_equal :yaml, obj_format[@layout] end def test_footer assert_equal '', @layout.footer end def test_format assert_nil @layout.format(::Logging::LogEvent.new('a','b','c',false)) end def test_format_obj obj = 'test string' r = @layout.format_obj obj assert_same obj, r obj = RuntimeError.new r = @layout.format_obj obj assert_equal ' RuntimeError', r obj = TypeError.new 'only works with Integers' r = @layout.format_obj obj assert_equal ' only works with Integers', r obj = Exception.new 'some exception' obj.set_backtrace %w( this is the backtrace ) r = @layout.format_obj obj obj = " some exception\n\tthis\n\tis\n\tthe\n\tbacktrace" assert_equal obj, r obj = [1, 2, 3, 4] r = @layout.format_obj obj assert_equal " #{[1,2,3,4]}", r obj = %w( one two three four ) @layout = ::Logging::Layout.new :format_as => :inspect r = @layout.format_obj obj assert_equal ' ["one", "two", "three", "four"]', r @layout = ::Logging::Layout.new :format_as => :json r = @layout.format_obj obj assert_equal ' ["one","two","three","four"]', r @layout = ::Logging::Layout.new :format_as => :yaml r = @layout.format_obj obj assert_match %r/\A \n--- ?\n- one\n- two\n- three\n- four\n/, r r = @layout.format_obj Class assert_match %r/\A (\n--- !ruby\/class ')?Class('\n)?/, r end def test_format_obj_without_backtrace @layout = ::Logging::Layout.new :backtrace => 'off' obj = Exception.new 'some exception' obj.set_backtrace %w( this is the backtrace ) r = @layout.format_obj obj obj = " some exception" assert_equal obj, r ::Logging.backtrace :off @layout = ::Logging::Layout.new obj = ArgumentError.new 'wrong type of argument' obj.set_backtrace %w( this is the backtrace ) r = @layout.format_obj obj obj = " wrong type of argument" assert_equal obj, r end def test_initializer assert_raise(ArgumentError) {::Logging::Layout.new :backtrace => 'foo'} end end # class TestLayout end # module TestLogging logging-2.0.0/test/layouts/0000755000175000017500000000000012551223063015165 5ustar globusglobuslogging-2.0.0/test/layouts/test_yaml.rb0000644000175000017500000001221412551223063017513 0ustar globusglobusrequire 'time' require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestLayouts class TestYaml < Test::Unit::TestCase include LoggingTestCase def setup super @layout = Logging.layouts.yaml({}) @levels = Logging::LEVELS @date_fmt = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(Z|[+-]\d{2}:\d{2})' Thread.current[:name] = nil end def test_format h = { 'level' => 'INFO', 'logger' => 'ArrayLogger', 'message' => 'log message' } event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) assert_yaml_match h, @layout.format(event) event.data = [1, 2, 3, 4] h['message'] = [1,2,3,4] assert_yaml_match h, @layout.format(event) event.level = @levels['debug'] event.data = 'and another message' h['level'] = 'DEBUG' h['message'] = 'and another message' assert_yaml_match h, @layout.format(event) event.logger = 'Test' event.level = @levels['fatal'] event.data = Exception.new h['level'] = 'FATAL' h['logger'] = 'Test' h['message'] = {:class => 'Exception', :message => 'Exception'} assert_yaml_match h, @layout.format(event) end def test_items assert_equal %w[timestamp level logger message], @layout.items end def test_items_eq event = Logging::LogEvent.new('TestLogger', @levels['info'], ['log message'], false) @layout.items = %w[timestamp] assert_equal %w[timestamp], @layout.items assert_match %r/\A--- ?\ntimestamp: ["']#@date_fmt["']\n/, @layout.format(event) # 'foo' is not a recognized item assert_raise(ArgumentError) { @layout.items = %w[timestamp logger foo] } end def test_items_all event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) event.file = 'test_file.rb' event.line = 123 event.method = 'method_name' @layout.items = %w[logger] assert_match %r/\A--- ?\nlogger: TestLogger\n/, @layout.format(event) @layout.items = %w[file] assert_match %r/\A--- ?\nfile: test_file.rb\n/, @layout.format(event) @layout.items = %w[level] assert_match %r/\A--- ?\nlevel: INFO\n/, @layout.format(event) @layout.items = %w[line] assert_match %r/\A--- ?\nline: 123\n/, @layout.format(event) @layout.items = %w[message] assert_match %r/\A--- ?\nmessage: log message\n/, @layout.format(event) @layout.items = %w[method] assert_match %r/\A--- ?\nmethod: method_name\n/, @layout.format(event) @layout.items = %w[hostname] assert_match %r/\A--- ?\nhostname: #{Socket.gethostname}\n/, @layout.format(event) @layout.items = %w[pid] assert_match %r/\A--- ?\npid: \d+\n\z/, @layout.format(event) @layout.items = %w[millis] assert_match %r/\A--- ?\nmillis: \d+\n\z/, @layout.format(event) @layout.items = %w[thread_id] assert_match %r/\A--- ?\nthread_id: -?\d+\n\z/, @layout.format(event) @layout.items = %w[thread] assert_match %r/\A--- ?\nthread: ?\n/, @layout.format(event) Thread.current[:name] = "Main" assert_match %r/\A--- ?\nthread: Main\n/, @layout.format(event) @layout.items = %w[mdc] assert_match %r/\A--- ?\nmdc: \{\}\n/, @layout.format(event) @layout.items = %w[ndc] assert_match %r/\A--- ?\nndc: \[\]\n/, @layout.format(event) end def test_mdc_output event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.mdc['X-Session'] = '123abc' Logging.mdc['Cookie'] = 'monster' @layout.items = %w[timestamp level logger message mdc] format = @layout.format(event) assert_match %r/\nmdc: ?(?:\n (?:X-Session: 123abc|Cookie: monster)\n?){2}/, format Logging.mdc.delete 'Cookie' format = @layout.format(event) assert_match %r/\nmdc: ?\n X-Session: 123abc\n/, format end def test_ndc_output event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.ndc << 'context a' Logging.ndc << 'context b' @layout.items = %w[timestamp level logger message ndc] format = @layout.format(event) assert_match %r/\nndc: ?\n\s*- context a\n\s*- context b\n/, format Logging.ndc.pop format = @layout.format(event) assert_match %r/\nndc: ?\n\s*- context a\n/, format Logging.ndc.pop format = @layout.format(event) assert_match %r/\nndc: \[\]\n/, format end private def assert_yaml_match( expected, actual ) actual = YAML.load(actual) assert_instance_of String, actual['timestamp'] assert_instance_of Time, Time.parse(actual['timestamp']) assert_equal expected['level'], actual['level'] assert_equal expected['logger'], actual['logger'] assert_equal expected['message'], actual['message'] end end # class TestYaml end # module TestLayouts end # module TestLogging logging-2.0.0/test/layouts/test_color_pattern.rb0000644000175000017500000000651112551223063021427 0ustar globusglobus require File.expand_path('../../setup', __FILE__) module TestLogging module TestLayouts class TestColorPattern < Test::Unit::TestCase include LoggingTestCase CS = ::Logging::ColorScheme def setup super ::Logging.color_scheme :levels, :levels => { :debug => :blue, :info => :green, :warn => :yellow, :error => :red, :fatal => :cyan } ::Logging.color_scheme :lines, :lines => { :debug => :blue, :info => :green, :warn => :yellow, :error => :red, :fatal => :cyan }, :date => :blue, :logger => :cyan ::Logging.color_scheme :tokens, :date => :blue, :logger => :green, :message => :magenta @levels = Logging::LEVELS end def test_level_coloring layout = Logging.layouts.pattern(:color_scheme => :levels) event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) rgxp = Regexp.new(Regexp.escape("#{CS::GREEN}INFO #{CS::RESET}")) assert_match rgxp, layout.format(event) event.level = @levels['debug'] rgxp = Regexp.new(Regexp.escape("#{CS::BLUE}DEBUG#{CS::RESET}")) assert_match rgxp, layout.format(event) event.level = @levels['error'] rgxp = Regexp.new(Regexp.escape("#{CS::RED}ERROR#{CS::RESET}")) assert_match rgxp, layout.format(event) end def test_multiple_level_coloring layout = Logging.layouts.pattern(:pattern => '%.1l, %5l -- %c: %m\n', :color_scheme => :levels) event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) rgxp = Regexp.new(Regexp.escape("#{CS::GREEN}I#{CS::RESET}, #{CS::GREEN} INFO#{CS::RESET}")) assert_match rgxp, layout.format(event) event.level = @levels['debug'] rgxp = Regexp.new(Regexp.escape("#{CS::BLUE}D#{CS::RESET}, #{CS::BLUE}DEBUG#{CS::RESET}")) assert_match rgxp, layout.format(event) event.level = @levels['error'] rgxp = Regexp.new(Regexp.escape("#{CS::RED}E#{CS::RESET}, #{CS::RED}ERROR#{CS::RESET}")) assert_match rgxp, layout.format(event) end def test_line_coloring layout = Logging.layouts.pattern(:color_scheme => :lines) event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) rgxp = Regexp.new('^'+Regexp.escape(CS::GREEN)+'.*?'+Regexp.escape(CS::RESET)+'$', Regexp::MULTILINE) assert_match rgxp, layout.format(event) event.level = @levels['error'] rgxp = Regexp.new('^'+Regexp.escape(CS::RED)+'.*?'+Regexp.escape(CS::RESET)+'$', Regexp::MULTILINE) assert_match rgxp, layout.format(event) event.level = @levels['warn'] rgxp = Regexp.new('^'+Regexp.escape(CS::YELLOW)+'.*?'+Regexp.escape(CS::RESET)+'$', Regexp::MULTILINE) assert_match rgxp, layout.format(event) end def test_token_coloring layout = Logging.layouts.pattern(:color_scheme => :tokens) event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) rgxp = Regexp.new( '^\['+Regexp.escape(CS::BLUE)+'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'+Regexp.escape(CS::RESET)+ '\] INFO -- '+Regexp.escape(CS::GREEN)+'ArrayLogger'+Regexp.escape(CS::RESET)+ ' : '+Regexp.escape(CS::MAGENTA)+'log message'+Regexp.escape(CS::RESET) ) assert_match rgxp, layout.format(event) end end # TestColorPattern end # TestLayouts end # TestLogging logging-2.0.0/test/layouts/test_json.rb0000644000175000017500000001330612551223063017525 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestLayouts class TestJson < Test::Unit::TestCase include LoggingTestCase def setup super @layout = Logging.layouts.json({}) @levels = Logging::LEVELS @date_fmt = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}(Z|[+-]\d{2}:\d{2})' Thread.current[:name] = nil end def test_initializer assert_raise(ArgumentError) { Logging.layouts.parseable.new :style => :foo } end def test_format event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"INFO"/, format assert_match %r/"logger":"ArrayLogger"/, format assert_match %r/"message":"log message"/, format event.data = [1, 2, 3, 4] format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"INFO"/, format assert_match %r/"logger":"ArrayLogger"/, format assert_match %r/"message":\[1,2,3,4\]/, format event.level = @levels['debug'] event.data = 'and another message' format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"DEBUG"/, format assert_match %r/"logger":"ArrayLogger"/, format assert_match %r/"message":"and another message"/, format event.logger = 'Test' event.level = @levels['fatal'] event.data = Exception.new format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"FATAL"/, format assert_match %r/"logger":"Test"/, format assert_match %r/"message":\{(?:"(?:class|message)":"Exception",?){2}\}/, format end def test_items assert_equal %w[timestamp level logger message], @layout.items end def test_items_eq event = Logging::LogEvent.new('TestLogger', @levels['info'], ['log message'], false) @layout.items = %w[timestamp] assert_equal %w[timestamp], @layout.items assert_match %r/\{"timestamp":"#@date_fmt"\}\n/, @layout.format(event) # 'foo' is not a recognized item assert_raise(ArgumentError) { @layout.items = %w[timestamp logger foo] } end def test_items_all event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) event.file = 'test_file.rb' event.line = 123 event.method = 'method_name' @layout.items = %w[logger] assert_equal %Q[{"logger":"TestLogger"}\n], @layout.format(event) @layout.items = %w[file] assert_equal %Q[{"file":"test_file.rb"}\n], @layout.format(event) @layout.items = %w[level] assert_equal %Q[{"level":"INFO"}\n], @layout.format(event) @layout.items = %w[line] assert_equal %Q[{"line":123}\n], @layout.format(event) @layout.items = %w[message] assert_equal %Q[{"message":"log message"}\n], @layout.format(event) @layout.items = %w[method] assert_equal %Q[{"method":"method_name"}\n], @layout.format(event) @layout.items = %w[hostname] assert_equal %Q[{"hostname":"#{Socket.gethostname}"}\n], @layout.format(event) @layout.items = %w[pid] assert_match %r/\A\{"pid":\d+\}\n\z/, @layout.format(event) @layout.items = %w[millis] assert_match %r/\A\{"millis":\d+\}\n\z/, @layout.format(event) @layout.items = %w[thread_id] assert_match %r/\A\{"thread_id":-?\d+\}\n\z/, @layout.format(event) @layout.items = %w[thread] assert_equal %Q[{"thread":null}\n], @layout.format(event) Thread.current[:name] = "Main" assert_equal %Q[{"thread":"Main"}\n], @layout.format(event) @layout.items = %w[mdc] assert_match %r/\A\{"mdc":\{\}\}\n\z/, @layout.format(event) @layout.items = %w[ndc] assert_match %r/\A\{"ndc":\[\]\}\n\z/, @layout.format(event) end def test_mdc_output event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.mdc['X-Session'] = '123abc' Logging.mdc['Cookie'] = 'monster' @layout.items = %w[timestamp level logger message mdc] format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"INFO"/, format assert_match %r/"logger":"TestLogger"/, format assert_match %r/"message":"log message"/, format assert_match %r/"mdc":\{(?:(?:"X-Session":"123abc"|"Cookie":"monster"),?){2}\}/, format Logging.mdc.delete 'Cookie' format = @layout.format(event) assert_match %r/"mdc":\{"X-Session":"123abc"\}/, format end def test_ndc_output event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.ndc << 'context a' Logging.ndc << 'context b' @layout.items = %w[timestamp level logger message ndc] format = @layout.format(event) assert_match %r/"timestamp":"#@date_fmt"/, format assert_match %r/"level":"INFO"/, format assert_match %r/"logger":"TestLogger"/, format assert_match %r/"message":"log message"/, format assert_match %r/"ndc":\["context a","context b"\]/, format Logging.ndc.pop format = @layout.format(event) assert_match %r/"ndc":\["context a"\]/, format Logging.ndc.pop format = @layout.format(event) assert_match %r/"ndc":\[\]/, format end end # class TestJson end # module TestLayouts end # module TestLogging logging-2.0.0/test/layouts/test_pattern.rb0000644000175000017500000001571412551223063020236 0ustar globusglobus require File.expand_path('../../setup', __FILE__) module TestLogging module TestLayouts class TestPattern < Test::Unit::TestCase include LoggingTestCase def setup super @layout = Logging.layouts.pattern({}) @levels = Logging::LEVELS @date_fmt = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}' Thread.current[:name] = nil end def test_date_method assert_nil @layout.date_method end def test_date_method_eq @layout.date_method = :to_f assert_equal :to_f, @layout.date_method assert_instance_of Float, @layout.format_date(Time.now) @layout.date_method = 'usec' assert_equal 'usec', @layout.date_method assert_instance_of Fixnum, @layout.format_date(Time.now) @layout.date_method = :to_s assert_equal :to_s, @layout.date_method assert_instance_of String, @layout.format_date(Time.now) # now, even if we have defined a date_pattern, the date_method should # supersede the date_pattern @layout.date_pattern = '%Y' @layout.date_method = 'usec' assert_equal 'usec', @layout.date_method assert_instance_of Fixnum, @layout.format_date(Time.now) end def test_date_pattern assert_equal '%Y-%m-%dT%H:%M:%S', @layout.date_pattern end def test_date_pattern_eq @layout.date_pattern = '%Y' assert_equal '%Y', @layout.date_pattern assert_match %r/\A\d{4}\z/, @layout.format_date(Time.now) @layout.date_pattern = '%H:%M' assert_equal '%H:%M', @layout.date_pattern assert_match %r/\A\d{2}:\d{2}\z/, @layout.format_date(Time.now) end def test_format fmt = '\[' + @date_fmt + '\] %s -- %s : %s\n' event = Logging::LogEvent.new('ArrayLogger', @levels['info'], 'log message', false) rgxp = Regexp.new(sprintf(fmt, 'INFO ', 'ArrayLogger', 'log message')) assert_match rgxp, @layout.format(event) event.data = [1, 2, 3, 4] rgxp = Regexp.new(sprintf(fmt, 'INFO ', 'ArrayLogger', Regexp.escape(" #{[1,2,3,4]}"))) assert_match rgxp, @layout.format(event) event.level = @levels['debug'] event.data = 'and another message' rgxp = Regexp.new( sprintf(fmt, 'DEBUG', 'ArrayLogger', 'and another message')) assert_match rgxp, @layout.format(event) event.logger = 'Test' event.level = @levels['fatal'] event.data = Exception.new rgxp = Regexp.new( sprintf(fmt, 'FATAL', 'Test', ' Exception')) assert_match rgxp, @layout.format(event) end def test_format_date rgxp = Regexp.new @date_fmt assert_match rgxp, @layout.format_date(Time.now) end def test_pattern assert_equal "[%d] %-5l -- %c : %m\n", @layout.pattern end def test_pattern_eq event = Logging::LogEvent.new('TestLogger', @levels['info'], ['log message'], false) @layout.pattern = '%d' assert_equal '%d', @layout.pattern assert_match Regexp.new(@date_fmt), @layout.format(event) end def test_pattern_all event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) event.file = 'test_file.rb' event.line = '123' event.method = 'method_name' @layout.pattern = '%c' assert_equal 'TestLogger', @layout.format(event) @layout.pattern = '%d' assert_match Regexp.new(@date_fmt), @layout.format(event) @layout.pattern = '%F' assert_equal 'test_file.rb', @layout.format(event) @layout.pattern = '%l' assert_equal 'INFO', @layout.format(event) @layout.pattern = '%L' assert_equal '123', @layout.format(event) @layout.pattern = '%m' assert_equal 'log message', @layout.format(event) @layout.pattern = '%M' assert_equal 'method_name', @layout.format(event) @layout.pattern = '%p' assert_match %r/\A\d+\z/, @layout.format(event) @layout.pattern = '%r' assert_match %r/\A\d+\z/, @layout.format(event) @layout.pattern = '%t' assert_match %r/\A-?\d+\z/, @layout.format(event) @layout.pattern = '%T' assert_equal "", @layout.format(event) Thread.current[:name] = "Main" assert_equal "Main", @layout.format(event) @layout.pattern = '%h' hostname = Socket.gethostname assert_equal hostname, @layout.format(event) @layout.pattern = '%%' assert_equal '%', @layout.format(event) # 'z' is not a recognized format character assert_raise(ArgumentError) { @layout.pattern = '[%d] %% %c - %l %z blah' } assert_equal '%', @layout.format(event) @layout.pattern = '%5l' assert_equal ' INFO', @layout.format(event) @layout.pattern = '%-5l' assert_equal 'INFO ', @layout.format(event) @layout.pattern = '%.1l, %c' assert_equal 'I, TestLogger', @layout.format(event) @layout.pattern = '%7.7m' assert_equal 'log mes', @layout.format(event) event.data = 'tim' assert_equal ' tim', @layout.format(event) @layout.pattern = '%-7.7m' assert_equal 'tim ', @layout.format(event) end def test_pattern_logger_name_precision event = Logging::LogEvent.new('Foo', @levels['info'], 'message', false) @layout.pattern = '%c{2}' assert_equal 'Foo', @layout.format(event) event.logger = 'Foo::Bar::Baz::Buz' assert_equal 'Baz::Buz', @layout.format(event) assert_raise(ArgumentError) { @layout.pattern = '%c{0}' } @layout.pattern = '%c{foo}' event.logger = 'Foo::Bar' assert_equal 'Foo::Bar{foo}', @layout.format(event) @layout.pattern = '%m{42}' assert_equal 'message{42}', @layout.format(event) end def test_pattern_mdc @layout.pattern = 'S:%X{X-Session} C:%X{Cookie}' event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.mdc['X-Session'] = '123abc' Logging.mdc['Cookie'] = 'monster' assert_equal 'S:123abc C:monster', @layout.format(event) Logging.mdc.delete 'Cookie' assert_equal 'S:123abc C:', @layout.format(event) Logging.mdc.delete 'X-Session' assert_equal 'S: C:', @layout.format(event) end def test_pattern_mdc_requires_key_name assert_raise(ArgumentError) { @layout.pattern = '%X' } assert_raise(ArgumentError) { @layout.pattern = '%X{}' } end def test_pattern_ndc @layout.pattern = '%x' event = Logging::LogEvent.new('TestLogger', @levels['info'], 'log message', false) Logging.ndc << 'context a' Logging.ndc << 'context b' assert_equal 'context a context b', @layout.format(event) @layout.pattern = '%x{, }' assert_equal 'context a, context b', @layout.format(event) Logging.ndc.pop assert_equal 'context a', @layout.format(event) end end # TestBasic end # TestLayouts end # TestLogging logging-2.0.0/test/layouts/test_basic.rb0000644000175000017500000000213412551223063017632 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestLayouts class TestBasic < Test::Unit::TestCase include LoggingTestCase def setup super @layout = Logging.layouts.basic({}) @levels = Logging::LEVELS end def test_format event = Logging::LogEvent.new( 'ArrayLogger', @levels['info'], 'log message', false) assert_equal " INFO ArrayLogger : log message\n", @layout.format(event) event.data = [1, 2, 3, 4] assert_equal(" INFO ArrayLogger : #{[1,2,3,4]}\n", @layout.format(event)) event.level = @levels['debug'] event.data = 'and another message' log = "DEBUG ArrayLogger : and another message\n" assert_equal log, @layout.format(event) event.logger = 'Test' event.level = @levels['fatal'] event.data = Exception.new log = "FATAL Test : Exception\n" assert_equal log, @layout.format(event) end end # class TestBasic end # module TestLayouts end # module TestLogging logging-2.0.0/test/test_mapped_diagnostic_context.rb0000644000175000017500000000633512551223063022276 0ustar globusglobus require File.expand_path('../setup', __FILE__) module TestLogging class TestMappedDiagnosticContext < Test::Unit::TestCase include LoggingTestCase def test_key_value_access assert_nil Logging.mdc['foo'] Logging.mdc['foo'] = 'bar' assert_equal 'bar', Logging.mdc[:foo] assert_same Logging.mdc['foo'], Logging.mdc[:foo] Logging.mdc.delete(:foo) assert_nil Logging.mdc['foo'] end def test_clear Logging.mdc['foo'] = 'bar' Logging.mdc['baz'] = 'buz' assert_equal 'bar', Logging.mdc[:foo] assert_equal 'buz', Logging.mdc[:baz] Logging.mdc.clear assert_nil Logging.mdc['foo'] assert_nil Logging.mdc['baz'] end def test_context_update Logging.mdc.update(:foo => 'bar', :baz => 'buz') assert_equal 'bar', Logging.mdc[:foo] assert_equal 'buz', Logging.mdc[:baz] Logging.mdc.update('foo' => 1, 'baz' => 2) assert_equal 1, Logging.mdc[:foo] assert_equal 2, Logging.mdc[:baz] assert_equal 1, Logging.mdc.stack.length end def test_context_pushing assert Logging.mdc.context.empty? assert_equal 1, Logging.mdc.stack.length Logging.mdc.push(:foo => 'bar', :baz => 'buz') assert_equal 'bar', Logging.mdc[:foo] assert_equal 'buz', Logging.mdc[:baz] assert_equal 2, Logging.mdc.stack.length Logging.mdc.push(:foo => 1, :baz => 2, :foobar => 3) assert_equal 1, Logging.mdc[:foo] assert_equal 2, Logging.mdc[:baz] assert_equal 3, Logging.mdc[:foobar] assert_equal 3, Logging.mdc.stack.length Logging.mdc.pop assert_equal 'bar', Logging.mdc[:foo] assert_equal 'buz', Logging.mdc[:baz] assert_nil Logging.mdc[:foobar] assert_equal 2, Logging.mdc.stack.length Logging.mdc.pop assert Logging.mdc.context.empty? assert_equal 1, Logging.mdc.stack.length Logging.mdc.pop assert Logging.mdc.context.empty? assert_equal 1, Logging.mdc.stack.length end def test_thread_uniqueness Logging.mdc['foo'] = 'bar' Logging.mdc['baz'] = 'buz' t = Thread.new { sleep Logging.mdc.clear assert_nil Logging.mdc['foo'] assert_nil Logging.mdc['baz'] Logging.mdc['foo'] = 42 assert_equal 42, Logging.mdc['foo'] } Thread.pass until t.status == 'sleep' t.run t.join assert_equal 'bar', Logging.mdc['foo'] assert_equal 'buz', Logging.mdc['baz'] end def test_thread_inheritance Logging.mdc['foo'] = 'bar' Logging.mdc['baz'] = 'buz' Logging.mdc.push(:foo => 1, 'foobar' => 'something else') t = Thread.new(Logging.mdc.context) { |context| sleep assert_not_equal context.object_id, Logging.mdc.context.object_id assert_equal 1, Logging.mdc['foo'] assert_equal 'buz', Logging.mdc['baz'] assert_equal 'something else', Logging.mdc['foobar'] assert_nil Logging.mdc['unique'] assert_equal 1, Logging.mdc.stack.length } Thread.pass until t.status == 'sleep' Logging.mdc.pop Logging.mdc['unique'] = 'value' t.run t.join end end # class TestMappedDiagnosticContext end # module TestLogging logging-2.0.0/test/test_appender.rb0000644000175000017500000001230712551223063016652 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestAppender < Test::Unit::TestCase include LoggingTestCase def setup super ::Logging.init @levels = ::Logging::LEVELS @event = ::Logging::LogEvent.new('logger', @levels['debug'], 'message', false) @appender = ::Logging::Appender.new 'test_appender' end def test_append ary = [] @appender.instance_variable_set :@ary, ary def @appender.write( event ) str = event.instance_of?(::Logging::LogEvent) ? @layout.format(event) : event.to_s @ary << str end assert_nothing_raised {@appender.append @event} assert_equal "DEBUG logger : message\n", ary.pop @appender.level = :info @appender.append @event assert_nil ary.pop @event.level = @levels['info'] @appender.append @event assert_equal " INFO logger : message\n", ary.pop @appender.close assert_raise(RuntimeError) {@appender.append @event} end def test_append_with_filter ary = [] @appender.instance_variable_set :@ary, ary def @appender.write(event) @ary << event end @appender.level = :debug # Excluded @appender.filters = ::Logging::Filters::Level.new :info @appender.append @event assert_nil ary.pop # Allowed @appender.filters = ::Logging::Filters::Level.new :debug @appender.append @event assert_equal @event, ary.pop # No filter @appender.filters = nil @appender.append @event assert_equal @event, ary.pop end def test_append_with_modifying_filter ary = [] @appender.instance_variable_set :@ary, ary def @appender.write(event) @ary << event end @appender.level = :debug @appender.filters = [ ::Logging::Filters::Level.new(:debug, :info), RedactFilter.new ] # data will be redacted @appender.append @event event = ary.pop assert_not_same @event, event assert_equal "REDACTED!", event.data # event will be filtered out @event.level = @levels['warn'] @appender.append @event assert_nil ary.pop end def test_close assert_equal false, @appender.closed? @appender.close assert_equal true, @appender.closed? end def test_closed_eh assert_equal false, @appender.closed? @appender.close assert_equal true, @appender.closed? end def test_concat ary = [] @appender.instance_variable_set :@ary, ary def @appender.write( event ) str = event.instance_of?(::Logging::LogEvent) ? @layout.format(event) : event.to_s @ary << str end assert_nothing_raised {@appender << 'log message'} assert_equal 'log message', ary.pop @appender.level = :off @appender << 'another log message' assert_nil ary.pop layout = @appender.layout def layout.footer() 'this is the footer' end @appender.close assert_raise(RuntimeError) {@appender << 'log message'} assert_equal 'this is the footer', ary.pop end def test_flush assert_same @appender, @appender.flush end def test_initialize assert_raise(TypeError) {::Logging::Appender.new 'test', :layout => []} layout = ::Logging::Layouts::Basic.new @appender = ::Logging::Appender.new 'test', :layout => layout assert_same layout, @appender.instance_variable_get(:@layout) end def test_layout assert_instance_of ::Logging::Layouts::Basic, @appender.layout end def test_layout_eq layout = ::Logging::Layouts::Basic.new assert_not_equal layout, @appender.layout assert_raise(TypeError) {@appender.layout = Object.new} assert_raise(TypeError) {@appender.layout = 'not a layout'} @appender.layout = layout assert_same layout, @appender.layout end def test_level assert_equal 0, @appender.level end def test_level_eq assert_equal 0, @appender.level assert_raise(ArgumentError) {@appender.level = -1} assert_raise(ArgumentError) {@appender.level = 6} assert_raise(ArgumentError) {@appender.level = Object} assert_raise(ArgumentError) {@appender.level = 'bob'} assert_raise(ArgumentError) {@appender.level = :wtf} @appender.level = 'INFO' assert_equal 1, @appender.level @appender.level = :warn assert_equal 2, @appender.level @appender.level = 'error' assert_equal 3, @appender.level @appender.level = 4 assert_equal 4, @appender.level @appender.level = 'off' assert_equal 5, @appender.level @appender.level = :all assert_equal 0, @appender.level end def test_name assert_equal 'test_appender', @appender.name end def test_inspect expected = "" % @appender.object_id assert_equal expected, @appender.inspect end end # class TestAppender end # module TestLogging class RedactFilter < ::Logging::Filter def allow( event ) event = event.dup event.data = "REDACTED!" event end end logging-2.0.0/test/appenders/0000755000175000017500000000000012551223063015446 5ustar globusglobuslogging-2.0.0/test/appenders/test_periodic_flushing.rb0000644000175000017500000001015312551223063022527 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestPeriodicFlushing < Test::Unit::TestCase include LoggingTestCase def setup super @appender = Logging.appenders.string_io( 'test_appender', :flush_period => 2 ) @appender.clear @sio = @appender.sio @levels = Logging::LEVELS begin readline rescue EOFError end Thread.pass # give the flusher thread a moment to start end def teardown @appender.close @appender = nil super end def test_flush_period_set assert_equal 2, @appender.flush_period assert_equal Logging::Appenders::Buffering::DEFAULT_BUFFER_SIZE, @appender.auto_flushing @appender.flush_period = '01:30:45' assert_equal 5445, @appender.flush_period @appender.flush_period = '245' assert_equal 245, @appender.flush_period @appender.auto_flushing = true assert_equal Logging::Appenders::Buffering::DEFAULT_BUFFER_SIZE, @appender.auto_flushing @appender.auto_flushing = 200 assert_equal 200, @appender.auto_flushing end def test_periodic_flusher_running flusher = @appender.instance_variable_get(:@periodic_flusher) assert_instance_of Logging::Appenders::Buffering::PeriodicFlusher, flusher sleep 0.250 # give the flusher thread another moment to start assert flusher.waiting?, 'the periodic flusher should be waiting for a signal' end def test_append event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event @appender.append event event.level = @levels['debug'] event.data = 'the big log message' @appender.append event assert_nil(readline) sleep 3 assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) @appender.close assert_raise(RuntimeError) {@appender.append event} end def test_flush_on_close assert_equal false, @sio.closed? assert_equal false, @appender.closed? event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.flush_period = "24:00:00" @appender.append event event.level = @levels['debug'] event.data = 'the big log message' @appender.append event assert_nil(readline) @appender.close_method = :close_write @appender.close assert_equal false, @sio.closed? assert_equal true, @appender.closed? assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) @sio.close assert_equal true, @sio.closed? end def test_auto_flushing @appender.auto_flushing = 3 event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event @appender.append event event.level = @levels['debug'] event.data = 'the big log message' @appender.append event event.level = @levels['info'] event.data = 'just FYI' @appender.append event event.level = @levels['warn'] event.data = 'this is your last warning!' @appender.append event assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) sleep 3 assert_equal " INFO TestLogger : just FYI\n", readline assert_equal " WARN TestLogger : this is your last warning!\n", readline assert_nil(readline) end private def readline @appender.readline end end # class TestPeriodicFlushing end # module TestAppenders end # module TestLogging logging-2.0.0/test/appenders/test_file.rb0000644000175000017500000000742312551223063017757 0ustar globusglobus# encoding: UTF-8 require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestFile < Test::Unit::TestCase include LoggingTestCase NAME = 'logfile' def setup super Logging.init FileUtils.mkdir [File.join(TMP, 'dir'), File.join(TMP, 'uw_dir')] FileUtils.chmod 0555, File.join(TMP, 'uw_dir') FileUtils.touch File.join(TMP, 'uw_file') FileUtils.chmod 0444, File.join(TMP, 'uw_file') end def test_class_assert_valid_logfile log = File.join(TMP, 'uw_dir', 'file.log') assert_raise(ArgumentError) do Logging.appenders.file.assert_valid_logfile(log) end log = File.join(TMP, 'dir') assert_raise(ArgumentError) do Logging.appenders.file.assert_valid_logfile(log) end log = File.join(TMP, 'uw_file') assert_raise(ArgumentError) do Logging.appenders.file.assert_valid_logfile(log) end log = File.join(TMP, 'file.log') assert Logging.appenders.file.assert_valid_logfile(log) end def test_initialize log = File.join(TMP, 'file.log') appender = Logging.appenders.file(NAME, :filename => log) assert_equal 'logfile', appender.name assert_equal ::File.expand_path(log), appender.filename appender << "This will be the first line\n" appender << "This will be the second line\n" appender.flush File.open(log, 'r') do |file| assert_equal "This will be the first line\n", file.readline assert_equal "This will be the second line\n", file.readline assert_raise(EOFError) {file.readline} end cleanup appender = Logging.appenders.file(NAME, :filename => log) assert_equal 'logfile', appender.name assert_equal ::File.expand_path(log), appender.filename appender << "This will be the third line\n" appender.flush File.open(log, 'r') do |file| assert_equal "This will be the first line\n", file.readline assert_equal "This will be the second line\n", file.readline assert_equal "This will be the third line\n", file.readline assert_raise(EOFError) {file.readline} end cleanup appender = Logging.appenders.file(NAME, :filename => log, :truncate => true) assert_equal 'logfile', appender.name appender << "The file was truncated\n" appender.flush File.open(log, 'r') do |file| assert_equal "The file was truncated\n", file.readline assert_raise(EOFError) {file.readline} end cleanup end def test_changing_directories log = File.join(TMP, 'file.log') appender = Logging.appenders.file(NAME, :filename => log) assert_equal 'logfile', appender.name assert_equal ::File.expand_path(log), appender.filename begin pwd = Dir.pwd Dir.chdir TMP assert_nothing_raised { appender.reopen } ensure Dir.chdir pwd end end if Object.const_defined? :Encoding def test_encoding log = File.join(TMP, 'file-encoding.log') #appender = Logging.appenders.file(NAME, :filename => log, :encoding => 'ISO-8859-16') appender = Logging.appenders.file(NAME, :filename => log, :encoding => 'ASCII') appender << "A normal line of text\n" appender << "ümlaut\n" appender.close lines = File.readlines(log) assert_equal "A normal line of text\n", lines[0] assert_equal "ümlaut\n", lines[1] cleanup end end private def cleanup unless Logging.appenders[NAME].nil? Logging.appenders[NAME].close false Logging.appenders[NAME] = nil end end end # TestFile end # TestAppenders end # TestLogging logging-2.0.0/test/appenders/test_console.rb0000644000175000017500000000332512551223063020477 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestStdout < Test::Unit::TestCase include LoggingTestCase def test_initialize Logging::Repository.instance appender = Logging.appenders.stdout assert_equal 'stdout', appender.name assert_same STDOUT, appender.instance_variable_get(:@io) appender.close assert_equal true, appender.closed? assert_equal false, STDOUT.closed? appender = Logging.appenders.stdout('foo') assert_equal 'foo', appender.name appender = Logging.appenders.stdout(:level => :warn) assert_equal 'stdout', appender.name assert_equal 2, appender.level appender = Logging.appenders.stdout('bar', :level => :error) assert_equal 'bar', appender.name assert_equal 3, appender.level end end # class TestStdout class TestStderr < Test::Unit::TestCase include LoggingTestCase def test_initialize Logging::Repository.instance appender = Logging.appenders.stderr assert_equal 'stderr', appender.name assert_same STDERR, appender.instance_variable_get(:@io) appender.close assert_equal true, appender.closed? assert_equal false, STDERR.closed? appender = Logging.appenders.stderr('foo') assert_equal 'foo', appender.name appender = Logging.appenders.stderr(:level => :warn) assert_equal 'stderr', appender.name assert_equal 2, appender.level appender = Logging.appenders.stderr('bar', :level => :error) assert_equal 'bar', appender.name assert_equal 3, appender.level end end # class TestStderr end # module TestAppenders end # module TestLogging logging-2.0.0/test/appenders/test_io.rb0000644000175000017500000000720412551223063017444 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestIO < Test::Unit::TestCase include LoggingTestCase def setup super @appender = Logging.appenders.string_io('test_appender') @sio = @appender.sio @levels = Logging::LEVELS end def test_append event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_nil(readline) event.level = @levels['debug'] event.data = 'the big log message' @appender.append event assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) @appender.close assert_raise(RuntimeError) {@appender.append event} end def test_append_error # setup an internal logger to capture error messages from the IO # appender log = Logging.appenders.string_io('__internal_io') Logging.logger[Logging].add_appenders(log) Logging.logger[Logging].level = 'all' # close the string IO object so we get an error @sio.close event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event assert_equal "INFO Logging : appender \"test_appender\" has been disabled", log.readline.strip assert_equal "ERROR Logging : not opened for writing", log.readline.strip assert_equal false, @appender.closed? assert_equal 5, @appender.level end def test_close assert_equal false, @sio.closed? assert_equal false, @appender.closed? @appender.close assert_equal true, @sio.closed? assert_equal true, @appender.closed? [STDIN, STDERR, STDOUT].each do |io| @appender = Logging.appenders.io('test', io) @appender.close assert_equal false, io.closed? assert_equal true, @appender.closed? end end def test_concat @appender << "this is a test message\n" assert_equal "this is a test message\n", readline assert_nil(readline) @appender << "this is another message\n" @appender << "some other line\n" assert_equal "this is another message\n", readline assert_equal "some other line\n", readline assert_nil(readline) @appender.close assert_raise(RuntimeError) {@appender << 'message'} end def test_concat_error # setup an internal logger to capture error messages from the IO # appender log = Logging.appenders.string_io('__internal_io') Logging.logger[Logging].add_appenders(log) Logging.logger[Logging].level = 'all' # close the string IO object so we get an error @sio.close @appender << 'oopsy' assert_equal "INFO Logging : appender \"test_appender\" has been disabled", log.readline.strip assert_equal "ERROR Logging : not opened for writing", log.readline.strip # and the appender does not close itself assert_equal false, @appender.closed? assert_equal 5, @appender.level end def test_flush @appender.buffer << 'flush' assert_nil @appender.readline @appender.flush assert_equal 'flush', @appender.readline end def test_initialize assert_raise(EOFError) {@sio.readline} assert_raise(TypeError) {Logging.appenders.io('test', [])} end private def readline @appender.readline end end # class TestIO end # module TestAppenders end # module TestLogging logging-2.0.0/test/appenders/test_string_io.rb0000644000175000017500000000133512551223063021031 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestStringIO < Test::Unit::TestCase include LoggingTestCase def setup super @appender = Logging.appenders.string_io('test_appender') @sio = @appender.sio @levels = Logging::LEVELS end def teardown @appender.close @appender = nil super end def test_reopen assert_equal @sio.object_id, @appender.sio.object_id @appender.reopen assert @sio.closed?, 'StringIO instance is closed' assert_not_equal @sio.object_id, @appender.sio.object_id end end # class TestStringIO end # module TestAppenders end # module TestLogging logging-2.0.0/test/appenders/test_buffered_io.rb0000644000175000017500000001460312551223063021307 0ustar globusglobus# encoding: UTF-8 require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestBufferedIO < Test::Unit::TestCase include LoggingTestCase def setup super @appender = Logging.appenders.string_io( 'test_appender', :auto_flushing => 3, :immediate_at => :error ) @appender.clear @sio = @appender.sio @levels = Logging::LEVELS begin readline rescue EOFError end end def test_append event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event assert_nil(readline) @appender.append event assert_nil(readline) event.level = @levels['debug'] event.data = 'the big log message' @appender.append event assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) @appender.close assert_raise(RuntimeError) {@appender.append event} end def test_append_with_write_size event = Logging::LogEvent.new('TestLogger', @levels['warn'], %w[a b c d], false) @appender.write_size = 2 @appender.append event assert_nil(readline) @appender.append event assert_nil(readline) event.level = @levels['debug'] event.data = 'the big log message' @appender.append event assert_equal " WARN TestLogger : #{%w[a b c d]}\n", readline assert_equal " WARN TestLogger : #{%w[a b c d]}\n", readline assert_equal "DEBUG TestLogger : the big log message\n", readline assert_nil(readline) end def test_append_error # setup an internal logger to capture error messages from the IO # appender log = Logging.appenders.string_io('__internal_io') Logging.logger[Logging].add_appenders(log) Logging.logger[Logging].level = 'all' # close the string IO object so we get an error @sio.close event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event assert_nil(log.readline) @appender.append event assert_nil(log.readline) @appender.append event assert_equal "INFO Logging : appender \"test_appender\" has been disabled", log.readline.strip assert_equal "ERROR Logging : not opened for writing", log.readline.strip assert_equal false, @appender.closed? assert_equal 5, @appender.level end def test_auto_flushing assert_raise(ArgumentError) { @appender.auto_flushing = Object.new } assert_raise(ArgumentError) { @appender.auto_flushing = -1 } @appender.auto_flushing = 0 assert_equal Logging::Appenders::Buffering::DEFAULT_BUFFER_SIZE, @appender.auto_flushing end def test_close assert_equal false, @sio.closed? assert_equal false, @appender.closed? @appender.close assert_equal true, @sio.closed? assert_equal true, @appender.closed? [STDIN, STDERR, STDOUT].each do |io| @appender = Logging.appenders.io('test', io) @appender.close assert_equal false, io.closed? assert_equal true, @appender.closed? end end def test_concat @appender << "this is a test message\n" assert_nil(readline) @appender << "this is another message\n" assert_nil(readline) @appender << "some other line\n" assert_equal "this is a test message\n", readline assert_equal "this is another message\n", readline assert_equal "some other line\n", readline assert_nil(readline) @appender.close assert_raise(RuntimeError) {@appender << 'message'} end def test_concat_error # setup an internal logger to capture error messages from the IO # appender log = Logging.appenders.string_io('__internal_io') Logging.logger[Logging].add_appenders(log) Logging.logger[Logging].level = 'all' # close the string IO object so we get an error @sio.close @appender << 'oopsy' assert_nil(log.readline) @appender << 'whoopsy' assert_nil(log.readline) @appender << 'pooh' assert_equal "INFO Logging : appender \"test_appender\" has been disabled", log.readline.strip assert_equal "ERROR Logging : not opened for writing", log.readline.strip # and the appender does not close itself assert_equal false, @appender.closed? assert_equal 5, @appender.level end def test_flush @appender << "this is a test message\n" assert_nil(readline) @appender.flush assert_equal "this is a test message\n", readline assert_nil(readline) end def test_clear @appender << "this is a test message\n" @appender << "this is another test message\n" @appender.clear! @appender.flush assert_nil(readline) end def test_immediate_at event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) @appender.append event assert_nil(readline) event.level = @levels['error'] event.data = 'an error message' @appender.append event assert_equal " WARN TestLogger : #{[1, 2, 3, 4]}\n", readline assert_equal "ERROR TestLogger : an error message\n", readline assert_nil(readline) end if Object.const_defined?(:Encoding) def test_force_encoding a = 'ümlaut' b = 'hello ümlaut'.force_encoding('BINARY') event_a = Logging::LogEvent.new('TestLogger', @levels['info'], a, false) event_b = Logging::LogEvent.new('TestLogger', @levels['info'], b, false) @appender.append event_a @appender.append event_b assert_nil(readline) @appender.append event_a assert_equal " INFO TestLogger : #{a}\n", readline assert_equal " INFO TestLogger : #{b.force_encoding('UTF-8')}\n", readline assert_equal " INFO TestLogger : #{a}\n", readline assert_nil(readline) end end private def readline @appender.readline end end # class TestBufferedIO end # module TestAppenders end # module TestLogging logging-2.0.0/test/appenders/test_rolling_file.rb0000644000175000017500000002223612551223063021504 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) module TestLogging module TestAppenders class TestRollingFile < Test::Unit::TestCase include LoggingTestCase NAME = 'roller' def setup super Logging.init @fn = File.expand_path('test.log', TMP) @fn_fmt = File.expand_path('test.%d.log', TMP) @glob = File.expand_path('*.log', TMP) end def test_initialize assert_equal [], Dir.glob(@glob) # create a new appender ap = Logging.appenders.rolling_file(NAME, :filename => @fn) assert_equal @fn, ap.filename assert File.exist?(@fn) assert_equal 0, File.size(@fn) ap << "Just a line of text\n" # 20 bytes ap.flush assert_equal 20, File.size(@fn) cleanup # make sure we append to the current file (not truncate) ap = Logging.appenders.rolling_file(NAME, :filename => @fn) assert_equal @fn, ap.filename assert_equal [@fn], Dir.glob(@glob) assert_equal 20, File.size(@fn) ap << "Just another line of text\n" # 26 bytes ap.flush assert_equal 46, File.size(@fn) cleanup # setting the truncate option to true should roll the current log file # and create a new one ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :truncate => true) log1 = sprintf(@fn_fmt, 1) assert_equal [log1, @fn], Dir.glob(@glob).sort assert_equal 0, File.size(@fn) assert_equal 46, File.size(log1) ap << "Some more text in the new file\n" # 31 bytes ap.flush assert_equal 31, File.size(@fn) cleanup end def test_keep assert_equal [], Dir.glob(@glob) (1..12).each do |cnt| name = sprintf(@fn_fmt, cnt) File.open(name,'w') {|fd| fd.write 'X'*cnt} end FileUtils.touch(@fn) # keep only five files ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :keep => 5) # we still have 13 files because we did not truncate the log file, # and hence, we did not roll all the log files assert_equal 13, Dir.glob(@glob).length # force the appender to roll the files ap.send :copy_truncate ap.instance_variable_get(:@roller).roll_files assert_equal 6, Dir.glob(@glob).length (1..5).each do |cnt| name = sprintf(@fn_fmt, cnt) assert_equal cnt-1, File.size(name) end cleanup end def test_age d_glob = File.join(TMP, 'test.*.log') dt_glob = File.join(TMP, 'test.*-*.log') age_fn = @fn + '.age' assert_equal [], Dir.glob(@glob) assert_raise(ArgumentError) do Logging.appenders.rolling_file(NAME, :filename => @fn, :age => 'bob') end ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :age => 1) ap << "random message\n" assert_equal 1, Dir.glob(@glob).length now = ::File.mtime(age_fn) start = now - 42 ::File.utime(start, start, age_fn) ap.instance_variable_set(:@age_fn_mtime, nil) ap << "another random message\n" assert_equal 1, Dir.glob(dt_glob).length Dir.glob(d_glob).each {|fn| ::File.delete fn} cleanup ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :age => 'daily') ap << "random message\n" assert_equal 1, Dir.glob(@glob).length now = ::File.mtime(age_fn) start = now - 3600 * 24 ::File.utime(start, start, age_fn) ap.instance_variable_set(:@age_fn_mtime, nil) sleep 0.250 ap << "yet another random message\n" assert_equal 0, Dir.glob(dt_glob).length assert_equal 1, Dir.glob(d_glob).length Dir.glob(d_glob).each {|fn| ::File.delete fn} cleanup ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :age => 'weekly') ap << "random message\n" assert_equal 1, Dir.glob(@glob).length start = now - 3600 * 24 * 7 ::File.utime(start, start, age_fn) ap.instance_variable_set(:@age_fn_mtime, nil) sleep 0.250 ap << "yet another random message\n" assert_equal 0, Dir.glob(dt_glob).length assert_equal 1, Dir.glob(d_glob).length Dir.glob(d_glob).each {|fn| ::File.delete fn} cleanup ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :age => 'monthly') ap << "random message\n" assert_equal 1, Dir.glob(@glob).length start = now - 3600 * 24 * 31 ::File.utime(start, start, age_fn) ap.instance_variable_set(:@age_fn_mtime, nil) sleep 0.250 ap << "yet another random message\n" assert_equal 0, Dir.glob(dt_glob).length assert_equal 1, Dir.glob(d_glob).length end def test_size assert_equal [], Dir.glob(@glob) ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :size => 100) ap << 'X' * 100; ap.flush assert_equal 1, Dir.glob(@glob).length assert_equal 100, File.size(@fn) # this character is appended to the log file (bringing its size to 101) # and THEN the file is rolled resulting in a new, empty log file ap << 'X' assert_equal 2, Dir.glob(@glob).length assert_equal 0, File.size(@fn) ap << 'X' * 100; ap.flush assert_equal 2, Dir.glob(@glob).length assert_equal 100, File.size(@fn) ap << 'X' assert_equal 3, Dir.glob(@glob).length assert_equal 0, File.size(@fn) cleanup end def test_file_removed assert_equal [], Dir.glob(@glob) ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :size => 100) ap << 'X' * 100; ap.flush assert_equal 1, Dir.glob(@glob).length assert_equal 100, File.size(@fn) end def test_changing_directories ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :size => 100) begin pwd = Dir.pwd Dir.chdir TMP ap << 'X' * 100; ap.flush assert_equal 1, Dir.glob(@glob).length ap << 'X'; ap.flush assert_equal 2, Dir.glob(@glob).length ensure Dir.chdir pwd end end def test_stale_copy_file ap = Logging.appenders.rolling_file(NAME, :filename => @fn, :size => 100) fn_copy = @fn + '._copy_' File.open(fn_copy, 'w') { |copy| copy.puts 'stale copy file' } ap << 'X' * 100; ap.flush assert_equal 1, Dir.glob(@glob).length assert_equal 100, File.size(@fn) # this character is appended to the log file (bringing its size to 101) # but the file is NOT ROLLED because the _copy_ file is in the way ap << 'X' assert_equal 1, Dir.glob(@glob).length assert_equal 101, File.size(@fn) assert_equal 16, File.size(fn_copy) # if the _copy_ file is older than three minutes, it will be # concatenated to and moved out of the way time = Time.now - 200 ::File.utime(time, time, fn_copy) ap << 'X' assert_equal 2, Dir.glob(@glob).length assert_equal 0, File.size(@fn) assert_equal 118, File.size(Dir.glob(@glob).sort.first) assert !File.exist?(fn_copy), '_copy_ file should not exist' cleanup end def test_custom_numberd_filename fn = File.expand_path('test.log{{.%d}}', TMP) filename = File.expand_path('test.log', TMP) glob = File.expand_path('test.log.*', TMP) assert_equal [], Dir.glob(glob) ap = Logging.appenders.rolling_file(NAME, :filename => fn, :size => 100, :keep => 2) ap << 'X' * 100; ap.flush assert_equal 0, Dir.glob(glob).length assert_equal 100, File.size(filename) # this character is appended to the log file (bringing its size to 101) # and THEN the file is rolled resulting in a new, empty log file ap << 'X' assert_equal 1, Dir.glob(glob).length assert_equal 0, File.size(filename) ap << 'Y' * 100; ap.flush assert_equal 1, Dir.glob(glob).length assert_equal 100, File.size(filename) ap << 'Y' assert_equal 2, Dir.glob(glob).length assert_equal 0, File.size(filename) # now make sure we prune the correct file ap << 'Z' * 101; ap.flush files = Dir.glob(glob).sort assert_equal 2, files.length assert_equal 'Z'*101, ::File.read(files.first) assert_equal 'Y'*101, ::File.read(files.last) cleanup end def test_custom_timestamp_filename fn = File.expand_path('test{{.%S:%M}}.log', TMP) filename = File.expand_path('test.log', TMP) age_file = filename + '.age' glob = File.expand_path('test.*.log', TMP) assert_equal [], Dir.glob(glob) ap = Logging.appenders.rolling_file(NAME, :filename => fn, :age => 1, :keep => 2) ap << "random message\n" assert_equal 0, Dir.glob(glob).length now = ::File.mtime(age_file) start = now - 42 ::File.utime(start, start, age_file) ap.instance_variable_set(:@age_fn_mtime, nil) ap << "another random message\n" files = Dir.glob(glob) assert_equal 1, files.length assert_match %r/test\.\d{2}:\d{2}\.log\z/, files.first cleanup end private def cleanup unless Logging.appenders[NAME].nil? Logging.appenders[NAME].close false Logging.appenders[NAME] = nil end end end # TestRollingFile end # TestAppenders end # TestLogging logging-2.0.0/test/appenders/test_syslog.rb0000644000175000017500000001127212551223063020355 0ustar globusglobus require File.expand_path('../setup', File.dirname(__FILE__)) if HAVE_SYSLOG module TestLogging module TestAppenders class TestSyslog < Test::Unit::TestCase include LoggingTestCase include ::Syslog::Constants def setup super Logging.init @levels = Logging::LEVELS @logopt = 0 @logopt |= ::Syslog::LOG_NDELAY if defined?(::Syslog::LOG_NDELAY) @logopt |= ::Syslog::LOG_PERROR if defined?(::Syslog::LOG_PERROR) end def test_append return if RUBY_PLATFORM =~ %r/cygwin|java/i stderr = IO::pipe pid = fork do stderr[0].close STDERR.reopen(stderr[1]) stderr[1].close appender = create_syslog event = Logging::LogEvent.new('TestLogger', @levels['info'], [1, 2, 3, 4], false) appender.append event event.level = @levels['debug'] event.data = 'the big log message' appender.append event appender.level = :warn event.level = @levels['info'] event.data = 'this message should not get logged' appender.append event event.level = @levels['warn'] event.data = 'this is your last warning' appender.append event exit! end stderr[1].close Process.waitpid(pid) if defined?(::Syslog::LOG_PERROR) assert_match(%r/INFO TestLogger : #{Regexp.escape [1,2,3,4].to_s}/, stderr[0].gets) assert_match(%r/DEBUG TestLogger : the big log message/, stderr[0].gets) assert_match(%r/WARN TestLogger : this is your last warning/, stderr[0].gets) end end def test_append_error appender = create_syslog appender.close false event = Logging::LogEvent.new('TestLogger', @levels['warn'], [1, 2, 3, 4], false) assert_raise(RuntimeError) {appender.append event} assert_equal true, appender.closed? end def test_close appender = create_syslog assert_equal false, appender.closed? appender.close false assert_equal true, appender.closed? end def test_concat return if RUBY_PLATFORM =~ %r/cygwin|java/i stderr = IO::pipe pid = fork do stderr[0].close STDERR.reopen(stderr[1]) stderr[1].close appender = create_syslog appender << 'this is a test message' appender << 'this is another message' appender << 'some other line' exit! end stderr[1].close Process.waitpid(pid) if defined?(::Syslog::LOG_PERROR) assert_match(%r/this is a test message/, stderr[0].gets) assert_match(%r/this is another message/, stderr[0].gets) assert_match(%r/some other line/, stderr[0].gets) end end def test_concat_error appender = create_syslog appender.close false assert_raise(RuntimeError) {appender << 'oopsy'} assert_equal true, appender.closed? end def test_map_eq appender = create_syslog assert_equal( [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT], get_map_from(appender) ) appender.map = { :debug => LOG_DEBUG, :info => 'LOG_NOTICE', :warn => :LOG_WARNING, :error => 'log_err', :fatal => :log_alert } assert_equal( [LOG_DEBUG, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_ALERT], get_map_from(appender) ) end def test_map_eq_error appender = create_syslog # Object is not a valid syslog level assert_raise(ArgumentError) do appender.map = {:debug => Object} end # there is no syslog level named "info" # it should be "log_info" assert_raise(NameError) do appender.map = {:info => 'lg_info'} end end def test_initialize_map appender = Logging.appenders.syslog( 'syslog_test', :logopt => @logopt, :map => { :debug => :log_debug, :info => :log_info, :warn => :log_warning, :error => :log_err, :fatal => :log_alert } ) assert_equal( [LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_ALERT], get_map_from(appender) ) end private def create_syslog layout = Logging.layouts.pattern(:pattern => '%5l %c : %m') Logging.appenders.syslog( 'syslog_test', :logopt => @logopt, :facility => ::Syslog::LOG_USER, :layout => layout ) end def get_map_from( syslog ) syslog.instance_variable_get :@map end end # class TestSyslog end # module TestAppenders end # module TestLogging end # HAVE_SYSLOG logging-2.0.0/test/test_repository.rb0000644000175000017500000001022412551223063017267 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestRepository < Test::Unit::TestCase include LoggingTestCase def setup super @repo = ::Logging::Repository.instance end def test_instance assert_same @repo, ::Logging::Repository.instance end def test_aref root = @repo[:root] assert_same root, @repo[:root] a = [] ::Logging::Logger.new a assert_same @repo['Array'], @repo[Array] assert_same @repo['Array'], @repo[a] assert_not_same @repo['Array'], @repo[:root] ::Logging::Logger.new 'A' ::Logging::Logger.new 'A::B' assert_not_same @repo['A'], @repo['A::B'] end def test_aset root = @repo[:root] @repo[:root] = 'root' assert_not_same root, @repo[:root] assert_nil @repo['blah'] @repo['blah'] = 'root' assert_equal 'root', @repo['blah'] end def test_fetch assert @repo.has_logger?(:root) assert_same @repo[:root], @repo.fetch(:root) assert !@repo.has_logger?('A') assert_raise(KeyError) {@repo.fetch 'A'} %w(A A::B A::B::C::D A::B::C::E A::B::C::F).each do |name| ::Logging::Logger.new(name) end assert @repo.has_logger?('A') assert @repo.has_logger?('A::B') end def test_delete %w(A A::B A::C A::B::D).each do |name| ::Logging::Logger.new(name) end assert @repo.has_logger?('A') assert @repo.has_logger?('A::B') assert @repo.has_logger?('A::C') assert @repo.has_logger?('A::B::D') assert_raise(RuntimeError) {@repo.delete :root} assert_raise(KeyError) {@repo.delete 'Does::Not::Exist'} @repo.delete 'A' assert !@repo.has_logger?('A') assert_equal @repo[:root], @repo['A::B'].parent assert_equal @repo[:root], @repo['A::C'].parent assert_equal @repo['A::B'], @repo['A::B::D'].parent @repo.delete 'A::B' assert !@repo.has_logger?('A::B') assert_equal @repo[:root], @repo['A::B::D'].parent end def test_parent %w(A A::B A::B::C::D A::B::C::E A::B::C::F).each do |name| ::Logging::Logger.new(name) end assert_same @repo[:root], @repo.parent('A') assert_same @repo['A'], @repo.parent('A::B') assert_same @repo['A::B'], @repo.parent('A::B::C') assert_same @repo['A::B'], @repo.parent('A::B::C::D') assert_same @repo['A::B'], @repo.parent('A::B::C::E') assert_same @repo['A::B'], @repo.parent('A::B::C::F') ::Logging::Logger.new('A::B::C') assert_same @repo['A::B'], @repo.parent('A::B::C') assert_same @repo['A::B::C'], @repo.parent('A::B::C::D') assert_same @repo['A::B::C'], @repo.parent('A::B::C::E') assert_same @repo['A::B::C'], @repo.parent('A::B::C::F') ::Logging::Logger.new('A::B::C::E::G') assert_same @repo['A::B::C::E'], @repo.parent('A::B::C::E::G') assert_nil @repo.parent('root') end def test_children ::Logging::Logger.new('A') assert_equal [], @repo.children('A') ::Logging::Logger.new('A::B') a = %w(D E F).map {|name| ::Logging::Logger.new('A::B::C::'+name)}.sort assert_equal [@repo['A::B']], @repo.children('A') assert_equal a, @repo.children('A::B') assert_equal [], @repo.children('A::B::C') ::Logging::Logger.new('A::B::C') assert_equal [@repo['A::B::C']], @repo.children('A::B') assert_equal a, @repo.children('A::B::C') ::Logging::Logger.new('A::B::C::E::G') assert_equal a, @repo.children('A::B::C') assert_equal [@repo['A::B::C::E::G']], @repo.children('A::B::C::E') assert_equal [@repo['A'], @repo['Logging']], @repo.children('root') end def test_to_key assert_equal :root, @repo.to_key(:root) assert_equal 'Object', @repo.to_key('Object') assert_equal 'Object', @repo.to_key(Object) assert_equal 'Object', @repo.to_key(Object.new) assert_equal 'String', @repo.to_key(String) assert_equal 'Array', @repo.to_key([]) assert_equal 'blah', @repo.to_key('blah') assert_equal 'blah', @repo.to_key(:blah) end end # class TestRepository end # module TestLogging logging-2.0.0/test/test_logging.rb0000644000175000017500000001436612551223063016511 0ustar globusglobus require File.expand_path('../setup', __FILE__) module TestLogging class TestLogging < Test::Unit::TestCase include LoggingTestCase def setup super @levels = ::Logging::LEVELS @lnames = ::Logging::LNAMES @fn = File.join(TMP, 'test.log') @glob = File.join(TMP, '*.log') end def test_backtrace assert_equal true, ::Logging.backtrace assert_equal false, ::Logging.backtrace('off') assert_equal false, ::Logging.backtrace assert_equal true, ::Logging.backtrace('on') assert_equal true, ::Logging.backtrace assert_equal false, ::Logging.backtrace(:off) assert_equal false, ::Logging.backtrace assert_equal true, ::Logging.backtrace(:on) assert_equal true, ::Logging.backtrace assert_equal false, ::Logging.backtrace(false) assert_equal false, ::Logging.backtrace assert_equal true, ::Logging.backtrace(true) assert_equal true, ::Logging.backtrace assert_raise(ArgumentError) {::Logging.backtrace 'foo'} end def test_logger assert_raise(TypeError) {::Logging.logger []} logger = ::Logging.logger STDOUT assert_match %r/\A-?\d+\z/, logger.name assert_same logger, ::Logging.logger(STDOUT) logger.close assert !STDOUT.closed? assert !File.exist?(@fn) fd = File.new @fn, 'w' logger = ::Logging.logger fd, 2, 100 assert_equal @fn, logger.name logger.debug 'this is a debug message' logger.warn 'this is a warning message' logger.error 'and now we should have over 100 bytes of data ' + 'in the log file' logger.info 'but the log file should not roll since we provided ' + 'a file descriptor -- not a file name' logger.close assert fd.closed? assert File.exist?(@fn) assert_equal 1, Dir.glob(@glob).length FileUtils.rm_f @fn assert !File.exist?(@fn) logger = ::Logging.logger @fn, 2, 100 assert File.exist?(@fn) assert_equal @fn, logger.name logger.debug 'this is a debug message' logger.warn 'this is a warning message' logger.error 'and now we should have over 100 bytes of data ' + 'in the log file' logger.info 'but the log file should not roll since we provided ' + 'a file descriptor -- not a file name' logger.close assert_equal 3, Dir.glob(@glob).length end def test_init_default assert_equal({}, @levels) assert_equal([], @lnames) assert_same false, ::Logging.initialized? ::Logging::Repository.instance assert_equal 5, @levels.length assert_equal 5, @lnames.length assert_equal 5, ::Logging::MAX_LEVEL_LENGTH assert_equal 0, @levels['debug'] assert_equal 1, @levels['info'] assert_equal 2, @levels['warn'] assert_equal 3, @levels['error'] assert_equal 4, @levels['fatal'] assert_equal 'DEBUG', @lnames[0] assert_equal 'INFO', @lnames[1] assert_equal 'WARN', @lnames[2] assert_equal 'ERROR', @lnames[3] assert_equal 'FATAL', @lnames[4] end def test_init_special assert_equal({}, @levels) assert_equal([], @lnames) assert_same false, ::Logging.initialized? assert_raise(ArgumentError) {::Logging.init(1, 2, 3, 4)} ::Logging.init :one, 'two', :THREE, 'FoUr', :sIx assert_equal 5, @levels.length assert_equal 5, @lnames.length assert_equal 5, ::Logging::MAX_LEVEL_LENGTH assert_equal 0, @levels['one'] assert_equal 1, @levels['two'] assert_equal 2, @levels['three'] assert_equal 3, @levels['four'] assert_equal 4, @levels['six'] assert_equal 'ONE', @lnames[0] assert_equal 'TWO', @lnames[1] assert_equal 'THREE', @lnames[2] assert_equal 'FOUR', @lnames[3] assert_equal 'SIX', @lnames[4] end def test_init_all_off assert_equal({}, @levels) assert_equal([], @lnames) assert_same false, ::Logging.initialized? ::Logging.init %w(a b all c off d) assert_equal 4, @levels.length assert_equal 4, @lnames.length assert_equal 3, ::Logging::MAX_LEVEL_LENGTH assert_equal 0, @levels['a'] assert_equal 1, @levels['b'] assert_equal 2, @levels['c'] assert_equal 3, @levels['d'] assert_equal 'A', @lnames[0] assert_equal 'B', @lnames[1] assert_equal 'C', @lnames[2] assert_equal 'D', @lnames[3] end def test_format_as assert_equal false, ::Logging.const_defined?('OBJ_FORMAT') assert_raises(ArgumentError) {::Logging.format_as 'bob'} assert_raises(ArgumentError) {::Logging.format_as String} assert_raises(ArgumentError) {::Logging.format_as :what?} remove_const = lambda do |const| ::Logging.class_eval {remove_const const if const_defined? const} end ::Logging.format_as :string assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :string, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as :inspect assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :inspect, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as :json assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :json, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as :yaml assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :yaml, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as 'string' assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :string, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as 'inspect' assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :inspect, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] ::Logging.format_as 'yaml' assert ::Logging.const_defined?('OBJ_FORMAT') assert_equal :yaml, ::Logging::OBJ_FORMAT remove_const[:OBJ_FORMAT] end def test_path path = ::Logging.path(*%w[one two three]) assert_match %r/one\/two\/three$/, path end def test_version assert_match %r/\d+\.\d+\.\d+/, ::Logging.version end end # class TestLogging end # module TestLogging logging-2.0.0/test/benchmark.rb0000644000175000017500000000430112551223063015742 0ustar globusglobus require 'rubygems' libpath = File.expand_path('../../lib', __FILE__) $:.unshift libpath require 'logging' begin gem 'log4r' require 'log4r' $log4r = true rescue LoadError $log4r = false end require 'benchmark' require 'logger' module Logging class Benchmark def run this_many = 300_000 Logging.appenders.string_io( 'sio', :layout => Logging.layouts.pattern( :pattern => '%.1l, [%d] %5l -- %c: %m\n', :date_pattern => "%Y-%m-%dT%H:%M:%S.%s" ) ) sio = Logging.appenders['sio'].sio logging = ::Logging.logger('benchmark') logging.level = :warn logging.appenders = 'sio' logger = ::Logger.new sio logger.level = ::Logger::WARN log4r = if $log4r x = ::Log4r::Logger.new('benchmark') x.level = ::Log4r::WARN x.add ::Log4r::IOOutputter.new( 'benchmark', sio, :formatter => ::Log4r::PatternFormatter.new( :pattern => "%.1l, [%d #\#{Process.pid}] %5l : %M\n", :date_pattern => "%Y-%m-%dT%H:%M:%S.\#{Time.now.usec}" ) ) x end puts "== Debug (not logged) ==\n" ::Benchmark.bm(10) do |bm| bm.report('Logging:') {this_many.times {logging.debug 'not logged'}} bm.report('Logger:') {this_many.times {logger.debug 'not logged'}} bm.report('Log4r:') {this_many.times {log4r.debug 'not logged'}} if log4r end puts "\n== Warn (logged) ==\n" ::Benchmark.bm(10) do |bm| sio.seek 0 bm.report('Logging:') {this_many.times {logging.warn 'logged'}} sio.seek 0 bm.report('Logger:') {this_many.times {logger.warn 'logged'}} sio.seek 0 bm.report('Log4r:') {this_many.times {log4r.warn 'logged'}} if log4r end puts "\n== Concat ==\n" ::Benchmark.bm(10) do |bm| sio.seek 0 bm.report('Logging:') {this_many.times {logging << 'logged'}} sio.seek 0 bm.report('Logger:') {this_many.times {logger << 'logged'}} puts "Log4r: not supported" if log4r end end end # class Benchmark end # module Logging if __FILE__ == $0 bm = ::Logging::Benchmark.new bm.run end logging-2.0.0/test/test_filter.rb0000644000175000017500000000162112551223063016336 0ustar globusglobusrequire File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestFilter < Test::Unit::TestCase include LoggingTestCase def setup super ::Logging::init @lf = ::Logging::Filters::Level.new :debug, :warn end def test_level_filter_includes_selected_level debug_evt = event_for_level(:debug) warn_evt = event_for_level(:warn) assert_same debug_evt, @lf.allow(debug_evt), "Debug messages should be allowed" assert_same warn_evt, @lf.allow(warn_evt), "Warn messages should be allowed" end def test_level_filter_excludes_unselected_level event = event_for_level(:info) assert_nil @lf.allow(event), "Info messages should be disallowed" end def event_for_level(level) ::Logging::LogEvent.new('logger', ::Logging::LEVELS[level.to_s], 'message', false) end end end logging-2.0.0/test/test_log_event.rb0000644000175000017500000000316412551223063017037 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestLogEvent < Test::Unit::TestCase include LoggingTestCase def setup super @appender = EventAppender.new('test') @logger = ::Logging::Logger['TestLogger'] @logger.add_appenders @appender @logger.info 'message 1' @event = @appender.event end def test_data assert_equal 'message 1', @event.data end def test_data_eq @event.data = 'message 2' assert_equal 'message 2', @event.data end def test_file assert_equal '', @event.file @logger.caller_tracing = true @logger.warn 'warning message' assert_match %r/test_log_event.rb\z/, @appender.event.file end def test_level assert_equal 1, @event.level end def test_level_eq @event.level = 3 assert_equal 3, @event.level end def test_line assert_equal '', @event.file @logger.caller_tracing = true @logger.error 'error message' assert_equal __LINE__-1, @appender.event.line end def test_logger assert_equal 'TestLogger', @event.logger end def test_logger_eq @event.logger = 'MyLogger' assert_equal 'MyLogger', @event.logger end def test_method assert_equal '', @event.file @logger.caller_tracing = true @logger.debug 'debug message' assert_equal 'test_method', @appender.event.method end end # class TestLogEvent class EventAppender < ::Logging::Appender attr_reader :event def append( event ) @event = event end end end # module TestLogging logging-2.0.0/test/test_logger.rb0000644000175000017500000005336312551223063016342 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestLogger < Test::Unit::TestCase include LoggingTestCase def test_initialize assert_nothing_raised {::Logging::Logger[:test]} assert_equal ::Logging::Logger[:test], ::Logging::Logger['test'] assert_nothing_raised {::Logging::Logger.new(Object)} end def test_add root = ::Logging::Logger[:root] root.level = 'info' a1 = ::Logging::Appenders::StringIo.new 'a1' a2 = ::Logging::Appenders::StringIo.new 'a2' log = ::Logging::Logger.new 'A Logger' root.add_appenders a1 assert_nil a1.readline assert_nil a2.readline log.add(0, 'this should NOT be logged') assert_nil a1.readline assert_nil a2.readline log.add(1, 'this should be logged') assert_equal " INFO A Logger : this should be logged\n", a1.readline assert_nil a1.readline assert_nil a2.readline log.add(2,[1,2,3,4]) assert_equal " WARN A Logger : #{[1,2,3,4]}\n", a1.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a2 log.add(3, 'an error has occurred') assert_equal "ERROR A Logger : an error has occurred\n", a1.readline assert_equal "ERROR A Logger : an error has occurred\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.additive = false log.add(3, 'another error has occurred') assert_equal "ERROR A Logger : another error has occurred\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a1 log.add(4, 'fatal exception') assert_equal "FATAL A Logger : fatal exception\n", a1.readline assert_equal "FATAL A Logger : fatal exception\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.level = :warn log.add(2) do str = 'a string of data' str end assert_equal " WARN A Logger : a string of data\n", a1.readline assert_equal " WARN A Logger : a string of data\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.add(1) do rb_raise(RuntimeError, "this block should not be executed") end assert_nil a1.readline assert_nil a2.readline end def test_add_appenders log = ::Logging::Logger.new 'A' appenders = lambda {log.instance_variable_get :@appenders} assert_equal [], appenders[] assert_raise(ArgumentError) {log.add_appenders Object.new} assert_raise(ArgumentError) {log.add_appenders 'not an appender'} a = ::Logging::Appender.new 'test_appender_1' b = ::Logging::Appender.new 'test_appender_2' c = ::Logging::Appender.new 'test_appender_3' log.add_appenders a assert_equal [a], appenders[] log.add_appenders a assert_equal [a], appenders[] log.add_appenders b assert_equal [a,b], appenders[] log.add_appenders c assert_equal [a,b,c], appenders[] log.add_appenders a, c assert_equal [a,b,c], appenders[] log.clear_appenders assert_equal [], appenders[] log.add_appenders a, c assert_equal [a,c], appenders[] end def test_additive root = ::Logging::Logger.new :root log = ::Logging::Logger.new 'A' assert_raise(NoMethodError) {root.additive} assert_equal true, log.additive end def test_additive_eq root = ::Logging::Logger.new :root log = ::Logging::Logger.new 'A' assert_raise(NoMethodError) {root.additive = false} assert_equal true, log.additive log.additive = false assert_equal false, log.additive log.additive = true assert_equal true, log.additive log.additive = 'false' assert_equal false, log.additive log.additive = 'true' assert_equal true, log.additive log.additive = nil assert_equal true, log.additive assert_raise(ArgumentError) {log.additive = Object} end def test_appenders_eq log = ::Logging::Logger.new '42' appenders = lambda {log.instance_variable_get :@appenders} assert_equal [], appenders[] assert_raise(ArgumentError) {log.appenders = Object.new} assert_raise(ArgumentError) {log.appenders = 'not an appender'} a = ::Logging::Appender.new 'test_appender_1' b = ::Logging::Appender.new 'test_appender_2' c = ::Logging::Appender.new 'test_appender_3' log.appenders = a, b, c assert_equal [a, b, c], appenders[] log.appenders = b assert_equal [b], appenders[] log.appenders = c, a, b assert_equal [c,a,b], appenders[] log.appenders = nil assert_equal [], appenders[] log.appenders = %w[test_appender_1 test_appender_3] assert_equal [a,c], appenders[] assert_raise(ArgumentError) {log.appenders = 'unknown'} end def test_class_aref root = ::Logging::Logger[:root] assert_same root, ::Logging::Logger[:root] a = [] assert_same ::Logging::Logger['Array'], ::Logging::Logger[Array] assert_same ::Logging::Logger['Array'], ::Logging::Logger[a] assert_not_same ::Logging::Logger['Array'], ::Logging::Logger[:root] assert_not_same ::Logging::Logger['A'], ::Logging::Logger['A::B'] end def test_class_root root = ::Logging::Logger[:root] assert_same root, ::Logging::Logger.root end def test_clear_appenders log = ::Logging::Logger.new 'Elliott' appenders = lambda {log.instance_variable_get :@appenders} assert_equal [], appenders[] a = ::Logging::Appender.new 'test_appender_1' b = ::Logging::Appender.new 'test_appender_2' c = ::Logging::Appender.new 'test_appender_3' log.add_appenders a, b, c assert_equal [a,b,c], appenders[] log.clear_appenders assert_equal [], appenders[] end def test_concat a1 = ::Logging::Appenders::StringIo.new 'a1' a2 = ::Logging::Appenders::StringIo.new 'a2' log = ::Logging::Logger.new 'A' ::Logging::Logger[:root].add_appenders a1 assert_nil a1.readline assert_nil a2.readline log << "this is line one of the log file\n" assert_equal "this is line one of the log file\n", a1.readline assert_nil a1.readline assert_nil a2.readline log << "this is line two of the log file\n" log << "this is line three of the log file\n" assert_equal "this is line two of the log file\n", a1.readline assert_equal "this is line three of the log file\n", a1.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a2 log << "this is line four of the log file\n" assert_equal "this is line four of the log file\n", a1.readline assert_equal "this is line four of the log file\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.additive = false log << "this is line five of the log file\n" assert_equal "this is line five of the log file\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a1 log << "this is line six of the log file\n" assert_equal "this is line six of the log file\n", a1.readline assert_equal "this is line six of the log file\n", a2.readline assert_nil a1.readline assert_nil a2.readline end def test_inspect root = ::Logging::Logger.new :root str = "<#{root.class.name}:0x%x name=\"#{root.name}\">" % root.object_id assert_equal str, root.inspect end def test_level root = ::Logging::Logger.new :root log = ::Logging::Logger.new 'A' assert_equal 0, root.level assert_equal 0, log.level root.level = :warn assert_equal 2, root.level assert_equal 2, log.level log.level = nil assert_equal 2, root.level assert_equal 2, log.level log.level = :error assert_equal 2, root.level assert_equal 3, log.level end def test_level_eq root = ::Logging::Logger.new :root log = ::Logging::Logger.new 'A' logb = ::Logging::Logger.new 'A::B' assert_equal 0, root.level assert_equal 0, log.level assert_equal 0, logb.level assert_equal true, root.debug? assert_equal true, log.debug? assert_equal true, logb.debug? assert_raise(ArgumentError) {root.level = -1} assert_raise(ArgumentError) {root.level = 6} assert_raise(ArgumentError) {root.level = Object} assert_raise(ArgumentError) {root.level = 'bob'} assert_raise(ArgumentError) {root.level = :wtf} root.level = 'INFO' assert_equal 1, root.level assert_equal 1, log.level assert_equal 1, logb.level assert_equal false, root.debug? assert_equal true, root.info? assert_equal false, log.debug? assert_equal true , log.info? assert_equal false, logb.debug? assert_equal true , logb.info? root.level = :warn assert_equal 2, root.level assert_equal 2, log.level assert_equal 2, logb.level assert_equal false, root.info? assert_equal true, root.warn? assert_equal false, log.info? assert_equal true , log.warn? assert_equal false, logb.info? assert_equal true , logb.warn? root.level = 'error' assert_equal 3, root.level assert_equal 3, log.level assert_equal 3, logb.level assert_equal false, root.warn? assert_equal true, root.error? assert_equal false, log.warn? assert_equal true , log.error? assert_equal false, logb.warn? assert_equal true , logb.error? root.level = 4 assert_equal 4, root.level assert_equal 4, log.level assert_equal 4, logb.level assert_equal false, root.error? assert_equal true, root.fatal? assert_equal false, log.error? assert_equal true , log.fatal? assert_equal false, logb.error? assert_equal true , logb.fatal? log.level = nil assert_equal 4, root.level assert_equal 4, log.level assert_equal 4, logb.level assert_equal false, root.error? assert_equal true, root.fatal? assert_equal false, log.error? assert_equal true , log.fatal? assert_equal false, logb.error? assert_equal true , logb.fatal? log.level = :DEBUG assert_equal 4, root.level assert_equal 0, log.level assert_equal 0, logb.level assert_equal false, root.error? assert_equal true, root.fatal? assert_equal true, log.debug? assert_equal true, logb.debug? log.level = :off assert_equal 4, root.level assert_equal 5, log.level assert_equal 5, logb.level assert_equal false, root.error? assert_equal true, root.fatal? assert_equal false, log.fatal? assert_equal false, logb.fatal? root.level = :all assert_equal 0, root.level assert_equal 5, log.level assert_equal 5, logb.level assert_equal true, root.debug? assert_equal false, log.fatal? assert_equal false, logb.fatal? log.level = nil assert_equal 0, root.level assert_equal 0, log.level assert_equal 0, logb.level assert_equal true, root.debug? assert_equal true, log.debug? assert_equal true, logb.debug? logb.level = :warn assert_equal 0, root.level assert_equal 0, log.level assert_equal 2, logb.level assert_equal true, root.debug? assert_equal true, log.debug? assert_equal false, logb.info? assert_equal true, logb.warn? log.level = :info logb.level = nil assert_equal 0, root.level assert_equal 1, log.level assert_equal 1, logb.level assert_equal true, root.debug? assert_equal false, logb.debug? assert_equal true, log.info? assert_equal false, logb.debug? assert_equal true, logb.info? end def test_level_with_filter root = ::Logging::Logger[:root] root.level = 'debug' details_filter = ::Logging::Filters::Level.new :debug error_filter = ::Logging::Filters::Level.new :error a_detail = ::Logging::Appenders::StringIo.new 'detail', :filters => details_filter a_error = ::Logging::Appenders::StringIo.new 'error', :filters => error_filter root.add_appenders a_detail, a_error log = ::Logging::Logger.new 'A Logger' log.debug "debug level" assert_equal "DEBUG A Logger : debug level\n", a_detail.readline assert_nil a_error.readline log.error "error level" assert_nil a_detail.readline assert_equal "ERROR A Logger : error level\n", a_error.readline log.warn "warn level" assert_nil a_detail.readline assert_nil a_error.readline end def test_log root = ::Logging::Logger[:root] root.level = 'info' a1 = ::Logging::Appenders::StringIo.new 'a1' a2 = ::Logging::Appenders::StringIo.new 'a2' log = ::Logging::Logger.new 'A Logger' root.add_appenders a1 assert_nil a1.readline assert_nil a2.readline log.debug 'this should NOT be logged' assert_nil a1.readline assert_nil a2.readline log.info 'this should be logged' assert_equal " INFO A Logger : this should be logged\n", a1.readline assert_nil a1.readline assert_nil a2.readline log.warn [1,2,3,4] assert_equal " WARN A Logger : #{[1,2,3,4]}\n", a1.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a2 log.error 'an error has occurred' assert_equal "ERROR A Logger : an error has occurred\n", a1.readline assert_equal "ERROR A Logger : an error has occurred\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.additive = false log.error 'another error has occurred' assert_equal "ERROR A Logger : another error has occurred\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.add_appenders a1 log.fatal 'fatal exception' assert_equal "FATAL A Logger : fatal exception\n", a1.readline assert_equal "FATAL A Logger : fatal exception\n", a2.readline assert_nil a1.readline assert_nil a2.readline assert_equal false, log.debug assert_equal true, log.info assert_equal " INFO A Logger : nil\n", a1.readline assert_equal " INFO A Logger : nil\n", a2.readline assert_equal true, log.warn assert_equal " WARN A Logger : nil\n", a1.readline assert_equal " WARN A Logger : nil\n", a2.readline assert_equal true, log.error assert_equal "ERROR A Logger : nil\n", a1.readline assert_equal "ERROR A Logger : nil\n", a2.readline assert_equal true, log.fatal assert_equal "FATAL A Logger : nil\n", a1.readline assert_equal "FATAL A Logger : nil\n", a2.readline log.level = :warn assert_equal false, log.debug assert_equal false, log.info assert_equal true, log.warn assert_equal " WARN A Logger : nil\n", a1.readline assert_equal " WARN A Logger : nil\n", a2.readline assert_equal true, log.error assert_equal "ERROR A Logger : nil\n", a1.readline assert_equal "ERROR A Logger : nil\n", a2.readline assert_equal true, log.fatal assert_equal "FATAL A Logger : nil\n", a1.readline assert_equal "FATAL A Logger : nil\n", a2.readline assert_raise(NoMethodError) {log.critical 'this log level does not exist'} log.warn do str = 'a string of data' str end assert_equal " WARN A Logger : a string of data\n", a1.readline assert_equal " WARN A Logger : a string of data\n", a2.readline assert_nil a1.readline assert_nil a2.readline log.info do rb_raise(RuntimeError, "this block should not be executed") end assert_nil a1.readline assert_nil a2.readline end def test_log_eh ::Logging::Logger[:root].level = 'info' log = ::Logging::Logger['A Logger'] assert_equal false, log.debug? assert_equal true, log.info? assert_equal true, log.warn? assert_equal true, log.error? assert_equal true, log.fatal? log.level = :warn assert_equal false, log.debug? assert_equal false, log.info? assert_equal true, log.warn? assert_equal true, log.error? assert_equal true, log.fatal? assert_raise(NoMethodError) do log.critical? 'this log level does not exist' end end def test_name root = ::Logging::Logger.new :root log = ::Logging::Logger.new 'A' assert_equal 'root', root.name assert_equal 'A', log.name end def test_parent logger = ::Logging::Logger root = logger.new :root assert_raise(NoMethodError) {root.parent} assert_same root, logger['A'].parent assert_same logger['A'], logger['A::B'].parent assert_same logger['A::B'], logger['A::B::C::D'].parent assert_same logger['A::B'], logger['A::B::C::E'].parent assert_same logger['A::B'], logger['A::B::C::F'].parent assert_same logger['A::B'], logger['A::B::C'].parent assert_same logger['A::B::C'], logger['A::B::C::D'].parent assert_same logger['A::B::C'], logger['A::B::C::E'].parent assert_same logger['A::B::C'], logger['A::B::C::F'].parent assert_same logger['A::B::C::E'], logger['A::B::C::E::G'].parent end def test_remove_appenders log = ::Logging::Logger['X'] appenders = lambda {log.instance_variable_get :@appenders} assert_equal [], appenders[] a = ::Logging::Appender.new 'test_appender_1' b = ::Logging::Appender.new 'test_appender_2' c = ::Logging::Appender.new 'test_appender_3' log.add_appenders a, b, c assert_equal [a,b,c], appenders[] assert_raise(ArgumentError) {log.remove_appenders Object.new} assert_raise(ArgumentError) {log.remove_appenders 10} log.remove_appenders b assert_equal [a,c], appenders[] log.remove_appenders 'test_appender_1' assert_equal [c], appenders[] log.remove_appenders c assert_equal [], appenders[] log.remove_appenders a, b, c assert_equal [], appenders[] log.add_appenders a, b, c assert_equal [a,b,c], appenders[] log.remove_appenders a, c assert_equal [b], appenders[] end def test_spaceship logs = %w( A A::B A::B::C A::B::C::D A::B::C::E A::B::C::E::G A::B::C::F ).map {|x| ::Logging::Logger[x]} logs.unshift ::Logging::Logger[:root] logs.inject do |a,b| assert_equal(-1, a <=> b, "'#{a.name}' <=> '#{b.name}'") b end assert_equal 1, logs[1] <=> ::Logging::Logger[:root] assert_raise(ArgumentError) {logs[1] <=> Object.new} assert_raise(ArgumentError) {::Logging::Logger[:root] <=> 'string'} end def test_caller_tracing log = ::Logging::Logger[:root] assert_equal false, log.caller_tracing log.caller_tracing = true assert_equal true, log.caller_tracing log = ::Logging::Logger['A'] assert_equal false, log.caller_tracing log.caller_tracing = true assert_equal true, log.caller_tracing end def test_caller_trace_eq log = ::Logging::Logger.new 'A' assert_equal false, log.caller_tracing log.caller_tracing = true assert_equal true, log.caller_tracing log.caller_tracing = false assert_equal false, log.caller_tracing log.caller_tracing = 'true' assert_equal true, log.caller_tracing log.caller_tracing = 'false' assert_equal false, log.caller_tracing log.caller_tracing = nil assert_equal false, log.caller_tracing assert_raise(ArgumentError) {log.caller_tracing = Object} end def test_dump_configuration log_a = ::Logging::Logger['A-logger'] log_b = ::Logging::Logger['A-logger::B-logger'] log_c = ::Logging::Logger['A-logger::B-logger::C-logger'] log_d = ::Logging::Logger['A-logger::D-logger'] assert_equal \ "A-logger ........................................ debug +A -T\n", log_a._dump_configuration assert_equal \ "A-logger::B-logger .............................. debug +A -T\n", log_b._dump_configuration assert_equal \ "A-logger::B-logger::C-logger .................... debug +A -T\n", log_c._dump_configuration assert_equal \ "A-logger::D-logger .............................. debug +A -T\n", log_d._dump_configuration log_b.level = :warn log_b.caller_tracing = true assert_equal \ "A-logger::B-logger .............................. *warn +A +T\n", log_b._dump_configuration log_c.additive = false assert_equal \ "A-logger::B-logger::C-logger .................... warn -A -T\n", log_c._dump_configuration # with an indent specified assert_equal \ " A-logger .................................... debug +A -T\n", log_a._dump_configuration(4) assert_equal \ " A-logger::B-logger ...................... *warn +A +T\n", log_b._dump_configuration(8) assert_equal \ " A-logger::B-logger::C-logger .......... warn -A -T\n", log_c._dump_configuration(10) assert_equal \ " A-logger::D-logger ........ debug +A -T\n", log_d._dump_configuration(22) log_c.level = 0 assert_equal \ " A-logger::B...::C-logger *debug -A -T\n", log_c._dump_configuration(26) end end # class TestLogger end # module TestLogging logging-2.0.0/test/test_proxy.rb0000644000175000017500000000352112551223063016233 0ustar globusglobus require File.expand_path('../setup', __FILE__) module TestLogging class TestProxy < Test::Unit::TestCase include LoggingTestCase def setup super ::Logging.init @appender = Logging.appenders.string_io('test_appender') logger = Logging.logger[Array] logger.level = :debug logger.appenders = @appender end def test_initialize ary = [] proxy = Logging::Proxy.new ary assert_instance_of Array, proxy proxy.concat [1,2,3] assert_equal 3, proxy.length assert_equal [1,2,3], ary end def test_method_logging proxy = Logging::Proxy.new [] assert_equal 0, proxy.length assert_equal "Array#length()\n", @appender.readline proxy.concat [1,2,3] assert_equal "Array#concat(#{[1,2,3].inspect})\n", @appender.readline proxy = Logging::Proxy.new Array proxy.name assert_equal "Array.name()\n", @appender.readline proxy.new 0 assert_equal "Array.new(0)\n", @appender.readline end def test_custom_method_logging proxy = Logging::Proxy.new([]) { |name, *args, &block| @logger << "#@leader#{name}(#{args.inspect[1..-2]})" rv = @object.__send__(name, *args, &block) @logger << " => #{rv.inspect}\n" rv } @appender.clear assert_equal 0, proxy.length assert_equal "Array#length() => 0\n", @appender.readline proxy.concat [1,2,3] assert_equal "Array#concat(#{[1,2,3].inspect}) => #{[1,2,3].inspect}\n", @appender.readline proxy.concat [4,5,6] assert_equal "Array#concat(#{[4,5,6].inspect}) => #{[1,2,3,4,5,6].inspect}\n", @appender.readline end def test_error_when_proxying_nil assert_raises(ArgumentError, 'Cannot proxy nil') { Logging::Proxy.new nil } end end # TestProxy end # TestLogging logging-2.0.0/test/setup.rb0000644000175000017500000000136212551223063015154 0ustar globusglobus # Equivalent to a header guard in C/C++ # Used to prevent the class/module from being loaded more than once unless defined? LOGGING_TEST_SETUP LOGGING_TEST_SETUP = true require 'rubygems' require 'test/unit' begin require 'turn' rescue LoadError; end # This line is needed for Ruby 1.9 -- hashes throw a "KeyError" in 1.9 # whereas they throw an "IndexError" in 1.8 # KeyError = IndexError if not defined? KeyError require File.join(File.dirname(__FILE__), %w[.. lib logging]) module TestLogging module LoggingTestCase TMP = 'tmp' def setup super Logging.reset FileUtils.rm_rf TMP FileUtils.mkdir TMP end def teardown super FileUtils.rm_rf TMP end end # LoggingTestCase end # TestLogging end # defined? logging-2.0.0/test/test_utils.rb0000644000175000017500000000343112551223063016212 0ustar globusglobus require File.expand_path('setup', File.dirname(__FILE__)) module TestLogging class TestUtils < Test::Unit::TestCase def test_string_shrink str = 'this is the foobar string' len = str.length r = str.shrink(len + 1) assert_same str, r r = str.shrink(len) assert_same str, r r = str.shrink(len - 1) assert_equal 'this is the...bar string', r r = str.shrink(len - 10) assert_equal 'this i...string', r r = str.shrink(4) assert_equal 't...', r r = str.shrink(3) assert_equal '...', r r = str.shrink(0) assert_equal '...', r assert_raises(ArgumentError) { str.shrink(-1) } r = str.shrink(len - 1, '##') assert_equal 'this is the##obar string', r r = str.shrink(len - 10, '##') assert_equal 'this is##string', r r = str.shrink(4, '##') assert_equal 't##g', r r = str.shrink(3, '##') assert_equal 't##', r r = str.shrink(0, '##') assert_equal '##', r end def test_logger_name assert_equal 'Array', Array.logger_name # some lines are commented out for compatibility with ruby 1.9 c = Class.new(Array) # assert_equal '', c.name assert_equal 'Array', c.logger_name meta = class << Array; self; end # assert_equal '', meta.name assert_equal 'Array', meta.logger_name m = Module.new # assert_equal '', m.name assert_equal 'anonymous', m.logger_name c = Class.new(::Logging::Logger) # assert_equal '', c.name assert_equal 'Logging::Logger', c.logger_name meta = class << ::Logging::Logger; self; end # assert_equal '', meta.name assert_equal 'Logging::Logger', meta.logger_name end end # class TestUtils end # module TestLogging logging-2.0.0/test/test_nested_diagnostic_context.rb0000644000175000017500000000425412551223063022310 0ustar globusglobus require File.expand_path('../setup', __FILE__) module TestLogging class TestNestedDiagnosticContext < Test::Unit::TestCase include LoggingTestCase def test_push_pop ary = Logging.ndc.context assert ary.empty? assert_nil Logging.ndc.peek Logging.ndc.push 'first context' assert_equal 'first context', Logging.ndc.peek Logging.ndc << 'second' Logging.ndc << 'third' assert_equal 'third', Logging.ndc.peek assert_equal 3, ary.length assert_equal 'third', Logging.ndc.pop assert_equal 2, ary.length assert_equal 'second', Logging.ndc.pop assert_equal 1, ary.length assert_equal 'first context', Logging.ndc.pop assert ary.empty? end def test_push_block ary = Logging.ndc.context Logging.ndc.push('first context') do assert_equal 'first context', Logging.ndc.peek end assert ary.empty? Logging.ndc.push('first context') do assert_raise(ZeroDivisionError) do Logging.ndc.push('first context') { 1/0 } end end assert ary.empty? end def test_clear ary = Logging.ndc.context assert ary.empty? Logging.ndc << 'a' << 'b' << 'c' << 'd' assert_equal 'd', Logging.ndc.peek assert_equal 4, ary.length Logging.ndc.clear assert_nil Logging.ndc.peek end def test_thread_uniqueness Logging.ndc << 'first' << 'second' t = Thread.new { sleep Logging.ndc.clear assert_nil Logging.ndc.peek Logging.ndc << 42 assert_equal 42, Logging.ndc.peek } Thread.pass until t.status == 'sleep' t.run t.join assert_equal 'second', Logging.ndc.peek end def test_thread_inheritance Logging.ndc << 'first' << 'second' t = Thread.new(Logging.ndc.context) { |ary| sleep assert_not_equal ary.object_id, Logging.ndc.context.object_id assert_equal %w[first second], Logging.ndc.context } Thread.pass until t.status == 'sleep' Logging.ndc << 'third' t.run t.join end end # class TestNestedDiagnosticContext end # module TestLogging logging-2.0.0/test/test_color_scheme.rb0000644000175000017500000000207612551223063017520 0ustar globusglobus require File.expand_path('../setup', __FILE__) module TestLogging class TestColorScheme < Test::Unit::TestCase include LoggingTestCase def setup super ::Logging.init end def test_default_color_scheme scheme = Logging.color_scheme :default assert_instance_of ::Logging::ColorScheme, scheme assert_equal false, scheme.include?(:debug) assert scheme.include?(:info) assert scheme.include?(:warn) assert scheme.include?(:error) assert scheme.include?(:fatal) end def test_lines_levels_exclusivity assert_raise(ArgumentError) { Logging.color_scheme(:error, :lines => {}, :levels => {}) } end def test_colorization scheme = Logging.color_scheme :default assert_equal "no change", scheme.color('no change', :debug) assert_equal "\e[32minfo is green\e[0m", scheme.color('info is green', :info) assert_equal "\e[37m\e[41mfatal has multiple color codes\e[0m", scheme.color('fatal has multiple color codes', :fatal) end end # TestColorScheme end # TestLogging logging-2.0.0/metadata.yml0000644000175000017500000001341612551223063015016 0ustar globusglobus--- !ruby/object:Gem::Specification name: logging version: !ruby/object:Gem::Version version: 2.0.0 platform: ruby authors: - Tim Pease autorequire: bindir: bin cert_chain: [] date: 2015-03-29 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: little-plugger requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.1' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.1' - !ruby/object:Gem::Dependency name: multi_json requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.10' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.10' - !ruby/object:Gem::Dependency name: flexmock requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' - !ruby/object:Gem::Dependency name: bones-git requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: bones requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 3.8.3 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 3.8.3 description: |- Logging is a flexible logging library for use in Ruby programs based on the design of Java's log4j library. It features a hierarchical logging system, custom level names, multiple output destinations per log event, custom formatting, and more. email: tim.pease@gmail.com executables: [] extensions: [] extra_rdoc_files: - History.txt files: - ".gitignore" - ".travis.yml" - History.txt - README.md - Rakefile - examples/appenders.rb - examples/classes.rb - examples/colorization.rb - examples/custom_log_levels.rb - examples/fork.rb - examples/formatting.rb - examples/hierarchies.rb - examples/layouts.rb - examples/lazy.rb - examples/loggers.rb - examples/mdc.rb - examples/names.rb - examples/rspec_integration.rb - examples/simple.rb - lib/logging.rb - lib/logging/appender.rb - lib/logging/appenders.rb - lib/logging/appenders/buffering.rb - lib/logging/appenders/console.rb - lib/logging/appenders/file.rb - lib/logging/appenders/io.rb - lib/logging/appenders/rolling_file.rb - lib/logging/appenders/string_io.rb - lib/logging/appenders/syslog.rb - lib/logging/color_scheme.rb - lib/logging/diagnostic_context.rb - lib/logging/filter.rb - lib/logging/filters.rb - lib/logging/filters/level.rb - lib/logging/layout.rb - lib/logging/layouts.rb - lib/logging/layouts/basic.rb - lib/logging/layouts/parseable.rb - lib/logging/layouts/pattern.rb - lib/logging/log_event.rb - lib/logging/logger.rb - lib/logging/proxy.rb - lib/logging/rails_compat.rb - lib/logging/repository.rb - lib/logging/root_logger.rb - lib/logging/utils.rb - lib/logging/version.rb - lib/rspec/logging_helper.rb - lib/spec/logging_helper.rb - logging.gemspec - script/bootstrap - test/appenders/test_buffered_io.rb - test/appenders/test_console.rb - test/appenders/test_file.rb - test/appenders/test_io.rb - test/appenders/test_periodic_flushing.rb - test/appenders/test_rolling_file.rb - test/appenders/test_string_io.rb - test/appenders/test_syslog.rb - test/benchmark.rb - test/layouts/test_basic.rb - test/layouts/test_color_pattern.rb - test/layouts/test_json.rb - test/layouts/test_pattern.rb - test/layouts/test_yaml.rb - test/setup.rb - test/test_appender.rb - test/test_color_scheme.rb - test/test_filter.rb - test/test_layout.rb - test/test_log_event.rb - test/test_logger.rb - test/test_logging.rb - test/test_mapped_diagnostic_context.rb - test/test_nested_diagnostic_context.rb - test/test_proxy.rb - test/test_repository.rb - test/test_root_logger.rb - test/test_utils.rb homepage: http://rubygems.org/gems/logging licenses: [] metadata: {} post_install_message: rdoc_options: - "--main" - README.md require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: logging rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: A flexible and extendable logging library for Ruby test_files: - test/appenders/test_buffered_io.rb - test/appenders/test_console.rb - test/appenders/test_file.rb - test/appenders/test_io.rb - test/appenders/test_periodic_flushing.rb - test/appenders/test_rolling_file.rb - test/appenders/test_string_io.rb - test/appenders/test_syslog.rb - test/layouts/test_basic.rb - test/layouts/test_color_pattern.rb - test/layouts/test_json.rb - test/layouts/test_pattern.rb - test/layouts/test_yaml.rb - test/test_appender.rb - test/test_color_scheme.rb - test/test_filter.rb - test/test_layout.rb - test/test_log_event.rb - test/test_logger.rb - test/test_logging.rb - test/test_mapped_diagnostic_context.rb - test/test_nested_diagnostic_context.rb - test/test_proxy.rb - test/test_repository.rb - test/test_root_logger.rb - test/test_utils.rb logging-2.0.0/.travis.yml0000644000175000017500000000024312551223063014616 0ustar globusglobuslanguage: ruby before_install: "gem install bones" install: "rake gem:install_dependencies" script: "rake" rvm: - 1.9.3 - 2.0.0 - 2.1.5 - 2.2.1