Commit c974fbd3 authored by Chris McDonough's avatar Chris McDonough

Use PEP282-based logger instead of homegrown.

parent 65108784
##############################################################################
#
# Copyright (c) 2001, 2002 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
#
##############################################################################
"""
An abstract logger meant to provide features to the access logger,
the event logger, and the debug logger.
"""
class BaseLogger:
def reopen(self):
for handler in self.logger.handlers:
if hasattr(handler, 'reopen') and callable(handler.reopen):
handler.reopen()
##############################################################################
#
# Copyright (c) 2001, 2002 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
#
##############################################################################
"""
A logging module which handles event messages.
This uses Vinay Sajip's PEP 282 logging module.
"""
__version__='$Revision$'[11:-2]
import os, sys, time
try:
import textwrap
except ImportError:
textwrap = None
import logging
from BaseLogger import BaseLogger
from LogHandlers import FileHandler, NullHandler, SysLogHandler
from logging import StreamHandler, Formatter
class EventLogger(BaseLogger):
logger = logging.getLogger('event')
logger.addHandler(NullHandler())
log_format = '%(sev)s %(subsys)s %(summary)s%(detail)s'
def log(self, subsystem, severity, summary, detail, error):
if error:
kw = {'exc_info':1}
else:
kw = {}
if detail:
detail = '\n' + detail
else:
detail = ''
msg = self.log_format % {
'sev' : severity_string(severity),
'subsys' : subsystem,
'summary': summary,
'detail' : detail,
}
if textwrap and len(msg) > 80:
msg = '\n'.join(textwrap.wrap(
msg, width=79, subsequent_indent=" "*20,
break_long_words=0))
severity = zlog_to_pep282_severity(severity)
self.logger.log(severity, msg, **kw)
EventLogger = EventLogger()
log_write = EventLogger.log
def severity_string(severity, mapping={
-300: 'TRACE',
-200: 'DEBUG',
-100: 'BLATHER',
0: 'INFO',
100: 'PROBLEM',
200: 'ERROR',
300: 'PANIC',
}):
"""Convert a severity code to a string."""
s = mapping.get(int(severity), '')
return "%s(%s)" % (s, severity)
def zlog_to_pep282_severity(zlog_severity):
"""
We map zLOG severities to PEP282 severities here.
This is how they are mapped:
zLOG severity PEP282 severity
------------- ---------------
PANIC (300) critical (50)
ERROR (200), PROBLEM (100) error (40)
INFO (0) warn (30)
BLATHER (-100) info (20)
DEBUG (-200), TRACE (-300) debug (10)
"""
sev = zlog_severity
if sev >= 300:
return logging.CRITICAL
if sev >= 100:
return logging.ERROR
if sev >= 0:
return logging.WARN
if sev >= -100:
return logging.INFO
else:
return logging.DEBUG
def log_time():
"""Return a simple time string without spaces suitable for logging."""
return ("%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d"
% time.localtime()[:6])
def get_env_severity_info():
# EVENT_LOG_SEVERITY is the preferred envvar, but we accept
# STUPID_LOG_SEVERITY also
eget = os.environ.get
severity = eget('EVENT_LOG_SEVERITY') or eget('STUPID_LOG_SEVERITY')
if severity:
severity = int(severity)
else:
severity = 0 # INFO
return severity
def get_env_syslog_info():
eget = os.environ.get
addr = None
port = None
path = eget('ZSYSLOG')
facility = eget('ZSYSLOG_FACILITY', 'user')
server = eget('ZSYSLOG_SERVER')
if server:
addr, port = server.split(':')
port = int(port)
if addr:
return (facility, (addr, port))
else:
return (facility, path)
def get_env_file_info():
eget = os.environ.get
# EVENT_LOG_FILE is the preferred envvar, but we accept
# STUPID_LOG_FILE also
path = eget('EVENT_LOG_FILE')
if path is None:
path = eget('STUPID_LOG_FILE')
if path is None:
dest = None
else:
dest = path
return dest
formatters = {
'file': Formatter(fmt='------\n%(asctime)s %(message)s',
datefmt='%Y-%m-%dT%H:%M:%S'),
'syslog': Formatter(fmt='%(message)s'),
}
def initialize_from_environment():
""" Reinitialize the event logger from the environment """
# clear the current handlers from the event logger
EventLogger.logger.handlers = []
handlers = []
# set up syslog handler if necessary
facility, syslogdest = get_env_syslog_info()
if syslogdest:
handler = SysLogHandler(syslogdest, facility)
handler.setFormatter(formatters['syslog'])
handlers.append(handler)
# set up file handler if necessary
filedest = get_env_file_info()
if filedest:
handler = FileHandler(filedest)
handler.setFormatter(formatters['file'])
handlers.append(handler)
elif filedest == '':
# if dest is an empty string, log to standard error
handler = StreamHandler()
handler.setFormatter(formatters['file'])
handlers.append(handler)
else:
# log to nowhere, but install a 'null' handler in order to
# prevent error messages from emanating due to a missing handler
handlers.append(NullHandler())
severity = get_env_severity_info()
severity = zlog_to_pep282_severity(severity)
EventLogger.logger.setLevel(severity)
for handler in handlers:
EventLogger.logger.addHandler(handler)
##############################################################################
#
# 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 """
from logging import Handler, StreamHandler
from logging.handlers import SysLogHandler
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 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()
def flush(self):
self.stream.flush()
def flushBufferTo(self, target):
for record in self.buffer:
target.handle(record)
self.buffer = []
##############################################################################
#
# Copyright (c) 2001, 2002 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.
#
##############################################################################
__version__='$Revision: 1.21 $'[11:-2]
import os, sys, time
try:
import textwrap
except ImportError:
textwrap = None
from traceback import format_exception
def severity_string(severity, mapping={
-300: 'TRACE',
-200: 'DEBUG',
-100: 'BLATHER',
0: 'INFO',
100: 'PROBLEM',
200: 'ERROR',
300: 'PANIC',
}):
"""Convert a severity code to a string."""
s = mapping.get(int(severity), '')
return "%s(%s)" % (s, severity)
def log_time():
"""Return a simple time string without spaces suitable for logging."""
return ("%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d"
% time.localtime()[:6])
def _set_log_dest(dest):
global _log_dest
_log_dest = dest
_log_dest = None
_stupid_severity = None
class stupid_log_write:
def __init__(self):
self.initialize()
def initialize(self):
global _log_level
eget = os.environ.get
# EVENT_LOG_FILE is the preferred envvar, but we accept
# STUPID_LOG_FILE also
path = eget('EVENT_LOG_FILE')
if path is None:
path = eget('STUPID_LOG_FILE')
if path is None:
_set_log_dest(None)
else:
if path:
_set_log_dest(open(path, 'a'))
else:
_set_log_dest(sys.stderr)
# EVENT_LOG_SEVERITY is the preferred envvar, but we accept
# STUPID_LOG_SEVERITY also
severity = eget('EVENT_LOG_SEVERITY') or eget('STUPID_LOG_SEVERITY')
if severity:
_log_level = int(severity)
else:
_log_level = 0 # INFO
def log(self, subsystem, severity, summary, detail, error):
if _log_dest is None or severity < _log_level:
return
buf = ["------"]
line = ("%s %s %s %s" %
(log_time(), severity_string(severity), subsystem, summary))
if not textwrap or len(line) < 80:
buf.append(line)
else:
buf.extend(textwrap.wrap(line,
width=79,
subsequent_indent=" "*20,
break_long_words=0))
if detail:
buf.append(str(detail))
if error:
try:
lines = format_exception(error[0], error[1], error[2],
limit=100)
buf.append(''.join(lines))
except:
buf.append("%s: %s" % error[:2])
if buf[-1] and buf[-1][-1] != "\n":
buf.append("") # Cause a final \n to be appended
_log_dest.write("\n".join(buf))
_log_dest.flush()
_log = stupid_log_write()
log_write = _log.log
initialize = _log.initialize
......@@ -86,10 +86,10 @@ There is a default event logging facility that:
can be overridden with the environment variable EVENT_LOG_SEVERITY
"""
__version__='$Revision: 1.12 $'[11:-2]
__version__='$Revision: 1.13 $'[11:-2]
from MinimalLogger import log_write, log_time, severity_string, \
_set_log_dest, initialize
from EventLogger import log_write, log_time, severity_string, \
initialize_from_environment as initialize
from traceback import format_exception
# Standard severities
......
......@@ -63,7 +63,7 @@ class StupidLogTest(unittest.TestCase):
if severity:
os.environ['%s_LOG_SEVERITY' % self.prefix] = str(severity)
self._severity = severity
zLOG.MinimalLogger._log.initialize()
zLOG.initialize()
def verifyEntry(self, f, time=None, subsys=None, severity=None,
summary=None, detail=None, error=None):
......@@ -110,7 +110,6 @@ class StupidLogTest(unittest.TestCase):
def checkBasics(self):
self.setLog()
zLOG.LOG("basic", zLOG.INFO, "summary")
f = self.getLogFile()
self.verifyEntry(f, subsys="basic", summary="summary")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment