[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