How can I define my own levels in log4r without a conflict with other log4r-loggers?

570 views Asked by At

I can define my own log-levels with log4r:

require 'log4r'
require 'log4r/configurator'

# This is how we specify our levels
Log4r::Configurator.custom_levels "Foo", "Bar", "Baz"

log = Log4r::Logger.new('custom levels')
p log.levels  #-> ["ALL", "Foo", "Bar", "Baz", "OFF"]
log.add Log4r::StdoutOutputter.new('console')

puts log.foo? #-> true
log.foo "This is foo 1"

log.level = Log4r::Bar
puts log.foo? #->false
log.foo "This is foo 2"

The log4r-documentation says:

Also, custom levels should be set before anything else is done with Log4r, otherwise the default levels will be loaded.

This restriction is the source of two problems:

  • Setting new levels with Log4r::Configurator.custom_levels works only, if there is no logger defined.
  • If I create another logger after defining my own levels, I get again the custom levels.

Problem 1:

Example:

require 'log4r'
require 'log4r/configurator'

#Create a dummy logger
Log4r::Logger.new('dummy')

# Define new levels -> does not work after creation of another logger.
Log4r::Configurator.custom_levels "Foo", "Bar", "Baz"
log = Log4r::Logger.new('custom levels')
p log.levels  #-> ["ALL", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF"]

The custom levels are not used.

Problem 2

If I define my own levels, I have no possibility to go back to the defaults.

Example:

require 'log4r'
require 'log4r/configurator'

# Define customer levels
Log4r::Configurator.custom_levels "Foo", "Bar", "Baz"
Log4r::Logger.new('dummy with custom levels')

#Reset to standard levels ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"] <- this does not work
Log4r::Configurator.custom_levels( *Log4r::Log4rConfig::LogLevels )
log2 = Log4r::Logger.new('log')
p log2.levels #-> ["ALL", "Foo", "Bar", "Baz", "OFF"], but I wanted ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"]

Background:

I want to create a logger with my own levels similar to Apache: DEBUG INFO NOTICE WARN ERROR CRIT ALERT EMERG.

When I do so, I get two problems:

  • If another application uses also log4r, that application has no fatal-level. This can be solved, if I add a fatal-level in my application.
  • If another application is loaded before my application and creates a logger, my application will fail, because my customer levels are not available.

My question:

How can I create a logger with customized log levels and without side effects to other loggers?

1

There are 1 answers

0
bestmike007 On BEST ANSWER

Custom levels are process-wide and you cannot reset it after it's already set. But there might be some way to achieve what you want.

Problem 1

Previous custom levels must be set for a reason, and logger methods and outputter methods are created during setting up the custom levels, thus you should be careful if you're going to remove some of the custom levels which might cause unexpected errors.

However, there is a workaround to reset the custom levels from my fork of Log4r and it's for test purpose. (This method resets log4r to the original state, wipes out all existing loggers and outputters)

Problem 2

There are two solutions to resolve the conflicts:

  • Define all custom levels before other modules do. And you must include all custom levels. e.g. ['Foo', 'Bar', 'Bing', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL']
  • If you cannot do that before other modules do, use the method above to reset log4r and redefine custom levels and also load configurations from other modules if needed
  • Use alias methods as follow and change your log4r configuration correspondingly (e.g. replace foo with debug, etc.)
Log4r::Logger.module_eval %{
  alias_method :foo, :debug
  alias_method :bar, :info
  alias_method :baz, :error
  alias_method :qux, :fatal
}

Hope it helps.