[Zope3-checkins] SVN: Zope3/trunk/ Restructured the error reporting
service to become a utility.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Wed Dec 15 18:52:42 EST 2004
Log message for revision 28629:
Restructured the error reporting service to become a utility.
Changed:
U Zope3/trunk/doc/CHANGES.txt
U Zope3/trunk/src/zope/app/appsetup/bootstrap.py
U Zope3/trunk/src/zope/app/appsetup/tests.py
U Zope3/trunk/src/zope/app/configure.zcml
A Zope3/trunk/src/zope/app/error/
U Zope3/trunk/src/zope/app/error/__init__.py
A Zope3/trunk/src/zope/app/error/bootstrap.py
U Zope3/trunk/src/zope/app/error/browser/__init__.py
U Zope3/trunk/src/zope/app/error/browser/configure.zcml
U Zope3/trunk/src/zope/app/error/browser/error.gif
U Zope3/trunk/src/zope/app/error/configure.zcml
A Zope3/trunk/src/zope/app/error/error.py
U Zope3/trunk/src/zope/app/error/interfaces.py
U Zope3/trunk/src/zope/app/error/tests.py
D Zope3/trunk/src/zope/app/errorservice/
U Zope3/trunk/src/zope/app/publication/tests/test_zopepublication.py
U Zope3/trunk/src/zope/app/publication/zopepublication.py
U Zope3/trunk/src/zope/app/servicenames.py
U Zope3/trunk/src/zope/app/zopeappgenerations/evolve1.py
-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/doc/CHANGES.txt 2004-12-15 23:52:41 UTC (rev 28629)
@@ -161,6 +161,11 @@
Restructuring
+ - Removal of Services.
+
+ + Converted Error Reporting Service to a utility. Added database
+ evolution code to convert all serice instances to utilities.
+
- The `pluggableauth` package has been deprecated. The `pas` module
provides a much more modular approach with many more capabilities.
Modified: Zope3/trunk/src/zope/app/appsetup/bootstrap.py
===================================================================
--- Zope3/trunk/src/zope/app/appsetup/bootstrap.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/appsetup/bootstrap.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -31,11 +31,11 @@
from zope.app.publication.zopepublication import ZopePublication
from zope.app.folder import rootFolder
from zope.app.servicenames import PrincipalAnnotation
-from zope.app.servicenames import ErrorLogging, Utilities
+from zope.app.servicenames import Utilities
from zope.app.site.service import ServiceManager, ServiceRegistration
-from zope.app.errorservice import RootErrorReportingService
from zope.app.container.interfaces import INameChooser
from zope.app.utility import UtilityRegistration, LocalUtilityService
+from zope.app.utility.interfaces import ILocalUtilityService
# XXX It should be possible to remove each of these from the basic
# bootstrap, at which point we can remove the
@@ -87,8 +87,8 @@
Returns the name added or ``None`` if nothing was added.
"""
utility_manager = zapi.getService(Utilities, root_folder)
- utility = utility_manager.queryUtility(interface, name)
- if utility is None:
+ utils = list(utility_manager.getLocalUtilitiesFor(interface))
+ if len(utils) == 0:
return addConfigureUtility(
root_folder, interface, utility_type, utility_factory,
name, **kw
@@ -125,7 +125,7 @@
setattr(service, k, v)
return name
-def configureService(root_folder, service_type, name, initial_status='Active'):
+def configureService(root_folder, service_type, name, initial_status=u'Active'):
"""Configure a service in the root folder."""
package = getServiceManagerDefault(root_folder)
registration_manager = package.getRegistrationManager()
@@ -139,8 +139,8 @@
def addConfigureUtility(
root_folder, interface, utility_type, utility_factory, name='', **kw):
"""Add and configure a service to the root folder."""
- folder_name = addUtility(root_folder, utility_type, utility_factory, **kw)
- configureUtility(root_folder, interface, utility_type, name, folder_name)
+ utility_name = addUtility(root_folder, utility_type, utility_factory, **kw)
+ configureUtility(root_folder, interface, utility_type, name, utility_name)
return name
def addUtility(root_folder, utility_type, utility_factory, **kw):
@@ -161,7 +161,7 @@
def configureUtility(
root_folder, interface, utility_type, name, folder_name,
- initial_status='Active'):
+ initial_status=u'Active'):
"""Configure a utility in the root folder."""
package = getServiceManagerDefault(root_folder)
registration_manager = package.getRegistrationManager()
@@ -223,9 +223,6 @@
service_manager = getServiceManager(root_folder)
# Sundry other services
- ensureService(service_manager, root_folder, ErrorLogging,
- RootErrorReportingService,
- copy_to_zlog=False)
ensureService(service_manager, root_folder, PrincipalAnnotation,
PrincipalAnnotationService)
ensureService(service_manager, root_folder, Utilities,
Modified: Zope3/trunk/src/zope/app/appsetup/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/appsetup/tests.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/appsetup/tests.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -19,21 +19,22 @@
from transaction import get_transaction
from ZODB.tests.util import DB
+from zope.app.error.error import ErrorReportingUtility
+from zope.app.error.interfaces import IErrorReportingUtility
from zope.app.folder import rootFolder
from zope.app.folder.interfaces import IRootFolder
-from zope.app.traversing.api import traverse
-from zope.app.errorservice.interfaces import IErrorReportingService
from zope.app.principalannotation.interfaces import IPrincipalAnnotationService
from zope.app.publication.zopepublication import ZopePublication
+from zope.app.servicenames import Utilities
+from zope.app.site.service import ServiceManager
from zope.app.site.tests.placefulsetup import PlacefulSetup
-from zope.app.servicenames import ErrorLogging, Utilities
-from zope.app.errorservice import ErrorReportingService
from zope.app.traversing.api import traverse
-from zope.app.site.service import ServiceManager
+from zope.app.utility.utility import LocalUtilityService
from zope.app.appsetup.bootstrap import bootStrapSubscriber
from zope.app.appsetup.bootstrap import addService, configureService, \
- ensureService, getInformationFromEvent, getServiceManager, ensureObject
+ ensureService, getInformationFromEvent, getServiceManager, ensureObject,\
+ ensureUtility
class EventStub(object):
@@ -84,7 +85,7 @@
package = traverse(root_folder, package_name)
cx.close()
- def test_ensureService(self):
+ def test_ensureUtility(self):
self.createRFAndSM()
self.createRootFolder()
@@ -94,13 +95,16 @@
# XXX check EventSub
root_folder = self.root_folder
service_manager = getServiceManager(root_folder)
+ ensureService(service_manager, root_folder, Utilities,
+ LocalUtilityService)
for i in range(2):
cx = self.db.open()
- name = ensureService(service_manager, root_folder,
- ErrorLogging,
- ErrorReportingService)
+ name = ensureUtility(root_folder, IErrorReportingUtility,
+ 'ErrorReporting', ErrorReportingUtility,
+ 'ErrorReporting')
+
if i == 0:
- self.assertEqual(name, 'ErrorLogging')
+ self.assertEqual(name, 'ErrorReporting')
else:
self.assertEqual(name, None)
@@ -110,8 +114,8 @@
package_name = '/++etc++site/default'
package = traverse(self.root_folder, package_name)
- self.assert_(IErrorReportingService.providedBy(
- traverse(package, 'ErrorLogging')))
+ self.assert_(IErrorReportingUtility.providedBy(
+ traverse(package, 'ErrorReporting')))
get_transaction().commit()
cx.close()
Modified: Zope3/trunk/src/zope/app/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/configure.zcml 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/configure.zcml 2004-12-15 23:52:41 UTC (rev 28629)
@@ -47,8 +47,12 @@
<!-- Local Component Registration -->
<include package="zope.app.registration" />
+
+ <!-- Database boostrapping and maintanance -->
+ <include package=".appsetup" />
+ <include package=".zopeappgenerations" />
+
<!-- Services -->
- <include package="zope.app.errorservice" />
<include package="zope.app.pluggableauth" />
<include package="zope.app.site" />
<include package="zope.app.adapter" />
@@ -56,6 +60,7 @@
<include package="zope.app.principalannotation" />
<!-- Utilities -->
+ <include package="zope.app.error" />
<include package="zope.app.schema" />
<include package="zope.app.intid" />
<include package="zope.app.keyreference" />
@@ -66,12 +71,6 @@
<!-- Broken-object support -->
<include package="zope.app.broken" />
-
- <!-- Database boostrapping and maintanance -->
- <include package=".appsetup" />
- <include package=".zopeappgenerations" />
-
-
<!-- Skins -->
<include package="zope.app.basicskin" />
Copied: Zope3/trunk/src/zope/app/error (from rev 28626, Zope3/trunk/src/zope/app/errorservice)
Modified: Zope3/trunk/src/zope/app/error/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/__init__.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/__init__.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -1,242 +1,14 @@
-##############################################################################
-#
-# 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.
-#
-##############################################################################
-"""Error reporting service
+# Make directory a package.
-This is a port of the Zope 2 error reporting object
+###############################################################################
+# BBB: 12/14/2004
-$Id$
-"""
-__docformat__ = 'restructuredtext'
+from error import RootErrorReportingUtility
+from error import ErrorReportingUtility
+from error import globalErrorReportingUtility
-import time
-import logging
-from persistent import Persistent
-from random import random
-from thread import allocate_lock
-from types import StringTypes
+RootErrorReportingService = RootErrorReportingUtility
+ErrorReportingService = ErrorReportingUtility
+globalErrorReportingService = globalErrorReportingUtility
-from zope.exceptions.exceptionformatter import format_exception
-from zope.interface import implements
-from zope.app.site.interfaces import ISimpleService
-from zope.app.container.contained import Contained
-from interfaces import IErrorReportingService, ILocalErrorReportingService
-
-#Restrict the rate at which errors are sent to the Event Log
-_rate_restrict_pool = {}
-
-# The number of seconds that must elapse on average between sending two
-# exceptions of the same name into the the Event Log. one per minute.
-_rate_restrict_period = 60
-
-# The number of exceptions to allow in a burst before the above limit
-# kicks in. We allow five exceptions, before limiting them to one per
-# minute.
-_rate_restrict_burst = 5
-
-# _temp_logs holds the logs.
-_temp_logs = {} # { oid -> [ traceback string ] }
-
-cleanup_lock = allocate_lock()
-
-class ErrorReportingService(Persistent, Contained):
- """Error Reporting Service
- """
- implements(IErrorReportingService,
- ILocalErrorReportingService,
- ISimpleService,
- )
-
- keep_entries = 20
- copy_to_zlog = 0
- _ignored_exceptions = ('Unauthorized',)
-
-
- def _getLog(self):
- """Returns the log for this object.
- Careful, the log is shared between threads.
- """
- log = _temp_logs.get(self._p_oid, None)
- if log is None:
- log = []
- _temp_logs[self._p_oid] = log
- return log
-
- # Exceptions that happen all the time, so we dont need
- # to log them. Eventually this should be configured
- # through-the-web.
- def raising(self, info, request=None):
- """Log an exception.
- Called by ZopePublication.handleException method.
- """
- now = time.time()
- try:
- tb_text = None
- tb_html = None
-
- strtype = str(getattr(info[0], '__name__', info[0]))
- if strtype in self._ignored_exceptions:
- return
-
- if not isinstance(info[2], StringTypes):
- tb_text = ''.join(
- format_exception(*info, **{'as_html': 0}))
- tb_html = ''.join(
- format_exception(*info, **{'as_html': 1}))
- else:
- tb_text = info[2]
-
- url = None
- username = None
- req_html = None
- if request:
- # XXX: Temporary fix, which Steve should undo. URL is
- # just too HTTPRequest-specific.
- if hasattr(request, 'URL'):
- url = request.URL
- try:
- # XXX: UnauthenticatedPrincipal does not have getLogin()
- if hasattr(request.principal, 'getLogin'):
- login = request.principal.getLogin()
- else:
- login = 'unauthenticated'
- username = ', '.join(map(str, (login,
- request.principal.id,
- request.principal.title,
- request.principal.description
- )))
- # When there's an unauthorized access, request.principal is
- # not set, so we get an AttributeError
- # XXX is this right? Surely request.principal should be set!
- # XXX Answer: Catching AttributeError is correct for the
- # simple reason that UnauthenticatedUser (which
- # I always use during coding), has no 'getLogin()'
- # method. However, for some reason this except
- # does **NOT** catch these errors.
- except AttributeError:
- pass
-
- req_html = ''.join(['%s : %s<br>' % item
- for item in request.items()])
- try:
- strv = str(info[1])
- # A call to str(obj) could raise anything at all.
- # We'll ignore these errors, and print something
- # useful instead, but also log the error.
- except:
- logging.getLogger('SiteError').exception(
- 'Error in ErrorReportingService while getting a str '
- 'representation of an object')
- strv = '<unprintable %s object>' % (
- str(type(info[1]).__name__)
- )
-
- log = self._getLog()
- entry_id = str(now) + str(random()) # Low chance of collision
-
- log.append({
- 'type': strtype,
- 'value': strv,
- 'time': time.ctime(now),
- 'id': entry_id,
- 'tb_text': tb_text,
- 'tb_html': tb_html,
- 'username': username,
- 'url': url,
- 'req_html': req_html,
- })
- cleanup_lock.acquire()
- try:
- if len(log) >= self.keep_entries:
- del log[:-self.keep_entries]
- finally:
- cleanup_lock.release()
-
- if self.copy_to_zlog:
- self._do_copy_to_zlog(now, strtype, str(url), info)
- finally:
- info = None
-
- def _do_copy_to_zlog(self, now, strtype, url, info):
- # XXX info is unused; logging.exception() will call sys.exc_info()
- # work around this with an evil hack
- when = _rate_restrict_pool.get(strtype,0)
- if now > when:
- next_when = max(when,
- now - _rate_restrict_burst*_rate_restrict_period)
- next_when += _rate_restrict_period
- _rate_restrict_pool[strtype] = next_when
- try:
- raise info[0], info[1], info[2]
- except:
- logging.getLogger('SiteError').exception(str(url))
-
- def getProperties(self):
- return {
- 'keep_entries': self.keep_entries,
- 'copy_to_zlog': self.copy_to_zlog,
- 'ignored_exceptions': self._ignored_exceptions,
- }
-
- def setProperties(self, keep_entries, copy_to_zlog=0,
- ignored_exceptions=()):
- """Sets the properties of this site error log.
- """
- copy_to_zlog = bool(copy_to_zlog)
- self.keep_entries = int(keep_entries)
- self.copy_to_zlog = copy_to_zlog
- self._ignored_exceptions = tuple(
- filter(None, map(str, ignored_exceptions))
- )
-
- def getLogEntries(self):
- """Returns the entries in the log, most recent first.
-
- Makes a copy to prevent changes.
- """
- res = [entry.copy() for entry in self._getLog()]
- res.reverse()
- return res
-
- def getLogEntryById(self, id):
- """Returns the specified log entry.
- Makes a copy to prevent changes. Returns None if not found.
- """
- for entry in self._getLog():
- if entry['id'] == id:
- return entry.copy()
- return None
-
-class RootErrorReportingService(ErrorReportingService):
- rootId = 'root'
-
- def _getLog(self):
- """Returns the log for this object.
- Careful, the log is shared between threads.
- """
- log = _temp_logs.get(self.rootId, None)
- if log is None:
- log = []
- _temp_logs[self.rootId] = log
- return log
-
-globalErrorReportingService = RootErrorReportingService()
-
-def _cleanup_temp_log():
- _temp_logs.clear()
-
-_clear = _cleanup_temp_log
-# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
-from zope.testing.cleanup import addCleanUp
-addCleanUp(_clear)
-del addCleanUp
+###############################################################################
Added: Zope3/trunk/src/zope/app/error/bootstrap.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/bootstrap.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/bootstrap.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -0,0 +1,36 @@
+##############################################################################
+#
+# Copyright (c) 2002, 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.
+#
+##############################################################################
+"""Bootstrap code for error reporting utility.
+
+$Id: bootstrap.py 28023 2004-10-12 18:11:29Z anguenot $
+"""
+
+from zope.app.appsetup.bootstrap import ensureUtility, getInformationFromEvent
+
+from zope.app.error.error import RootErrorReportingUtility
+from zope.app.error.interfaces import IErrorReportingUtility
+
+def bootStrapSubscriber(event):
+ """Subscriber to the IDataBaseOpenedEvent
+
+ Create utility at that time if not yet present
+ """
+
+ db, connection, root, root_folder = getInformationFromEvent(event)
+
+ ensureUtility(root_folder, IErrorReportingUtility, 'ErrorReporting',
+ RootErrorReportingUtility, copy_to_zlog=False)
+
+ get_transaction().commit()
+ connection.close()
Modified: Zope3/trunk/src/zope/app/error/browser/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/browser/__init__.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/browser/__init__.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -11,18 +11,18 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Define view component for event service control.
+"""Define view component for event utility control.
$Id$
"""
from zope.app.publisher.browser import BrowserView
from zope.app import zapi
-from zope.app.servicenames import ErrorLogging
-from zope.app.errorservice.interfaces import ILocalErrorReportingService
+from zope.app.error.interfaces import IErrorReportingUtility
+from zope.app.error.interfaces import ILocalErrorReportingUtility
class EditErrorLog(object):
- __used_for__ = ILocalErrorReportingService
+ __used_for__ = ILocalErrorReportingUtility
def updateProperties(self, keep_entries, copy_to_zlog=None,
ignored_exceptions=None):
@@ -42,12 +42,12 @@
# redirect the browser to the site root "/@@errorRedirect.html"
# to handle redirection to the site error logger instead
try:
- err = zapi.getService(ErrorLogging)
+ err = zapi.getUtility(IErrorReportingUtility)
url = str(zapi.getView(err, 'absolute_url', self.request))
url = url + "/@@SelectedManagementView.html"
except TypeError:
- siterooturl = self.request.getApplicationURL()
- url = siterooturl + "/@@errorRedirect.html"
+ # siterooturl = self.request.getApplicationURL()
+ url = self.request.getURL(1) + "/@@SelectedManagementView.html"
self.request.response.redirect(url)
Modified: Zope3/trunk/src/zope/app/error/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/browser/configure.zcml 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/browser/configure.zcml 2004-12-15 23:52:41 UTC (rev 28629)
@@ -16,10 +16,10 @@
title="Errors"
action="@@errorRedirect.html" />
- <!--Error Logging Service -->
+ <!--Error Logging Utility -->
<pages
- for="zope.app.errorservice.interfaces.IErrorReportingService"
+ for="zope.app.error.interfaces.IErrorReportingUtility"
permission="zope.ManageServices"
class=".EditErrorLog">
@@ -33,14 +33,14 @@
</pages>
<addMenuItem
- class="zope.app.errorservice.ErrorReportingService"
- title="Error Logging Service"
- description="Error Reporting Service for Logging Errors"
+ class="zope.app.error.error.ErrorReportingUtility"
+ title="Error Logging Utility"
+ description="Error Reporting Utility for Logging Errors"
permission="zope.ManageServices" />
<icon name="zmi_icon"
- for="zope.app.errorservice.interfaces.IErrorReportingService"
- file="error_service.gif" />
+ for="zope.app.error.interfaces.IErrorReportingUtility"
+ file="error.gif" />
</zope:configure>
Modified: Zope3/trunk/src/zope/app/error/browser/error.gif
===================================================================
(Binary files differ)
Modified: Zope3/trunk/src/zope/app/error/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/configure.zcml 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/configure.zcml 2004-12-15 23:52:41 UTC (rev 28629)
@@ -1,41 +1,44 @@
-<configure xmlns="http://namespaces.zope.org/zope">
+<configure
+ xmlns="http://namespaces.zope.org/zope">
- <!-- Error Reporting Service -->
+ <!-- BBB: 12/15/2004 -->
+ <modulealias
+ module="zope.app.error"
+ alias="zope.app.errorservice" />
- <serviceType
- id="ErrorLogging"
- interface=".interfaces.IErrorReportingService"
- />
- <content class=".ErrorReportingService">
+ <localUtility class=".error.ErrorReportingUtility">
<factory
id="zope.app.ErrorLogging"
/>
<require
permission="zope.Public"
- interface=".interfaces.IErrorReportingService"
+ interface=".interfaces.IErrorReportingUtility"
/>
<require
permission="zope.ManageServices"
- interface=".interfaces.ILocalErrorReportingService"
+ interface=".interfaces.ILocalErrorReportingUtility"
/>
- </content>
+ </localUtility>
- <content class=".RootErrorReportingService">
+ <content class=".error.RootErrorReportingUtility">
<require
permission="zope.Public"
- interface=".interfaces.IErrorReportingService"
+ interface=".interfaces.IErrorReportingUtility"
/>
<require
permission="zope.ManageServices"
- interface=".interfaces.ILocalErrorReportingService"
+ interface=".interfaces.ILocalErrorReportingUtility"
/>
- </content>
+ </content>
- <service
- serviceType="ErrorLogging"
- permission="zope.Public"
- component=".globalErrorReportingService"
+ <utility
+ provides=".interfaces.IErrorReportingUtility"
+ component=".error.globalErrorReportingUtility" />
+
+ <subscriber
+ for="zope.app.appsetup.IDatabaseOpenedEvent"
+ factory=".bootstrap.bootStrapSubscriber"
/>
<include package=".browser" />
Added: Zope3/trunk/src/zope/app/error/error.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/error.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/error.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -0,0 +1,242 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Error Reporting Utility
+
+This is a port of the Zope 2 error reporting object
+
+$Id: __init__.py 26735 2004-07-23 22:04:28Z pruggera $
+"""
+__docformat__ = 'restructuredtext'
+
+import time
+import logging
+from persistent import Persistent
+from random import random
+from thread import allocate_lock
+from types import StringTypes
+
+from zope.exceptions.exceptionformatter import format_exception
+from zope.interface import implements
+
+from zope.app.container.contained import Contained
+from zope.app.error.interfaces import IErrorReportingUtility
+from zope.app.error.interfaces import ILocalErrorReportingUtility
+
+#Restrict the rate at which errors are sent to the Event Log
+_rate_restrict_pool = {}
+
+# The number of seconds that must elapse on average between sending two
+# exceptions of the same name into the the Event Log. one per minute.
+_rate_restrict_period = 60
+
+# The number of exceptions to allow in a burst before the above limit
+# kicks in. We allow five exceptions, before limiting them to one per
+# minute.
+_rate_restrict_burst = 5
+
+# _temp_logs holds the logs.
+_temp_logs = {} # { oid -> [ traceback string ] }
+
+cleanup_lock = allocate_lock()
+
+class ErrorReportingUtility(Persistent, Contained):
+ """Error Reporting Utility"""
+ implements(IErrorReportingUtility, ILocalErrorReportingUtility)
+
+ keep_entries = 20
+ copy_to_zlog = 0
+ _ignored_exceptions = ('Unauthorized',)
+
+
+ def _getLog(self):
+ """Returns the log for this object.
+ Careful, the log is shared between threads.
+ """
+ log = _temp_logs.get(self._p_oid, None)
+ if log is None:
+ log = []
+ _temp_logs[self._p_oid] = log
+ return log
+
+ # Exceptions that happen all the time, so we dont need
+ # to log them. Eventually this should be configured
+ # through-the-web.
+ def raising(self, info, request=None):
+ """Log an exception.
+ Called by ZopePublication.handleException method.
+ """
+ now = time.time()
+ try:
+ tb_text = None
+ tb_html = None
+
+ strtype = str(getattr(info[0], '__name__', info[0]))
+ if strtype in self._ignored_exceptions:
+ return
+
+ if not isinstance(info[2], StringTypes):
+ tb_text = ''.join(
+ format_exception(*info, **{'as_html': 0}))
+ tb_html = ''.join(
+ format_exception(*info, **{'as_html': 1}))
+ else:
+ tb_text = info[2]
+
+ url = None
+ username = None
+ req_html = None
+ if request:
+ # XXX: Temporary fix, which Steve should undo. URL is
+ # just too HTTPRequest-specific.
+ if hasattr(request, 'URL'):
+ url = request.URL
+ try:
+ # XXX: UnauthenticatedPrincipal does not have getLogin()
+ if hasattr(request.principal, 'getLogin'):
+ login = request.principal.getLogin()
+ else:
+ login = 'unauthenticated'
+ username = ', '.join(map(str, (login,
+ request.principal.id,
+ request.principal.title,
+ request.principal.description
+ )))
+ # When there's an unauthorized access, request.principal is
+ # not set, so we get an AttributeError
+ # XXX is this right? Surely request.principal should be set!
+ # XXX Answer: Catching AttributeError is correct for the
+ # simple reason that UnauthenticatedUser (which
+ # I always use during coding), has no 'getLogin()'
+ # method. However, for some reason this except
+ # does **NOT** catch these errors.
+ except AttributeError:
+ pass
+
+ req_html = ''.join(['%s : %s<br>' % item
+ for item in request.items()])
+ try:
+ strv = str(info[1])
+ # A call to str(obj) could raise anything at all.
+ # We'll ignore these errors, and print something
+ # useful instead, but also log the error.
+ except:
+ logging.getLogger('SiteError').exception(
+ 'Error in ErrorReportingUtility while getting a str '
+ 'representation of an object')
+ strv = '<unprintable %s object>' % (
+ str(type(info[1]).__name__)
+ )
+
+ log = self._getLog()
+ entry_id = str(now) + str(random()) # Low chance of collision
+
+ log.append({
+ 'type': strtype,
+ 'value': strv,
+ 'time': time.ctime(now),
+ 'id': entry_id,
+ 'tb_text': tb_text,
+ 'tb_html': tb_html,
+ 'username': username,
+ 'url': url,
+ 'req_html': req_html,
+ })
+ cleanup_lock.acquire()
+ try:
+ if len(log) >= self.keep_entries:
+ del log[:-self.keep_entries]
+ finally:
+ cleanup_lock.release()
+
+ if self.copy_to_zlog:
+ self._do_copy_to_zlog(now, strtype, str(url), info)
+ finally:
+ info = None
+
+ def _do_copy_to_zlog(self, now, strtype, url, info):
+ # XXX info is unused; logging.exception() will call sys.exc_info()
+ # work around this with an evil hack
+ when = _rate_restrict_pool.get(strtype,0)
+ if now > when:
+ next_when = max(when,
+ now - _rate_restrict_burst*_rate_restrict_period)
+ next_when += _rate_restrict_period
+ _rate_restrict_pool[strtype] = next_when
+ try:
+ raise info[0], info[1], info[2]
+ except:
+ logging.getLogger('SiteError').exception(str(url))
+
+ def getProperties(self):
+ return {
+ 'keep_entries': self.keep_entries,
+ 'copy_to_zlog': self.copy_to_zlog,
+ 'ignored_exceptions': self._ignored_exceptions,
+ }
+
+ def setProperties(self, keep_entries, copy_to_zlog=0,
+ ignored_exceptions=()):
+ """Sets the properties of this site error log.
+ """
+ copy_to_zlog = bool(copy_to_zlog)
+ self.keep_entries = int(keep_entries)
+ self.copy_to_zlog = copy_to_zlog
+ self._ignored_exceptions = tuple(
+ filter(None, map(str, ignored_exceptions))
+ )
+
+ def getLogEntries(self):
+ """Returns the entries in the log, most recent first.
+
+ Makes a copy to prevent changes.
+ """
+ res = [entry.copy() for entry in self._getLog()]
+ res.reverse()
+ return res
+
+ def getLogEntryById(self, id):
+ """Returns the specified log entry.
+ Makes a copy to prevent changes. Returns None if not found.
+ """
+ for entry in self._getLog():
+ if entry['id'] == id:
+ return entry.copy()
+ return None
+
+class RootErrorReportingUtility(ErrorReportingUtility):
+ rootId = 'root'
+
+ def _getLog(self):
+ """Returns the log for this object.
+
+ Careful, the log is shared between threads.
+ """
+ log = _temp_logs.get(self.rootId, None)
+ if log is None:
+ log = []
+ _temp_logs[self.rootId] = log
+ return log
+
+
+globalErrorReportingUtility = RootErrorReportingUtility()
+
+def _cleanup_temp_log():
+ _temp_logs.clear()
+
+_clear = _cleanup_temp_log
+
+# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
+from zope.testing.cleanup import addCleanUp
+addCleanUp(_clear)
+del addCleanUp
Modified: Zope3/trunk/src/zope/app/error/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/interfaces.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/interfaces.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Error Reporting Service interfaces
+"""Error Reporting Utility interfaces
$Id$
"""
@@ -19,18 +19,17 @@
from zope.interface import Interface
-class IErrorReportingService(Interface):
- """Error Reporting Service
- """
+class IErrorReportingUtility(Interface):
+ """Error Reporting Utility"""
def raising(info, request=None):
- """Logs an exception.
- """
+ """Logs an exception."""
-class ILocalErrorReportingService(Interface):
- """Local Error Reporting Service
- Included management functions
+class ILocalErrorReportingUtility(Interface):
+ """Local Error Reporting Utility
+
+ This interface contains additional management functions.
"""
def getProperties():
@@ -47,9 +46,15 @@
"""
def getLogEntries():
- """Returns the entries in the log, most recent first.
- """
+ """Returns the entries in the log, most recent first."""
def getLogEntryById(id):
- """Return LogEntry by ID
- """
+ """Return LogEntry by ID"""
+
+###############################################################################
+# BBB: 12/14/2004
+
+IErrorReportingService = IErrorReportingUtility
+ILocalErrorReportingService = ILocalErrorReportingUtility
+
+###############################################################################
Modified: Zope3/trunk/src/zope/app/error/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/errorservice/tests.py 2004-12-13 00:04:16 UTC (rev 28626)
+++ Zope3/trunk/src/zope/app/error/tests.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -11,18 +11,20 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Error Reporting Service Tests
+"""Error Reporting Utility Tests
$Id$
"""
import sys
-from unittest import TestCase, TestLoader, TextTestRunner
-from zope.app.errorservice import ErrorReportingService
-from zope.testing.cleanup import CleanUp
+import unittest
+
from zope.exceptions.exceptionformatter import format_exception
from zope.publisher.tests.httprequest import TestRequest
+from zope.app.tests.placelesssetup import PlacelessSetup
+from zope.app.error.error import ErrorReportingUtility
+
class C1(object):
def getAnErrorInfo(self):
exc_info = None
@@ -33,39 +35,40 @@
return exc_info
-class ErrorReportingServiceTests(CleanUp, TestCase):
+class ErrorReportingUtilityTests(PlacelessSetup, unittest.TestCase):
+
def test_checkForEmpryLog(self):
# Test Check Empty Log
- errService = ErrorReportingService()
- getProp = errService.getLogEntries()
+ errUtility = ErrorReportingUtility()
+ getProp = errUtility.getLogEntries()
self.failIf(getProp)
def test_checkProperties(self):
# Test Properties test
- errService = ErrorReportingService()
+ errUtility = ErrorReportingUtility()
setProp = {
'keep_entries':10,
'copy_to_zlog':1,
'ignored_exceptions':()
}
- errService.setProperties(**setProp)
- getProp = errService.getProperties()
+ errUtility.setProperties(**setProp)
+ getProp = errUtility.getProperties()
self.assertEqual(setProp, getProp)
def test_ErrorLog(self):
# Test for Logging Error. Create one error and check whether its
# logged or not.
- errService = ErrorReportingService()
+ errUtility = ErrorReportingUtility()
exc_info = C1().getAnErrorInfo()
- errService.raising(exc_info)
- getErrLog = errService.getLogEntries()
+ errUtility.raising(exc_info)
+ getErrLog = errUtility.getLogEntries()
self.assertEquals(1, len(getErrLog))
tb_text = ''.join(format_exception(*exc_info, **{'as_html': 0}))
err_id = getErrLog[0]['id']
self.assertEquals(tb_text,
- errService.getLogEntryById(err_id)['tb_text'])
+ errUtility.getLogEntryById(err_id)['tb_text'])
def test_ErrorLog_Unicode_urls(self):
# Emulate a unicode url, it gets encoded to utf-8 before it's passed
@@ -73,22 +76,23 @@
# environment
request = TestRequest(environ={'PATH_INFO': '/\xd1\x82',
'SOME_UNICODE': u'\u0441'})
- errService = ErrorReportingService()
+ errUtility = ErrorReportingUtility()
exc_info = C1().getAnErrorInfo()
- errService.raising(exc_info, request=request)
- getErrLog = errService.getLogEntries()
+ errUtility.raising(exc_info, request=request)
+ getErrLog = errUtility.getLogEntries()
self.assertEquals(1, len(getErrLog))
tb_text = ''.join(format_exception(*exc_info, **{'as_html': 0}))
err_id = getErrLog[0]['id']
self.assertEquals(tb_text,
- errService.getLogEntryById(err_id)['tb_text'])
+ errUtility.getLogEntryById(err_id)['tb_text'])
def test_suite():
- loader=TestLoader()
- return loader.loadTestsFromTestCase(ErrorReportingServiceTests)
+ return unittest.TestSuite((
+ unittest.makeSuite(ErrorReportingUtilityTests),
+ ))
-if __name__=='__main__':
- TextTestRunner().run(test_suite())
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Modified: Zope3/trunk/src/zope/app/publication/tests/test_zopepublication.py
===================================================================
--- Zope3/trunk/src/zope/app/publication/tests/test_zopepublication.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/publication/tests/test_zopepublication.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -41,8 +41,8 @@
from zope.app.tests import setup
from zope.app.tests import ztapi
-from zope.app.errorservice.interfaces import IErrorReportingService
-from zope.app.servicenames import ErrorLogging, Authentication
+from zope.app.error.interfaces import IErrorReportingUtility
+from zope.app.servicenames import Authentication
from zope.app.location.interfaces import ILocation
from zope.app.traversing.interfaces import IPhysicallyLocatable
from zope.app.security.principalregistry import principalRegistry
@@ -83,8 +83,8 @@
def getPrincipal(self, id):
return Principal(id)
-class ErrorLoggingService(object):
- implements(IErrorReportingService)
+class ErrorReportingUtility(object):
+ implements(IErrorReportingUtility)
def __init__(self):
self.exceptions = []
@@ -223,7 +223,7 @@
self.assertEqual(
str(handler),
'SiteError ERROR\n'
- ' Error while reporting an error to the ErrorLogging service')
+ ' Error while reporting an error to the Error Reporting utility')
# Here we got a single log record, because we havdn't
# installed an error reporting service. That's OK.
@@ -272,7 +272,7 @@
self.assertEqual(
str(handler),
'SiteError ERROR\n'
- ' Error while reporting an error to the ErrorLogging service\n'
+ ' Error while reporting an error to the Error Reporting utility\n'
'SiteError ERROR\n'
' http://test.url'
)
@@ -367,11 +367,12 @@
# assert that we get a new transaction
self.assert_(txn is not get_transaction())
- def testAbortTransactionWithErrorLoggingService(self):
+ def testAbortTransactionWithErrorReportingUtility(self):
# provide our fake error logging service
sm = getGlobalServices()
- sm.defineService(ErrorLogging, IErrorReportingService)
- sm.provideService(ErrorLogging, ErrorLoggingService())
+ utils = sm.getService('Utilities')
+ utils.provideUtility(IErrorReportingUtility,
+ ErrorReportingUtility())
class FooError(Exception):
pass
@@ -389,13 +390,14 @@
self.assertEqual(last_txn_info, new_txn_info)
# instead, we expect a message in our logging service
- error_log = sm.getService(ErrorLogging)
+ error_log = utils.getUtility(IErrorReportingUtility)
self.assertEqual(len(error_log.exceptions), 1)
error_info, request = error_log.exceptions[0]
self.assertEqual(error_info[0], FooError)
self.assert_(isinstance(error_info[1], FooError))
self.assert_(request is self.request)
+
class ZopePublicationTests(BasePublicationTests):
def testPlacefulAuth(self):
Modified: Zope3/trunk/src/zope/app/publication/zopepublication.py
===================================================================
--- Zope3/trunk/src/zope/app/publication/zopepublication.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/publication/zopepublication.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -39,7 +39,8 @@
from zope.app.applicationcontrol.applicationcontrol \
import applicationControllerRoot
from zope.app.component.hooks import getSite
-from zope.app.errorservice import RootErrorReportingService
+from zope.app.error.error import RootErrorReportingUtility
+from zope.app.error.interfaces import IErrorReportingUtility
from zope.app.exception.interfaces import ISystemErrorView
from zope.app.location import LocationProxy
from zope.app.publication.interfaces import BeforeTraverseEvent
@@ -203,32 +204,31 @@
txn.setExtendedInfo('request_type', iface_dotted)
return txn
- def _logErrorWithErrorReportingService(self, object, request, exc_info):
- # Record the error with the ErrorReportingService
+ def _logErrorWithErrorReportingUtility(self, object, request, exc_info):
+ # Record the error with the ErrorReportingUtility
self.beginErrorHandlingTransaction(request, object,
- 'error reporting service')
+ 'error reporting utility')
try:
- errService = zapi.getService(zapi.servicenames.ErrorLogging)
+ errUtility = zapi.getUtility(IErrorReportingUtility)
- # It is important that an error in errService.raising
+ # It is important that an error in errUtility.raising
# does not propagate outside of here. Otherwise, nothing
# meaningful will be returned to the user.
#
- # The error reporting service should not be doing database
+ # The error reporting utility should not be doing database
# stuff, so we shouldn't get a conflict error.
# Even if we do, it is more important that we log this
# error, and proceed with the normal course of events.
# We should probably (somehow!) append to the standard
# error handling that this error occurred while using
- # the ErrorReportingService, and that it will be in
+ # the ErrorReportingUtility, and that it will be in
# the zope log.
- errService.raising(exc_info, request)
+ errUtility.raising(exc_info, request)
get_transaction().commit()
except:
tryToLogException(
- 'Error while reporting an error to the %s service' %
- zapi.servicenames.ErrorLogging)
+ 'Error while reporting an error to the Error Reporting utility')
get_transaction().abort()
def handleException(self, object, request, exc_info, retry_allowed=True):
@@ -247,8 +247,8 @@
# handling determine whether a retry is allowed or not?
# Assume not for now.
- # Record the error with the ErrorReportingService
- self._logErrorWithErrorReportingService(object, request, exc_info)
+ # Record the error with the ErrorReportingUtility
+ self._logErrorWithErrorReportingUtility(object, request, exc_info)
response = request.response
response.reset()
@@ -331,8 +331,8 @@
tryToLogException(
'Exception while rendering view on exception')
- # Record the error with the ErrorReportingService
- self._logErrorWithErrorReportingService(
+ # Record the error with the ErrorReportingUtility
+ self._logErrorWithErrorReportingUtility(
object, request, sys.exc_info())
view = None
Modified: Zope3/trunk/src/zope/app/servicenames.py
===================================================================
--- Zope3/trunk/src/zope/app/servicenames.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/servicenames.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -1,4 +1,3 @@
-
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
Modified: Zope3/trunk/src/zope/app/zopeappgenerations/evolve1.py
===================================================================
--- Zope3/trunk/src/zope/app/zopeappgenerations/evolve1.py 2004-12-15 23:33:56 UTC (rev 28628)
+++ Zope3/trunk/src/zope/app/zopeappgenerations/evolve1.py 2004-12-15 23:52:41 UTC (rev 28629)
@@ -16,22 +16,64 @@
$Id$
"""
__docformat__ = "reStructuredText"
-from zope.app.zopeappgenerations import getRootFolder
+from zope.app import zapi
+from zope.app.error.error import ErrorReportingUtility
+from zope.app.error.interfaces import IErrorReportingService
+from zope.app.error.interfaces import IErrorReportingUtility
from zope.app.generations.utility import findObjectsProviding
from zope.app.registration.interfaces import IComponentRegistration
-from zope.app.site.interfaces import ISite
+from zope.app.registration.interfaces import ActiveStatus, UnregisteredStatus
+from zope.app.site.interfaces import ISite, IServiceRegistration
+from zope.app.utility import UtilityRegistration
+from zope.app.zopeappgenerations import getRootFolder
generation = 1
def evolve(context):
"""Evolve the ZODB from a Zope X3.0 to a X3.1 compatible format.
+ - The Error Reporting Service was replaced by the Error Reporting
+ Utility. Thus, all service registrations have to be changed to utility
+ registrations.
+
- Component-based registrations used to keep track of their components via
the component's path. Now it stores the component directly. All
registrations are updated to this new format.
"""
root = getRootFolder(context)
+ # Fix up Error Reporting Service --> Utility
+ # We do this by simply removing old Error Reporting Services and their
+ # registrations and then add a new error reporting utility.
+ for site in findObjectsProviding(root, ISite):
+ for reg in findObjectsProviding(site.getSiteManager(),
+ IServiceRegistration):
+
+ if reg.name == 'ErrorLogging':
+ errors = reg.component
+ # Set the registration to unregistered and then delete it
+ reg.status = UnregisteredStatus
+ del zapi.getParent(reg)[zapi.name(reg)]
+ # Get the properties from the old error reporting service and
+ # delete it
+ props = errors.getProperties()
+ folder = zapi.getParent(errors)
+ del folder[zapi.name(errors)]
+
+ # Only add a new error reporting utility, if there is none.
+ if 'ErrorReporting' not in folder:
+ # Create the error reporting utility and set its properties
+ utility = ErrorReportingUtility()
+ utility.setProperties(**props)
+ folder['ErrorReporting'] = utility
+ # Register the utility and set the registration active
+ reg = UtilityRegistration('', IErrorReportingUtility,
+ utility)
+ reg_manager = folder.getRegistrationManager()
+ key = reg_manager.addRegistration(reg)
+ reg_manager[key].status = ActiveStatus
+
+
# Fix up registration `componentPath` --> `component`
sites = findObjectsProviding(root, ISite)
for site in sites:
More information about the Zope3-Checkins
mailing list