logging-2.0.0/ 0000755 0001750 0001750 00000000000 12551223063 012506 5 ustar globus globus logging-2.0.0/lib/ 0000755 0001750 0001750 00000000000 12551223063 013254 5 ustar globus globus logging-2.0.0/lib/logging.rb 0000644 0001750 0001750 00000041051 12551223063 015230 0 ustar globus globus require 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/ 0000755 0001750 0001750 00000000000 12551223063 014206 5 ustar globus globus logging-2.0.0/lib/spec/logging_helper.rb 0000644 0001750 0001750 00000000346 12551223063 017523 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 014370 5 ustar globus globus logging-2.0.0/lib/rspec/logging_helper.rb 0000644 0001750 0001750 00000001700 12551223063 017700 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 014702 5 ustar globus globus logging-2.0.0/lib/logging/filters.rb 0000644 0001750 0001750 00000000124 12551223063 016674 0 ustar globus globus module Logging
module Filters ; end
require libpath('logging/filters/level')
end logging-2.0.0/lib/logging/appenders.rb 0000644 0001750 0001750 00000002725 12551223063 017216 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000002520 12551223063 017210 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000034771 12551223063 016522 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000022407 12551223063 017032 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 016402 5 ustar globus globus logging-2.0.0/lib/logging/layouts/pattern.rb 0000644 0001750 0001750 00000052263 12551223063 020414 0 ustar globus globus module 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.rb 0000644 0001750 0001750 00000022165 12551223063 020673 0 ustar globus globus require '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.rb 0000644 0001750 0001750 00000002232 12551223063 020007 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000001673 12551223063 017713 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000010762 12551223063 016375 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000004171 12551223063 016413 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000001212 12551223063 016510 0 ustar globus globus module 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.rb 0000644 0001750 0001750 00000000276 12551223063 016734 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 016663 5 ustar globus globus logging-2.0.0/lib/logging/appenders/io.rb 0000644 0001750 0001750 00000004616 12551223063 017626 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000004022 12551223063 021203 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000032124 12551223063 021161 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000033051 12551223063 021657 0 ustar globus globus module 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.rb 0000644 0001750 0001750 00000005176 12551223063 020140 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000004505 12551223063 020656 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000015172 12551223063 020536 0 ustar globus globus
# 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.rb 0000644 0001750 0001750 00000035114 12551223063 021123 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000015144 12551223063 017453 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000007002 12551223063 016543 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000003031 12551223063 017546 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000000204 12551223063 016710 0 ustar globus globus module 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/ 0000755 0001750 0001750 00000000000 12551223063 016352 5 ustar globus globus logging-2.0.0/lib/logging/filters/level.rb 0000644 0001750 0001750 00000001426 12551223063 020011 0 ustar globus globus require '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.rb 0000644 0001750 0001750 00000025370 12551223063 017700 0 ustar globus globus # 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.txt 0000644 0001750 0001750 00000022765 12551223063 014724 0 ustar globus globus == 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/.gitignore 0000644 0001750 0001750 00000000456 12551223063 014503 0 ustar globus globus # 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/Rakefile 0000644 0001750 0001750 00000001572 12551223063 014160 0 ustar globus globus begin
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/ 0000755 0001750 0001750 00000000000 12551223063 014012 5 ustar globus globus logging-2.0.0/script/bootstrap 0000755 0001750 0001750 00000000337 12551223063 015760 0 ustar globus globus #!/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/ 0000755 0001750 0001750 00000000000 12551223063 014324 5 ustar globus globus logging-2.0.0/examples/appenders.rb 0000644 0001750 0001750 00000002475 12551223063 016642 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002776 12551223063 017037 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002135 12551223063 016307 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000001636 12551223063 016321 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002521 12551223063 015612 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000000740 12551223063 016143 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000005415 12551223063 015421 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000004562 12551223063 017146 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000003076 12551223063 015762 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002425 12551223063 020373 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002272 12551223063 016354 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000003243 12551223063 015632 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000003767 12551223063 017402 0 ustar globus globus # :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.rb 0000644 0001750 0001750 00000002673 12551223063 020406 0 ustar globus globus # :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.gemspec 0000644 0001750 0001750 00000011441 12551223063 015502 0 ustar globus globus # -*- 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.md 0000644 0001750 0001750 00000013362 12551223063 013772 0 ustar globus globus ## Logging
by Tim Pease [](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/ 0000755 0001750 0001750 00000000000 12551223063 013465 5 ustar globus globus logging-2.0.0/test/test_root_logger.rb 0000644 0001750 0001750 00000003425 12551223063 017377 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000006422 12551223063 016372 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 015165 5 ustar globus globus logging-2.0.0/test/layouts/test_yaml.rb 0000644 0001750 0001750 00000012214 12551223063 017513 0 ustar globus globus require '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.rb 0000644 0001750 0001750 00000006511 12551223063 021427 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000013306 12551223063 017525 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000015714 12551223063 020236 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000002134 12551223063 017632 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000006335 12551223063 022276 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000012307 12551223063 016652 0 ustar globus globus
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/ 0000755 0001750 0001750 00000000000 12551223063 015446 5 ustar globus globus logging-2.0.0/test/appenders/test_periodic_flushing.rb 0000644 0001750 0001750 00000010153 12551223063 022527 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000007423 12551223063 017757 0 ustar globus globus # 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.rb 0000644 0001750 0001750 00000003325 12551223063 020477 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000007204 12551223063 017444 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000001335 12551223063 021031 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000014603 12551223063 021307 0 ustar globus globus # 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.rb 0000644 0001750 0001750 00000022236 12551223063 021504 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000011272 12551223063 020355 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000010224 12551223063 017267 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000014366 12551223063 016511 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000004301 12551223063 015742 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000001621 12551223063 016336 0 ustar globus globus require 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.rb 0000644 0001750 0001750 00000003164 12551223063 017037 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000053363 12551223063 016342 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000003521 12551223063 016233 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000001362 12551223063 015154 0 ustar globus globus
# 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.rb 0000644 0001750 0001750 00000003431 12551223063 016212 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000004254 12551223063 022310 0 ustar globus globus
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.rb 0000644 0001750 0001750 00000002076 12551223063 017520 0 ustar globus globus
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.yml 0000644 0001750 0001750 00000013416 12551223063 015016 0 ustar globus globus --- !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.yml 0000644 0001750 0001750 00000000243 12551223063 014616 0 ustar globus globus language: 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