Commit 30e96240 authored by Tres Seaver's avatar Tres Seaver

Undeprecated 'zLOG', which will remain a backward-compatibility shim for the Python logging module.

parent d90bf5bb
......@@ -8,6 +8,9 @@ Zope Changes
Restructuring
- Undeprecated 'zLOG', which will remain a backward-compatibility
shim for the Python logging module.
- Indexes: Removed unused parameters from '_apply_index' methods.
- Fixed Collector #2190: Calls to
......
##############################################################################
#
# 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.1 (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 logging
import time
# XXX Defining custom levels needs to move to
# ZConfig.components.logger, I think. It only needs to be done when
# logging configuration is activated (the logger factory is called).
#
# Custom logging levels
CUSTOM_BLATHER = 15 # Mapping for zLOG.BLATHER
CUSTOM_TRACE = 5 # Mapping for zLOG.TRACE
logging.addLevelName("BLATHER", CUSTOM_BLATHER)
logging.addLevelName("TRACE", CUSTOM_TRACE)
def log_write(subsystem, severity, summary, detail, error):
level = (zlog_to_pep282_severity_cache_get(severity) or
zlog_to_pep282_severity(severity))
msg = summary
if detail:
msg = "%s\n%s" % (msg, detail)
logger = logging.getLogger(subsystem)
# Since the logging module of Python does not allow to pass a
# traceback triple, we need to fake the exception. (See also
# Collector #1234).
if isinstance(error, tuple):
try:
raise error[0], error[1], error[2]
except:
pass
logger.log(level, msg, exc_info=(error is not None))
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) FATAL, CRITICAL (50)
ERROR (200) ERROR (40)
WARNING, PROBLEM (100) WARN (30)
INFO (0) INFO (20)
BLATHER (-100) BLATHER (15) [*]
DEBUG (-200) DEBUG (10)
TRACE (-300) TRACE (5) [*]
[*] BLATHER and TRACE are custom logging levels.
"""
sev = zlog_severity
if sev >= 300:
return logging.FATAL
if sev >= 200:
return logging.ERROR
if sev >= 100:
return logging.WARN
if sev >= 0:
return logging.INFO
if sev >= -100:
return CUSTOM_BLATHER
if sev >= -200:
return logging.DEBUG
return CUSTOM_TRACE
zlog_to_pep282_severity_cache = {}
for _sev in range(-300, 301, 100):
zlog_to_pep282_severity_cache[_sev] = zlog_to_pep282_severity(_sev)
zlog_to_pep282_severity_cache_get = zlog_to_pep282_severity_cache.get
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])
##############################################################################
#
# 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.1 (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.
#
##############################################################################
"""General logging facility
Note:
This module exists only for backward compatibility. Any new code
for Zope 2.8 and newer should use the logging module from Python's
standard library directly. zLOG is only an API shim to map existing
use of zLOG onto the standard logging API.
This module attempts to provide a simple programming API for logging
with a pluggable API for defining where log messages should go.
Programmers call the LOG function to log information.
The LOG function, in turn, calls the log_write method to actually write
logging information somewhere. This module provides a very simple
log_write implementation. It is intended that applications main
programs will replace this method with a method more suited to their needs.
The module provides a register_subsystem method that does nothing, but
provides a hook that logging management systems could use to collect
information about subsystems being used.
The module defines several standard severities:
TRACE=-300 -- Trace messages
DEBUG=-200 -- Debugging messages
BLATHER=-100 -- Somebody shut this app up.
INFO=0 -- For things like startup and shutdown.
PROBLEM=100 -- This isn't causing any immediate problems, but deserves
attention.
WARNING=100 -- A wishy-washy alias for PROBLEM.
ERROR=200 -- This is going to have adverse effects.
PANIC=300 -- We're dead!
To plug in a log handler, simply replace the log_write function
with a callable object that takes 5 arguments:
subsystem -- The subsystem generating the message (e.g. ZODB)
severity -- The "severity" of the event. This may be an integer or
a floating point number. Logging back ends may
consider the int() of this valua to be significant.
For example, a backend may consider any severity
whos integer value is WARNING to be a warning.
summary -- A short summary of the event
detail -- A detailed description
error -- A three-element tuple consisting of an error type, value, and
traceback. If provided, then a summary of the error
is added to the detail.
The default logging facility uses Python's logging module as a
back-end; configuration of the logging module must be handled
somewhere else.
"""
from EventLogger import log_write, log_time, severity_string
from traceback import format_exception
# Standard severities
TRACE = -300
DEBUG = -200
BLATHER = -100
INFO = 0
PROBLEM = 100
WARNING = 100
ERROR = 200
PANIC = 300
def initialize():
pass
def set_initializer(func):
"""Set the function used to re-initialize the logs.
This should be called when initialize_from_environment() is not
appropiate.
This does not ensure that the new function gets called; the caller
should do that separately.
"""
pass
def LOG(subsystem, severity, summary, detail='', error=None, reraise=None):
"""Log some information
The required arguments are:
subsystem -- The subsystem generating the message (e.g. ZODB)
severity -- The "severity" of the event. This may be an integer or
a floating point number. Logging back ends may
consider the int() of this value to be significant.
For example, a backend may consider any severity
whos integer value is WARNING to be a warning.
summary -- A short summary of the event
detail -- A detailed description
error -- A three-element tuple consisting of an error type, value, and
traceback. If provided, then a summary of the error
is added to the detail.
reraise -- If provided with a true value, then the error given by
error is reraised.
"""
log_write(subsystem, severity, summary, detail, error)
if reraise and error:
raise error[0], error[1], error[2]
_subsystems = []
def register_subsystem(subsystem):
"""Register a subsystem name
A logging facility might replace this function to collect information about
subsystems used in an application.
"""
_subsystems.append(subsystem)
# Most apps interested in logging only want the names below.
__all__ = ['LOG', 'TRACE', 'DEBUG', 'BLATHER', 'INFO', 'PROBLEM',
'WARNING', 'ERROR', 'PANIC', 'log_time']
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Tests of the integration with the standard logging package."""
import logging
import unittest
from ZConfig.components.logger.tests.test_logger import LoggingTestBase
import zLOG
from zLOG.EventLogger import log_write
class CollectingHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
self.records = []
def emit(self, record):
self.records.append(record)
class LoggingIntegrationTestCase(LoggingTestBase):
def setUp(self):
LoggingTestBase.setUp(self)
self.handler = CollectingHandler()
self.records = self.handler.records
self.logger = logging.getLogger()
self.logger.addHandler(self.handler)
def test_log_record(self):
#log_write(subsystem, severity, summary, detail, error)
log_write("sample.subsystem", zLOG.WARNING, "summary", "detail", None)
self.assertEqual(len(self.records), 1)
record = self.records[0]
self.assertEqual(record.levelno, logging.WARN)
self.assertEqual(record.name, "sample.subsystem")
# Make sure both the message and the detail information appear
# in the text that gets logged:
record.msg.index("summary")
record.msg.index("detail")
def test_suite():
return unittest.makeSuite(LoggingIntegrationTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")
##############################################################################
#
# 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.1 (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.
#
##############################################################################
import os
import sys
import tempfile
import unittest
import zLOG
import logging
severity_string = {
-300: 'TRACE',
-200: 'DEBUG',
-100: 'BLATHER',
0: 'INFO',
100: 'PROBLEM',
200: 'ERROR',
300: 'PANIC',
}
class EventLogTest(unittest.TestCase):
"""Test zLOG with the default implementation."""
def setUp(self):
self.path = tempfile.mktemp()
self._severity = 0
# Windows cannot remove a file that's open. The logging code
# keeps the log file open, and I can't find an advertised API
# to tell the logger to close a log file. So here we cheat:
# tearDown() will close and remove all the handlers that pop
# into existence after setUp() runs. This breaks into internals,
# but I couldn't find a sane way to do it.
self.handlers = logging._handlers.keys() # capture current handlers
def tearDown(self):
# Close and remove all the handlers that came into existence
# since setUp ran.
for h in logging._handlers.keys():
if h not in self.handlers:
h.close()
del logging._handlers[h]
os.remove(self.path)
zLOG.initialize()
def setLog(self, severity=0):
# XXX Need to write new logging initialization code here!
self._severity = severity
zLOG.initialize()
def verifyEntry(self, f, time=None, subsys=None, severity=None,
summary=None, detail=None, error=None):
# skip to the beginning of next entry
line = f.readline().strip()
while line != "------":
if not line:
self.fail("can't find entry in log file")
line = f.readline()
line = f.readline().strip()
_time, rest = line.split(" ", 1)
if time is not None:
self.assertEqual(_time, time)
if subsys is not None:
self.assert_(rest.find(subsys) != -1, "subsystem mismatch")
if severity is not None and severity >= self._severity:
s = severity_string[severity]
self.assert_(rest.find(s) != -1, "severity mismatch")
if summary is not None:
self.assert_(rest.find(summary) != -1, "summary mismatch")
if detail is not None:
line = f.readline()
self.assert_(line.find(detail) != -1, "missing detail")
if error is not None:
line = f.readline().strip()
self.assert_(line.startswith('Traceback'),
"missing traceback")
last = "%s: %s" % (error[0], error[1])
if last.startswith("exceptions."):
last = last[len("exceptions."):]
while 1:
line = f.readline().strip()
if not line:
self.fail("couldn't find end of traceback")
if line == "------":
self.fail("couldn't find end of traceback")
if line == last:
break
def getLogFile(self):
return open(self.path, 'rb')
def checkBasics(self):
self.setLog()
zLOG.LOG("basic", zLOG.INFO, "summary")
f = self.getLogFile()
try:
self.verifyEntry(f, subsys="basic", summary="summary")
finally:
f.close()
def checkDetail(self):
self.setLog()
zLOG.LOG("basic", zLOG.INFO, "xxx", "this is a detail")
f = self.getLogFile()
try:
self.verifyEntry(f, subsys="basic", detail="detail")
finally:
f.close()
def checkError(self):
self.setLog()
try:
1 / 0
except ZeroDivisionError, err:
err = sys.exc_info()
zLOG.LOG("basic", zLOG.INFO, "summary")
zLOG.LOG("basic", zLOG.ERROR, "raised exception", error=err)
f = self.getLogFile()
try:
self.verifyEntry(f, subsys="basic", summary="summary")
self.verifyEntry(f, subsys="basic", severity=zLOG.ERROR,
error=err)
finally:
f.close()
def test_suite():
return unittest.TestSuite()
return unittest.makeSuite(EventLogTest, 'check')
if __name__ == "__main__":
loader = unittest.TestLoader()
loader.testMethodPrefix = "check"
unittest.main(testLoader=loader)
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