#  _________________________________________________________________________
#
#  PyUtilib: A Python utility library.
#  Copyright (c) 2008 Sandia Corporation.
#  This software is distributed under the BSD License.
#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
#  the U.S. Government retains certain rights in this software.
#  _________________________________________________________________________

"""A plugin that supports global configuration of logging options for pyutilib.component.core."""

import os.path
import logging
import logging.handlers as handlers

from pyutilib.component.config.env_config import *
from pyutilib.component.config.options import *


class LoggingConfig(Plugin):
    """A plugin that supports global configuration of logging options."""

    implements(IUpdatedOptionsAction)


    def __init__(self, namespace):
        """Initialize logging information for a specified namespace"""
        self._hdlr=None
        self.namespace=namespace
        self.env_plugins = ExtensionPoint(IEnvironmentConfig)
        if self.namespace == "":
            section = "logging"
            section_re = None
        else:
            section = "logging."+namespace
            section_re = "^logging$"
        #
        declare_option("timestamp", section=section, section_re=section_re,
            default=False,
            doc="""Add timestamp to logging information.""")
        #
        declare_option("log_dir", section=section, section_re=section_re,
            default=None,
            doc="""The logging directory.

        The default directory is the application directory plus 'log'.""")
        #
        declare_option("log_type", section=section, section_re=section_re,
            default='none',
            doc="""Logging facility to use.

        Should be one of (`none`, `file`, `stderr`, `syslog`, `winlog`).""")
        #
        declare_option("log_file", section=section, section_re=section_re,
            default=namespace+'.log',
            doc="""If `log_type` is `file`, this should be a path to the log-file.""")
        #
        declare_option("log_level", section=section, section_re=section_re,
            default='WARN',
            doc="""Level of verbosity in log.

        Should be one of (`CRITICAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`).""")
        #
        declare_option("log_format", section=section, section_re=section_re,
            default=None,
            doc="""Custom logging format.

        If nothing is set, the following will be used:

        $(project)[$(env) $(module)] $(levelname): $(message)

        In addition to regular key names supported by the Python logger library
        library (see http://docs.python.org/lib/node422.html), one could use:
         - $(path)s     the path for the current environment
         - $(basename)s the last path component of the current environment
         - $(app)s      the name of the current application

        Note the usage of `$(...)s` instead of `%(...)s` as the latter form
        would be interpreted by the ConfigParser itself.
        """)

    def reset_after_updates(self):
        """
        Configure the pyutilib.component logging facility.  This will
        implicitly configure all of the environment-specific logging
        objects.
        """
        sys.stdout.flush()
        logger = logging.getLogger('pyutilib.component.core.'+self.namespace)
        if not self._hdlr is None:
            logger.removeHandler(self._hdlr)
        #
        # Set logging level
        #
        level = self.log_level
        level = level.upper()
        if level in ('DEBUG', 'ALL'):
            logger.setLevel(logging.DEBUG)
        elif level == 'INFO':
            logger.setLevel(logging.INFO)
        elif level == 'ERROR':
            logger.setLevel(logging.ERROR)
        elif level == 'CRITICAL':
            logger.setLevel(logging.CRITICAL)
        else:
            logger.setLevel(logging.WARNING)
        #
        # Initialize the current path.  Is there a rule to use for whic
        # environment will be used???  In practice, there is likely to be
        # only one environment.
        #
        if self.log_dir is None:
            path = None
            for plugin in self.env_plugins:
                (flag,count) = plugin.matches(self.namespace)
                tmp = plugin.get_option("path")
                if flag and not tmp is None:
                    path = tmp
                    break
            if path is None:
                path = os.getcwd()
        else:
            path = self.log_dir
        #
        # Setup the logging file
        #
        logtype = self.log_type.lower()
        if self.log_file is None:
            logfile = os.path.join(path, 'log')
        else:
            logfile = self.log_file
            if not os.path.isabs(logfile):
                logfile = os.path.join(path, logfile)
        #
        # Define the format
        #
        format = self.log_format
        if format is None:
            format = '[env=%(env)s where=%(module)s] %(levelname)s - %(message)s'
            if self.timestamp and logtype in ('file', 'stderr'):
                format = '%(asctime)s ' + format
            format = format.replace('$(', '%(') \
                    .replace('%(env)s', PluginGlobals.get_env().name)
        datefmt = ''
        if self.timestamp and self.log_type == 'stderr':
            datefmt = '%X'
        formatter = logging.Formatter(format, datefmt)
        #
        #  Define the handler
        #
        if logtype == 'file':
            hdlr = logging.FileHandler(logfile)
        elif logtype in ('winlog', 'eventlog', 'nteventlog'):
            # Requires win32 extensions
            hdlr = handlers.NTEventLogHandler(logid,
                                                  logtype='Application')
        elif logtype in ('syslog', 'unix'):
            hdlr = handlers.SysLogHandler('/dev/log')
        elif logtype in ('stderr'):
            hdlr = logging.StreamHandler(sys.stderr)
        else:
            hdlr = handlers.BufferingHandler(0)
            # Note: this _really_ throws away log events, as a `MemoryHandler`
            # would keep _all_ records in case there's no target handler (a bug?)
        self._hdlr = hdlr
        self._logtype = logtype
        self._logfile = logfile
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)
        self._log = logger


    def flush(self):
        """Flush logging I/O"""
        self._hdlr.flush()

    def shutdown(self):
        #
        # Q: should this shutdown _all_ logging?
        #
        logging.shutdown()

    def log(self,message):
        self._log.info(message)
