##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Handlers which can plug into a PEP 282 logger."""

import sys
import os

from logging import Handler, StreamHandler
from logging.handlers import SysLogHandler
from logging.handlers import HTTPHandler, SMTPHandler
from logging.handlers import NTEventLogHandler as Win32EventLogHandler
import zLOG

class FileHandler(StreamHandler):
    """
    A file handler which allows for reopening of logs in favor of the
    'rollover' features of the standard PEP282 FileHandler.
    """
    def __init__(self, filename, mode="a"):
        StreamHandler.__init__(self, open(filename, mode))
        self.baseFilename = filename
        self.mode = mode

    def close(self):
        self.stream.close()

    def reopen(self):
        self.close()
        self.stream = open(self.baseFilename, self.mode)

class Win32FileHandler(FileHandler):
    """File-based log handler for Windows that supports an additional 'rotate'
    method.  reopen() is generally useless since Windows cannot do a move on
    an open file.
    """
    def rotate(self, rotateFilename=None):
        if not rotateFilename:
            rotateFilename = self.baseFilename + ".last"
        error = None
        self.close()
        try:
            os.rename(self.baseFilename, rotateFilename)
        except OSError, error:
            # logger cannot be used until we get a file handle again, so we
            # store it in 'error'
            pass

        self.stream = open(self.baseFilename, self.mode)

        if error:
            # Note we *expect* to see a "file exists" error if some external 
            # process has not cleaned up these logs.  For now, that just gets
            # logged like any other error.
            zLOG.LOG("Win32FileLogger", zLOG.ERROR,
                     "File rotation error moving '%s' to '%s':" %
                               (self.baseFilename, rotateFilename), error)
        else:
            zLOG.LOG("Win32FileLogger", zLOG.INFO,
                     "File rotation moved '%s' to '%s'" %
                               (self.baseFilename, rotateFilename))

if os.name == "nt":
    # Make it the default for Windows - we install a 'reopen' handler that
    # tries to rotate the logfile.
    FileHandler = Win32FileHandler

class NullHandler(Handler):
    """
    A null handler.  Does nothing.
    """
    def emit(self, record):
        pass

    def handle(self, record):
        pass

class StartupHandler(Handler):
    """
    A handler which outputs messages to a stream but also buffers them until
    they can be flushed to a target handler.  Useful at startup before we can
    know that we can safely write to a config-specified handler.
    """
    def __init__(self, stream=None):
        Handler.__init__(self)
        if not stream:
            stream = sys.stderr
        self.stream = stream
        self.buffer = []

    def emit(self, record):
        try:
            self.buffer.append(record)
            msg = self.format(record)
            self.stream.write("%s\n" % msg)
            self.flush()
        except:
            self.handleError(record)

    def flush(self):
        self.stream.flush()

    def flushBufferTo(self, target):
        for record in self.buffer:
            target.handle(record)
        self.buffer = []
