[Zope3-dev] zope.exceptions.exceptionformatter
Shane Hathaway
shane at hathawaymix.org
Tue Jul 19 13:34:28 EDT 2005
Shane Hathaway wrote:
> I'd like to change Zope 3 to log exceptions using
> zope.exceptions.exceptionformatter. Zope's exceptionformatter formats
> tracebacks with information from __traceback_info__ and
> __traceback_supplement__ variables, which is very useful for debugging
> problems with page templates. I've made all the necessary changes in my
> own Zope 3 sandbox. I've also added an option to exceptionformatter for
> displaying module filenames rather than module names, so that old
> concern is gone.
>
> However, the place where it's possible to properly install
> exceptionformatter as the traceback generator is inside ZConfig. AFAICT,
> ZConfig tries hard to not import anything from the zope package. So I
> don't know how to get my code installed without poking a hole in
> ZConfig. What should I do?
FWIW, here are the patches I'm proposing.
Shane
-------------- next part --------------
Index: zope/app/traversing/adapters.py
===================================================================
--- zope/app/traversing/adapters.py (revision 33328)
+++ zope/app/traversing/adapters.py (working copy)
@@ -146,6 +146,8 @@
not provided.
"""
+ __traceback_info__ = (obj, name)
+
if name == '.':
return obj
Index: zope/exceptions/exceptionformatter.py
===================================================================
--- zope/exceptions/exceptionformatter.py (revision 33328)
+++ zope/exceptions/exceptionformatter.py (working copy)
@@ -19,6 +19,7 @@
import sys
import cgi
import linecache
+import traceback
DEBUG_EXCEPTION_FORMATTER = 1
@@ -27,14 +28,15 @@
line_sep = '\n'
show_revisions = 0
- def __init__(self, limit=None):
+ def __init__(self, limit=None, with_filenames=False):
self.limit = limit
+ self.with_filenames = with_filenames
def escape(self, s):
return s
def getPrefix(self):
- return 'Traceback (innermost last):'
+ return 'Traceback (most recent call last):'
def getLimit(self):
limit = self.limit
@@ -122,14 +124,16 @@
name = co.co_name
locals = f.f_locals
globals = f.f_globals
- modname = globals.get('__name__', filename)
- s = ' Module %s, line %d' % (modname, lineno)
+ if self.with_filenames:
+ s = ' File "%s", line %d' % (filename, lineno)
+ else:
+ modname = globals.get('__name__', filename)
+ s = ' Module %s, line %d' % (modname, lineno)
+ revision = self.getRevision(globals)
+ if revision:
+ s = s + ', rev. %s' % revision
- revision = self.getRevision(globals)
- if revision:
- s = s + ', rev. %s' % revision
-
s = s + ', in %s' % name
result = []
@@ -158,7 +162,6 @@
result.extend(self.formatSupplement(supp, tb))
except:
if DEBUG_EXCEPTION_FORMATTER:
- import traceback
traceback.print_exc()
# else just swallow the exception.
@@ -168,14 +171,12 @@
result.append(self.formatTracebackInfo(tbi))
except:
if DEBUG_EXCEPTION_FORMATTER:
- import traceback
traceback.print_exc()
# else just swallow the exception.
return self.line_sep.join(result)
def formatExceptionOnly(self, etype, value):
- import traceback
return self.line_sep.join(
traceback.format_exception_only(etype, value))
@@ -210,7 +211,7 @@
return cgi.escape(s)
def getPrefix(self):
- return '<p>Traceback (innermost last):\r\n<ul>'
+ return '<p>Traceback (most recent call last):\r\n<ul>'
def formatSupplementLine(self, line):
return '<b>%s</b>' % self.escape(str(line))
@@ -233,13 +234,32 @@
if hasattr(sys, 'tracebacklimit'):
limit = min(limit, sys.tracebacklimit)
-text_formatter = TextExceptionFormatter(limit)
-html_formatter = HTMLExceptionFormatter(limit)
+def format_exception(t, v, tb, limit=None, as_html=False,
+ with_filenames=False):
+ """Format a stack trace and the exception information.
-def format_exception(t, v, tb, limit=None, as_html=0):
+ See 'traceback.format_exception', but adds supplemental
+ information to the traceback and accepts two options, 'as_html'
+ and 'with_filenames'.
+ """
if as_html:
- fmt = html_formatter
+ fmt = HTMLExceptionFormatter(limit, with_filenames)
else:
- fmt = text_formatter
+ fmt = TextExceptionFormatter(limit, with_filenames)
return fmt.formatException(t, v, tb)
+
+
+def print_exception(t, v, tb, limit=None, file=None, as_html=False,
+ with_filenames=True):
+ """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
+
+ Similar to 'traceback.print_exception', but adds supplemental
+ information to the traceback and accepts two options, 'as_html'
+ and 'with_filenames'.
+ """
+ if file is None:
+ file = sys.stderr
+ lines = format_exception(t, v, tb, limit, as_html, with_filenames)
+ for line in lines:
+ file.write(line)
Index: zope/publisher/base.py
===================================================================
--- zope/publisher/base.py (revision 33328)
+++ zope/publisher/base.py (working copy)
@@ -18,12 +18,12 @@
$Id$
"""
-import traceback
from cStringIO import StringIO
from zope.interface import implements, providedBy
from zope.interface.common.mapping import IReadMapping, IEnumerableMapping
from zope.publisher.interfaces import NotFound
+from zope.exceptions.exceptionformatter import print_exception
from zope.publisher.interfaces import IPublication, IHeld
from zope.publisher.interfaces import NotFound, DebugError, Unauthorized
@@ -68,7 +68,7 @@
def handleException(self, exc_info):
'See IPublisherResponse'
- traceback.print_exception(
+ print_exception(
exc_info[0], exc_info[1], exc_info[2], 100, self)
def internalError(self):
-------------- next part --------------
Index: components/logger/handlers.xml
===================================================================
--- components/logger/handlers.xml (revision 33328)
+++ components/logger/handlers.xml (working copy)
@@ -12,6 +12,13 @@
</description>
<key name="dateformat"
default="%Y-%m-%dT%H:%M:%S"/>
+ <key name="enhance-tracebacks" datatype="boolean" default="yes">
+ <description>
+ When enabled, logging uses zope.exceptions.exceptionformatter
+ to enhance exception tracebacks with information from
+ __traceback_info__ and __traceback_supplement__ variables.
+ </description>
+ </key>
<key name="level"
default="notset"
datatype="ZConfig.components.logger.datatypes.logging_level"/>
Index: components/logger/handlers.py
===================================================================
--- components/logger/handlers.py (revision 33328)
+++ components/logger/handlers.py (working copy)
@@ -13,6 +13,7 @@
##############################################################################
"""ZConfig factory datatypes for log handlers."""
+import logging
import sys
from ZConfig.components.logger.factory import Factory
@@ -54,7 +55,23 @@
value = value.replace(pattern, replacement)
return value
+class EnhancedFormatter(logging.Formatter):
+ def formatException(self, ei):
+ """Format and return the specified exception information as a string.
+ Uses zope.exceptions.exceptionformatter to enhance the traceback.
+ """
+ import cStringIO
+ from zope.exceptions.exceptionformatter import print_exception
+
+ sio = cStringIO.StringIO()
+ print_exception(ei[0], ei[1], ei[2], file=sio, with_filenames=True)
+ s = sio.getvalue()
+ sio.close()
+ if s.endswith("\n"):
+ s = s[:-1]
+ return s
+
class HandlerFactory(Factory):
def __init__(self, section):
Factory.__init__(self)
@@ -65,10 +82,12 @@
"subclasses must override create_loghandler()")
def create(self):
- import logging
logger = self.create_loghandler()
- logger.setFormatter(logging.Formatter(self.section.format,
- self.section.dateformat))
+ if self.section.enhance_tracebacks:
+ f = EnhancedFormatter
+ else:
+ f = logging.Formatter
+ logger.setFormatter(f(self.section.format, self.section.dateformat))
logger.setLevel(self.section.level)
return logger
More information about the Zope3-dev
mailing list