# Authors: John Dennis <jdennis@redhat.com>
#
# Copyright (C) 2011  Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

'''

Quick Start Guide For Using This Module
=======================================

This module implements a Log Manager class which wraps the Python
logging module and provides some utility functions for use with
logging. All logging operations should be done through the
`LogManager` where available. *DO NOT create objects using the
Python logging module, the log manager will be unaware of them.*

This module was designed for ease of use while preserving advanced
functionality and performance. You must perform the following steps.

1. Import the log_manger module and instantiate *one* `LogManager`
   instance for your application or library. The `LogManager` is
   configured via `LogManager.configure()` whose values are
   easily populated from command line options or a config file. You
   can modify the configuration again at any point.

2. Create one or more output handlers via
   `LogManager.create_log_handlers()` an easy to use yet powerful
   interface.

3. In your code create loggers via `LogManager.get_logger()`. Since
   loggers are normally bound to a class this method is optimized for
   that case, all you need to do in the call ``__init__()`` is::

     log_mgr.get_logger(self, True)

   Then emitting messages is as simple as ``self.debug()`` or ``self.error()``

Example:
--------

::

  # Step 1, Create log manager and configure it
  prog_name = 'my_app'
  log_mgr = LogManager(prog_name)
  log_mgr.configure(dict(verbose=True))

  # Step 2, Create handlers
  log_mgr.create_log_handlers([dict(name='my_app stdout',
                                    stream=sys.stdout,
                                    level=logging.INFO),
                               dict(name='my_app file',
                                    filename='my_app.log',
                                    level=logging.DEBUG)])

  # Step 3, Create and use a logger in your code
  class FooBar:
      def __init__(self, name):
          log_mgr.get_logger(self, True)
          self.info("I'm alive! %s", name)

  foobar = FooBar('Dr. Frankenstein')

  # Dump the log manager state for illustration
  print
  print log_mgr


Running the above code would produce::

  <INFO>: I'm alive! Dr. Frankenstein

  root_logger_name: my_app
  configure_state: None
  default_level: INFO
  debug: False
  verbose: True
  number of loggers: 2
      "my_app" [level=INFO]
      "my_app.__main__.FooBar" [level=INFO]
  number of handlers: 2
      "my_app file" [level=DEBUG]
      "my_app stdout" [level=INFO]
  number of logger regexps: 0

*Note, Steps 1 & 2 were broken out for expository purposes.* You can
pass your handler configuration into `LogManager.configure()`. The above
could have been simpler and more compact.::

  # Step 1 & 2, Create log manager, and configure it and handlers
  prog_name = 'my_app'
  log_mgr = LogManager(prog_name)
  log_mgr.configure(dict(verbose=True,
                         handlers = [dict(name='my_app stdout',
                                          stream=sys.stdout,
                                          level=logging.INFO),
                                     dict(name='my_app file',
                                          filename='my_app.log',
                                          level=logging.DEBUG)])


FAQ (Frequently Asked Questions)
================================

#. **Why is this better than logging.basicConfig? The short example
   for the LogManager doesn't seem much different in complexity from
   basicConfig?**

   * You get independent logging namespaces. You can instantiate
     multiple logging namespaces. If you use this module you'll be
     isolated from other users of the Python logging module avoiding
     conflicts.

   * Creating and initializing loggers for classes is trivial. One
     simple call creates the logger, configures it, and sets logging
     methods on the class instance.

   * You can easily configure individual loggers to different
     levels. For example turn on debuging for just the part of the
     code you're working on.

   * The configuration is both simple and powerful. You get many more
     options than with basicConfig.

   * You can dynamically reset the logging configuration during
     execution, you're not forced to live with the config established
     during program initialization.

   * The manager optimizes the use of the logging objects, you'll
     spend less time executing pointless logging code for messages
     that won't be emitted.

   * You can see the state of all the logging objects in your
     namespace from one centrally managed location.

   * You can configure a LogManager to use the standard logging root
     logger and get all the benefits of this API.

#. **How do I turn on debug logging for a specific class without
   affecting the rest of the logging configuration?**

   Use a logger regular expression to bind a custom level to loggers
   whose name matches the regexp. See `LogManager.configure()`
   for details.

   Lets say you want to set your Foo.Bar class to debug, then do
   this::

     log_mgr.configure(dict(logger_regexps=[(r'Foo\.Bar', 'debug')]))

#. **I set the default_level but all my loggers are configured
   with a higher level, what happened?**

   You probably don't have any handlers defined at or below the
   default_level. The level set on a logger will never be
   lower than the lowest level handler available to that logger.

#. **My logger's all have their level set to a huge integer, why?**

   See above. Logger's will never have a level less than the level of
   the handlers visible to the logger. If there are no handlers then
   loggers can't output anything so their level is set to maxsize.

#. **I set the default_level but all the loggers are configured
   at INFO or DEBUG, what happened?**

   The verbose and debug config flags set the default_level to
   INFO and DEBUG respectively as a convenience.

#. **I'm not seeing messages output when I expect them to be, what's
   wrong?**

   For a message to be emitted the following 3 conditions must hold:

   * Message level >= logger's level
   * Message level >= handler's level
   * The message was not elided by a filter

   To verify the above conditions hold print out the log manager state
   (e.g. print log_mgr). Locate your logger, what level is at? Locate
   the handler you expected to see the message appear on, what level
   is it?

A General Discussion of Python Logging

======================================

The design of this module is driven by how the Python logging module
works. The following discussion complements the Python Logging Howto,
fills in some missing information and covers strategies for
implementing different functionality along with 
the trade-offs
involved.

Understanding when & how log messages are emitted:
--------------------------------------------------

Loggers provide the application interface for logging. Every logger
object has the following methods debug(), info(), warning(), error(),
critical(), exception() and log() all of which can accept a format
string and arguments. Applications generate logging messages by
calling one of these methods to produce a formatted message.

A logger's effective level is the first explicitly set level found
when searching from the logger through it's ancestors terminating at
the root logger. The root logger always has an explicit level
(defaults to WARNIN