[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService - ErrorReportingService.py:1.4 IErrorReportingService.py:1.4 error.gif:1.2 error_service.gif:1.2 __init__.py:1.2 configure.zcml:1.2
Jim Fulton
jim@zope.com
Wed, 23 Oct 2002 12:00:50 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService
In directory cvs.zope.org:/tmp/cvs-serv8321/lib/python/Zope/App/OFS/Services/ErrorReportingService
Modified Files:
__init__.py configure.zcml
Added Files:
ErrorReportingService.py IErrorReportingService.py error.gif
error_service.gif
Log Message:
Merging in the work done by Naveen and Rakesh on the
ErrorReportingService-branch branch.
There is now an error reporting service that works like the Zope 2.6
site error log. Yay!
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/ErrorReportingService.py 1.3 => 1.4 ===
--- /dev/null Wed Oct 23 12:00:50 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/ErrorReportingService.py Wed Oct 23 12:00:19 2002
@@ -0,0 +1,212 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+import os
+import sys
+import time
+from random import random
+from datetime import datetime
+from thread import allocate_lock
+from Persistence import Persistent
+from types import StringType, UnicodeType
+from zLOG import LOG, ERROR
+from Zope.Exceptions.ExceptionFormatter import format_exception
+from Zope.ContextWrapper import ContextMethod
+from Zope.App.ComponentArchitecture.NextService import getNextService
+from Zope.App.OFS.Services.ErrorReportingService.IErrorReportingService import IErrorReportingService
+
+#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):
+ """Error Reporting Service
+ """
+ __implements__ = IErrorReportingService
+
+ 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 _makestr(self, dictionary):
+ retVal = ''
+ for each in dictionary.keys():
+ retVal = retVal +str(each)+' : '+str(dictionary[each])+'<br> '
+ return retVal
+
+ def raising(self, info, request=None):
+ """Log an exception.
+ Called by ZopePublication.handleException method.
+ """
+ now = time.time()
+ try:
+ 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], StringType) and not isinstance(
+ info[2], UnicodeType):
+ 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:
+ url = request.URL
+ username = request.user.getLogin()+','+\
+ request.user.getId()+','+request.user.getTitle()+','+request.user.getDescription()
+ try:
+ req_html = self._makestr(request)
+ except:
+ pass
+
+ try:
+ strv = str(info[1])
+ except:
+ 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()
+ except:
+ LOG('SiteError', ERROR, 'Error while logging', error=sys.exc_info())
+ else:
+ if self.copy_to_zlog:
+ self._do_copy_to_zlog(now, strtype, str(url), info)
+ finally:
+ info = None
+ raising = ContextMethod(raising)
+
+ def _do_copy_to_zlog(self, now, strtype, url, info):
+ 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
+ LOG('SiteError', ERROR, str(url), error=info)
+
+ def getProperties(self):
+ return {
+ 'keep_entries': self.keep_entries,
+ 'copy_to_zlog': self.copy_to_zlog,
+ 'ignored_exceptions': self._ignored_exceptions,
+ }
+ getProperties = ContextMethod(getProperties)
+
+ def setProperties(self, keep_entries, copy_to_zlog=0,
+ ignored_exceptions=()):
+ """Sets the properties of this site error log.
+ """
+ copy_to_zlog = not not 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)))
+
+ setProperties = ContextMethod(setProperties)
+
+ def getLogEntries(self):
+ """Returns the entries in the log, most recent first.
+
+ Makes a copy to prevent changes.
+ """
+ # List incomprehension ;-)
+ res = [entry.copy() for entry in self._getLog()]
+ res.reverse()
+ return res
+
+ getLogEntries = ContextMethod(getLogEntries)
+
+ 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
+
+ getLogEntryById = ContextMethod(getLogEntryById)
+
+
+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
+
+
+
+
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/IErrorReportingService.py 1.3 => 1.4 ===
--- /dev/null Wed Oct 23 12:00:50 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/IErrorReportingService.py Wed Oct 23 12:00:19 2002
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+from Interface import Interface
+
+class IErrorReportingService(Interface):
+ """Error Reporting Service Interface.
+ """
+
+ def raising(info, request=None):
+ """Logs an exception.
+ """
+
+ def getProperties():
+ """Gets the properties as dictionary.
+ keep_entries, copy_to_logfile, ignored_exceptions
+ """
+
+ def setProperties(keep_entries, copy_to_zlog=0, ignored_exceptions=(), RESPONSE=None):
+ """Sets the properties. keep_entries, copy_to_logfile, ignored_exceptions
+ """
+
+ def getLogEntries():
+ """Returns the entries in the log, most recent first.
+ """
+
+ def getLogEntryById(id):
+ """Return LogEntry by ID
+ """
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/error.gif 1.1 => 1.2 ===
<Binary-ish file>
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/error_service.gif 1.1 => 1.2 ===
<Binary-ish file>
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/__init__.py 1.1 => 1.2 ===
=== Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/configure.zcml 1.1 => 1.2 ===
--- Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/configure.zcml:1.1 Thu Oct 17 13:07:26 2002
+++ Zope3/lib/python/Zope/App/OFS/Services/ErrorReportingService/configure.zcml Wed Oct 23 12:00:19 2002
@@ -1,7 +1,29 @@
<zopeConfigure
- xmlns='http://namespaces.zope.org/zope'>
+ xmlns='http://namespaces.zope.org/zope'
+ xmlns:browser='http://namespaces.zope.org/browser'
+ xmlns:service='http://namespaces.zope.org/service'
+>
+ <serviceType
+ id="ErrorReportingService"
+ interface=".IErrorReportingService." />
-<serviceType id="ErrorReporting" interface=".IErrorReportingService." />
+ <content class='.ErrorReportingService.'>
+ <require
+ permission="Zope.Public"
+ interface=".IErrorReportingService." />
+ <factory
+ id='ErrorReportingService'
+ permission='Zope.Public' />
+ </content>
+ <browser:menuItem menu="add_component" for="Zope.App.OFS.Container.IAdding."
+ action="ErrorReportingService" title='Error Reporting Service'
+ description='Error Reporting Service for Logging Errors' />
+
+ <browser:icon name="zmi_icon"
+ for=".IErrorReportingService."
+ file="error_service.gif" />
+
+ <include package=".Views" />
</zopeConfigure>