# apache_log_handler.py
# vim: set sw=4 :

from mod_python import apache

try:
    from threading import currentThread
except:
    from dummy_threading import currentThread

import logging

import sys
import new


class ApacheLogHandler(logging.Handler):

    """A log handler integrated with Apache logging interface.

    """

    apache_levels = {
        logging.CRITICAL:    apache.APLOG_CRIT,
        logging.ERROR:       apache.APLOG_ERR,
        logging.WARNING:     apache.APLOG_WARNING,
        logging.INFO:        apache.APLOG_INFO,
        logging.DEBUG:       apache.APLOG_DEBUG,
        logging.NOTSET:      apache.APLOG_INFO
    }

    def __init__(self):
        logging.Handler.__init__(self)
        self.__globals = _globals

    def emit(self,record):

	# The logging level must be rounded down to a
	# multiple of 10 and if it is still greater than
	# highest level, explicitly set to the highest
	# level. The level then needs to be translated
        # into an equivalent Apache log level.

        logging_level = record.levelno - (record.levelno % 10)
        if logging_level > logging.CRITICAL:
            logging_level = logging.CRITICAL

        apache_level = self.apache_levels[logging_level]
        apache_level |= apache.APLOG_NOERRNO

	# Retrieve mod_python request object from the
	# cache. If for some reason there is not request
	# object because it isn't a request handler
	# thread, logging will be done through global
	# logging interface and not the request specific
	# logging interface.

        req = self.__globals.cache.get(currentThread())

        message = self.format(record)

        if req is not None:
            req.log_error(message,apache_level)
        else:
            apache.log_error(message,apache_level)


# We cannot risk the global request cache being lost or
# overriden because of this module being reloaded. To
# cater for possible future changes to the module
# loading system whereby a module will be reloaded into
# a fresh new module and not on top of the old, we need
# to keep our persistent global data in a place outside
# of this module. For this we explicitly create a new
# module and store it in "sys.modules". We also store
# the global instance of the default log there as well
# and only create one the first time the module is
# loaded. This means that Apache has to be restarted to
# change the actual code of the default log handler.

if not sys.modules.has_key("_apache_log_handler_globals"):
    _globals = new.module("_apache_log_handler_globals")
    sys.modules["_apache_log_handler_globals"] = _globals

    # Create the cache.

    _globals.cache = {}

    # Create a default log handler and attach it against
    # "mod_python" log. Users need not use this default.
    # Instead separate instances of the ApacheLogHandler
    # could be created and associated with other logs as
    # appropriate.

    _globals.handler = ApacheLogHandler()
    log = logging.getLogger("mod_python")
    log.addHandler(_globals.handler)

else:
    _globals = sys.modules["_apache_log_handler_globals"]


# The following handler is used to cache the request
# object. In order for the log handler to be able to log
# messages via the request object logging interface
# rather than the global logging interface, this handler
# must be called. Use of the request object logging
# interface is preferred as the log messages can then be
# directed to a virtual host specific log file if Apache
# is configured appropriately.
#
# The best way to trigger this handler is to specify an
# additional handler to be called before any others which
# are already defined. For example:
#
#   AddHandler python-program .py
#   PythonHandler apache_log_handler
#   PythonHandler mod_python.publisher

def handler(req):
    thread = currentThread()

    # Cache the request object. We however need to
    # remember what the previously cached request object
    # may have been and restore it later so that
    # everything will work correctly when an internal
    # redirect occurs.

    prev_req = _globals.cache.get(thread)
    _globals.cache[thread] = req

    def cleanup(data):
        if prev_req is not None:
            _globals.cache[thread] = prev_req
        else:
            del _globals.cache[thread]

    req.register_cleanup(cleanup)

    return apache.OK

