[Zope-Checkins] SVN: Zope/trunk/lib/python/Z further changes to ConflictError logging:

Chris Withers chris at simplistix.co.uk
Fri Dec 2 09:35:08 EST 2005


Log message for revision 40472:
  further changes to ConflictError logging:
  - all conflict errors are counted and logged at info, as they were before Florent's change
  - logging makes it clear where the conflict has been resolved and where it hasn't
  - errors seen by the user are rendered with standard_error_message and are sent to the error_log which will likely copy them to the event log, depending on the users setup.
  - also checking in a functional test for generating write conflict errors.
  
  (I'll be committing to CHANGES.TXT shortly, I just wanted to keep the merges simpler)

Changed:
  A   Zope/trunk/lib/python/ZPublisher/tests/generate_conflicts.py
  U   Zope/trunk/lib/python/Zope2/App/startup.py

-=-
Added: Zope/trunk/lib/python/ZPublisher/tests/generate_conflicts.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/tests/generate_conflicts.py	2005-12-02 14:34:07 UTC (rev 40471)
+++ Zope/trunk/lib/python/ZPublisher/tests/generate_conflicts.py	2005-12-02 14:35:08 UTC (rev 40472)
@@ -0,0 +1,91 @@
+## This script requires:
+## - python2.4
+## - Zope 3's zope.testbrowser package:
+##   http://www.zope.org/Members/benji_york/ZopeTestbrowser-0.9.0.tgz
+##
+## The just run:
+## $python2.4 generate_conflicts.py
+import base64
+import string
+import threading
+import urllib2
+
+from zope.testbrowser.browser import Browser
+
+# create our browser
+class AuthBrowser(Browser):
+
+    def addBasicAuth(self,username,password):        
+        self.addHeader(
+            'Authorization',
+            'Basic '+base64.encodestring(username+':'+password).strip()
+            )
+
+    def open(self,uri,include_server=True):
+        if include_server:
+            uri = server+uri
+        return Browser.open(self,uri)
+
+browser = AuthBrowser()
+
+# constants
+server = 'http://localhost:8080'
+# the following user must be able to view the management screens
+# and create file objects
+username = 'username'
+password = 'password'
+browser.addBasicAuth(username,password)
+threads = 10
+filename = 'conflict.txt'
+filesize = 10000
+hits = 5
+
+# delete the file if it's already there
+browser.open('/manage_main')
+if filename in [c.optionValue
+                for c in browser.getControl(name='ids:list').controls]:
+    browser.open('/manage_delObjects?ids:list='+filename)
+
+# create it
+browser.open('/manage_addFile?id='+filename)
+
+# edit it, hopefully causing conflicts
+data = 'X'*filesize
+class EditThread(threading.Thread):
+
+    def __init__(self,i):
+        self.conflicts = 0
+        self.browser = AuthBrowser()
+        self.browser.handleErrors = False
+        self.browser.addBasicAuth(username,password)
+        threading.Thread.__init__(self,name=str(i))
+        
+    def run(self):
+        for i in range(1,hits+1):            
+            self.browser.open('/conflict.txt/manage_main')
+            self.browser.getControl(name='title').value='Test Title'
+            self.browser.getControl(name='filedata:text').value = data
+            try:
+                self.browser.getControl(name='manage_edit:method').click()
+            except urllib2.HTTPError,e:
+                # print e.read()
+                self.conflicts += 1
+                print "Thread %s - CONFLICT" % self.getName()
+            else:
+                print "Thread %s - EDIT" % self.getName()
+
+thread_objects = []
+for i in range(1,threads+1):
+    t = EditThread(i)
+    thread_objects.append(t)
+    t.start()
+for t in thread_objects:
+    t.join()
+total = 0
+print
+for t in thread_objects:
+    print "Thread %s - %i conflicts seen" % (t.getName(),t.conflicts)
+    total += t.conflicts
+print
+print "%i conflicts seen by browsers" % total
+


Property changes on: Zope/trunk/lib/python/ZPublisher/tests/generate_conflicts.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope/trunk/lib/python/Zope2/App/startup.py
===================================================================
--- Zope/trunk/lib/python/Zope2/App/startup.py	2005-12-02 14:34:07 UTC (rev 40471)
+++ Zope/trunk/lib/python/Zope2/App/startup.py	2005-12-02 14:35:08 UTC (rev 40472)
@@ -18,9 +18,9 @@
 from AccessControl.SecurityManagement import noSecurityManager
 from Acquisition import aq_acquire
 from App.config import getConfiguration
+from time import asctime
 from types import StringType, ListType
 from zExceptions import Unauthorized
-from zLOG import LOG, ERROR, WARNING, INFO, BLATHER, log_time
 from ZODB.POSException import ConflictError
 import transaction
 import AccessControl.User
@@ -28,6 +28,7 @@
 import ExtensionClass
 import Globals
 import imp
+import logging
 import OFS.Application
 import os
 import sys
@@ -103,7 +104,7 @@
     noSecurityManager()
 
     global startup_time
-    startup_time = log_time()
+    startup_time = asctime()
 
     Zope2.zpublisher_transactions_manager = TransactionsManager()
     Zope2.zpublisher_exception_hook = zpublisher_exception_hook
@@ -132,8 +133,13 @@
     def __init__(self,r): self.REQUEST=r
 
 conflict_errors = 0
+unresolved_conflict_errors = 0
 
+conflict_logger = logging.getLogger('ZODB.Conflict')
+
 def zpublisher_exception_hook(published, REQUEST, t, v, traceback):
+    global unresolved_conflict_errors
+    global conflict_errors
     try:
         if isinstance(t, StringType):
             if t.lower() in ('unauthorized', 'redirect'):
@@ -142,25 +148,31 @@
             if t is SystemExit:
                 raise
             if issubclass(t, ConflictError):
-                global conflict_errors
                 conflict_errors = conflict_errors + 1
-                method_name = REQUEST.get('PATH_INFO', '')
-                LOG('ZODB', BLATHER, "%s at %s: %s"
-                    " (%s conflicts since startup at %s)"
-                    % (v.__class__.__name__, method_name, v,
-                       conflict_errors, startup_time),
-                    error=(t, v, traceback))
+                # This logs _all_ conflict errors
+                conflict_logger.info(
+                    '%s at %s (%i conflicts, of which %i'
+                    ' were unresolved, since startup at %s)',
+                    v,
+                    REQUEST.get('PATH_INFO', '<unknown>'),
+                    conflict_errors,
+                    unresolved_conflict_errors,
+                    startup_time
+                    )
+                # This debug logging really doesn't help a lot...
+                conflict_logger.debug('Conflict traceback',exc_info=True)
                 raise ZPublisher.Retry(t, v, traceback)
             if t is ZPublisher.Retry:
-                # An exception that can't be retried anymore
-                # Retrieve the original exception
-                try: v.reraise()
-                except: t, v, traceback = sys.exc_info()
-                # Log it as ERROR
-                method_name = REQUEST.get('PATH_INFO', '')
-                LOG('Publisher', ERROR, "Unhandled %s at %s: %s"
-                    % (v.__class__.__name__, method_name, v))
-                # Then fall through to display the error to the user
+                try:
+                    v.reraise()
+                except:
+                    # we catch the re-raised exception so that it gets
+                    # stored in the error log and gets rendered with
+                    # standard_error_message
+                    t, v, traceback = sys.exc_info()
+                if issubclass(t, ConflictError):
+                    # ouch, a user saw this conflict error :-(
+                    unresolved_conflict_errors += 1
 
         try:
             log = aq_acquire(published, '__error_log__', containment=1)



More information about the Zope-Checkins mailing list