= LogSimple

LogSimple - a Ruby logging library. LogSimple logs messages differently according to their level. For instance an 'error' message will go to syslog, but a 'critical' message will get emailed to the administrator. Log levels, and the behaviour associated with a given level, can be defined dynamically
by the user. Behaviours include 'formatters' (to modify the message) and 'outputters' (to
output the message), and while a certain number of predefined behaviours are provided as part of the
library, users can create new ones.

Copyright (c) 2006 Netuxo

Author  : Netuxo (http://www.netuxo.co.uk) ; contact anselm at netuxo dot co dot uk

Licence : GPL V2 (See LICENSE[link:files/LICENSE.html])

Sponsor : War Resisters' International (http://www.wri-irg.org)

Ruby Forge project page : http://rubyforge.org/projects/logsimple/

Similar projects : Logger[http://ruby-doc.org/stdlib/libdoc/logger/rdoc/classes/Logger.html], 
which is part of the Ruby standard library and Log4r[http://log4r.sourceforge.net/], inspired by the Apache Log4j

= Overview
== Outputters
Log levels are defined dynamically. Each level can be given any number of behaviours to apply when logging at that level. For
instance :

  require 'logsimple.rb'
  include LogSimple

  Log.add_behaviour :debug, :log_stdout
  Log.debug "message"

The line with the call to "add_behaviour" creates the log level "debug" (if it didn't already exist) and associates the behaviour "log_stdout". 
The following line logs the message "message" at the level debug - so it gets logged to stdout.

Further behaviours can be added :

  Log.add_behaviour :debug, :log_syslog
  Log.debug "second message"

Now "second message" will be logged to stdout and then to syslog.

Behaviours can also have parameters associated, for instance :

  Log.add_behaviour :info, :log_file
  Log.info "hello", {:filename => "mylog.txt"}

Will log the message "hello" to the file "mylog.txt". Behaviours can be given default parameters
to avoid specifying them every time :

  Log.add_parameters :log_file, {:filename => "mylog.txt"}
  Log.info "another hello"

Will log "another hello" to the file "mylog.txt". In this case the default parameter applies
to the behaviour ':log_file', so changing the parameter will affect any log level that
uses this behaviour. It is also possible to set a parameter for only a specific log level :

  Log.add_parameters :log_file, {:filename => "mylog.txt"}
  Log.add_level_parameters :info, :log_file, {:filename => "theotherlog.txt"}
  Log.add_behaviour :info,  :log_file
  Log.add_behaviour :debug, :log_file
  Log.info  "info"
  Log.debug "debug"

Here the message "debug" goes to the generic file - mylog.txt - while the "info" message goes
to theotherlog.txt

== Formatters

Some behaviours are used to modifiy the message. For instance :

  Log.add_behaviour :error, :append_time, :log_stdout
  Log.error "error"

The logged message will have the current time appended to it. Behaviours are called in the
order they are defined. So :

 Log.add_behaviour :error, :log_stdout, :append_time, :log_file

Will cause errors to first be logged to stdout (without the time) then to a file, with the time.

= Default behaviours, and default log levels

The following behaviours are defined by default :

- :*null*,       which does not perform any action. The message is still sent to observers.
- :*log_stderr*, to log to stderr
- :*log_stdout*, to log to stdout
- :*log_file*,   to log to a file. Requires the :*file_name* parameter to be set.

- :*log_syslog*, to log to syslog (when available). Will use the optional parameters
  :*name* and :*level*. :name represents the application name (will use $0 if not set) and 
  :level represents the syslog error level. If not given, will log at syslog level 'err'.
  The options are debug, info, notice, warning, err, alert, emerg and crit

- :*log_email*   to send an email with the log (SMTP only). Requires :*smtp_server*, :*smtp_username*,
  :*smtp_password*, :*from* and :*to* (may be an array) parameters to be set. If the optional
  parameter :*subject* is set, then it is used for subject instead of $0 ; if the optional
  parameter :*smtp_port* is set then it is used for port instead of 25 ; if the optional
  parameter :*smtp_helo* is set then it is used for HELO message ; if the optional parameter
  :*smpt_authtype* is set, then it is used to determine the type of authentification (:plain,
  :login or :cram_md5). :plain is used by default.

- :*log_exec*,    to call an executable with the log. Requires :*executable* parameter to be set
- :*raise*,       to raise an exception. Will use the optional :*class_name* parameter to raise the
  execption (otherwise, raises a RuntimeError exception)
- :*exit*,        to terminate execution imediately
- :*append_level* Appends the log level at the begining of the message
- :*append_time*  Appends the date/time at the begining of the message

By default, the following two log levels are created :

- :*warning*,  for recovarable errors. Default behaviour is to append the level and log to stderr
- :*error*,    for unrecovarable errors. Default behaviour is to append the level and log to
  stderr and raise.

= Adding your own behaviours

Any behaviour can be added at run time ; provided there is a class instance function do_#{behaviour} 
that takes the message string and a hash of parameters as parameters :

  def Log.do_puts(message, p)
    puts "Via puts : " + message
  end

  Log.add_behaviour :debug, :puts
  Log.debug "bug"

Will output "Via puts : bug".
Behaviours that modify the message (such as :append_level) are defined by a class instance function
mod_#{behaviour} that takes the output level, the message string and a hash of parameters and
returns the modified string. For instance :

 def Log.mod_append_prog(level, message, p)
   "[" + $0.to_s + "] " + message
 end

Will cause the program name to be appended at the begining of the message.

= Observers

Observers can be added as Proc objects. Those are called with the content of the message
before each message gets called :

 last_log = nil
 Log.add_observer {|m| last_log = m}

= Streams

It is possible to obtain stream objects linked to a particular log level which support the "<<"
operator. This is usefull to pass the logger as a generic stream to external classes. For instance:

  o = Log.get_stream(:error)
  o << "message"

The last line has the same effect as Log.error "message". Note that parameters cannot be passed to
stream objects. An extra string can be provided to a stream that gets appended at the begining
of every message :

 o1 = Log.get_stream(:error, "[Module A] ")
 o2 = Log.get_stream(:error, "[Module B] ")
 o1 << "one"
 o2 << "two"

Will log '[Module A] one' and '[Module B] two' to the error log.