[Erp5-report] r38012 leonardo - in /erp5/trunk/utils/Products.LongRequestLogger/Products/Lo...

nobody at svn.erp5.org nobody at svn.erp5.org
Wed Aug 25 14:26:49 CEST 2010


Author: leonardo
Date: Wed Aug 25 14:26:47 2010
New Revision: 38012

URL: http://svn.erp5.org?rev=38012&view=rev
Log:
Add environment-variable based configurability

Modified:
    erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/__init__.py
    erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/dumper.py
    erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/monitor.py
    erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/tests/testLongRequestLogger.py

Modified: erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/__init__.py
URL: http://svn.erp5.org/erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/__init__.py?rev=38012&r1=38011&r2=38012&view=diff
==============================================================================
--- erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/__init__.py [utf8] (original)
+++ erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/__init__.py [utf8] Wed Aug 25 14:26:47 2010
@@ -4,10 +4,10 @@
 #                    Leonardo Rochael Almeida <leonardo at nexedi.com>
 #
 # WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
+# programmers who take the whole responsibility of assessing all potential
 # consequences resulting from its eventual inadequacies and bugs
 # End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly advised to contract a Free Software
+# guarantees and support are strongly advised to contract a Free Software
 # Service Company
 #
 # This program is Free Software; you can redistribute it and/or
@@ -28,6 +28,10 @@
 
 
 def initialize(context):
+    from Products.LongRequestLogger.dumper import do_enable
     from Products.LongRequestLogger.patch import do_patch
-    do_patch()
+    if do_enable():
+        # if not enabled on startup, it won't be enabled, period.
+        # it can be disabled at runtime afterwards, but will not be unpatched.
+        do_patch()
 

Modified: erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/dumper.py
URL: http://svn.erp5.org/erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/dumper.py?rev=38012&r1=38011&r2=38012&view=diff
==============================================================================
--- erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/dumper.py [utf8] (original)
+++ erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/dumper.py [utf8] Wed Aug 25 14:26:47 2010
@@ -4,10 +4,10 @@
 #                    Leonardo Rochael Almeida <leonardo at nexedi.com>
 #
 # WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
+# programmers who take the whole responsibility of assessing all potential
 # consequences resulting from its eventual inadequacies and bugs
 # End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly advised to contract a Free Software
+# guarantees and support are strongly advised to contract a Free Software
 # Service Company
 #
 # This program is Free Software; you can redistribute it and/or
@@ -26,9 +26,11 @@
 #
 ##############################################################################
 
+import os
+import os.path
 import traceback
+import logging
 from cStringIO import StringIO
-from logging import getLogger
 from thread import get_ident
 import time
 from pprint import pformat
@@ -39,10 +41,54 @@ except ImportError:
     # Python 2.4 and older
     from threadframe import dict as _current_frames
 
-# we might want to change this later to something more specific
-logger_name = __name__  
+class NullHandler(logging.Handler):
+    def __init__(self):
+        logging.Handler.__init__(self)
+        # for comparison purposes below
+        self.baseFilename = 'null'
+
+    def emit(self, *args, **kw):
+        pass
 
-log = getLogger(logger_name)
+# we might want to change this name later to something more specific
+logger_name = __name__  
+log = logging.getLogger(logger_name)
+log.propagate = False
+handler = NullHandler()
+log.addHandler(handler)
+
+DEFAULT_TIMEOUT = 2
+DEFAULT_INTERVAL = 1
+
+def do_enable():
+    global handler
+    # this function is not exactly threadsafe, but it shouldn't matter.
+    # The worse that can happen is that a change in longrequestlogger_file
+    # will interfere with an already running request and stop or change its
+    # logging destination.
+    logfile = os.environ.get('longrequestlogger_file')
+    if logfile:
+        if logfile != 'null':
+            # to immitate FileHandler
+            logfile = os.path.abspath(logfile)
+        if handler.baseFilename != logfile:
+            log.removeHandler(handler)
+            handler.close()
+            if logfile == 'null':
+                handler = NullHandler()
+            else:
+                handler = logging.FileHandler(logfile)
+            log.addHandler(handler)
+        return log # which is also True as boolean
+    return None # so that Dumpers know they are disabled
+
+def get_configuration():
+    return dict(
+        timeout=float(os.environ.get('longrequestlogger_timeout', 
+                                       DEFAULT_TIMEOUT)),
+        interval=float(os.environ.get('longrequestlogger_interval', 
+                                       DEFAULT_INTERVAL)),
+    )
 
 REQUEST_FORMAT = """
 request:
@@ -60,6 +106,14 @@ class Dumper(object):
             thread_id = get_ident()
         self.thread_id = thread_id
         self.start = time.time()
+        # capture it here in case it gets disabled in the future
+        self.log = do_enable()
+        conf = get_configuration()
+        self.timeout = conf['timeout']
+        self.interval = conf['interval']
+
+    def is_enabled(self):
+        return bool(self.log)
 
     def format_request(self, request):
         if request is None:
@@ -110,4 +164,4 @@ class Dumper(object):
         return output.getvalue()
 
     def __call__(self):
-        log.warning(self.format_thread())
+        self.log.warning(self.format_thread())

Modified: erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/monitor.py
URL: http://svn.erp5.org/erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/monitor.py?rev=38012&r1=38011&r2=38012&view=diff
==============================================================================
--- erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/monitor.py [utf8] (original)
+++ erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/monitor.py [utf8] Wed Aug 25 14:26:47 2010
@@ -4,10 +4,10 @@
 #                    Leonardo Rochael Almeida <leonardo at nexedi.com>
 #
 # WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
+# programmers who take the whole responsibility of assessing all potential
 # consequences resulting from its eventual inadequacies and bugs
 # End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly advised to contract a Free Software
+# guarantees and support are strongly advised to contract a Free Software
 # Service Company
 #
 # This program is Free Software; you can redistribute it and/or
@@ -28,10 +28,7 @@
 
 from threading import Thread
 from threading import Condition
-from Products.LongRequestLogger.dumper import Dumper
-
-DEFAULT_TIMEOUT = 2
-DEFAULT_INTERVAL = 1
+from Products.LongRequestLogger import dumper
 
 class Monitor(Thread):
     """Logs the stack-trace of a thread until it's stopped
@@ -46,18 +43,20 @@ class Monitor(Thread):
     Stop the monitoring, whether timed-out or not
     """
 
+    running = False
+
     def __init__(self,
                  thread_id=None,
-                 timeout=DEFAULT_TIMEOUT,
-                 interval=DEFAULT_INTERVAL):
+                 timeout=None,
+                 interval=None):
         Thread.__init__(self)
-        self.timeout = timeout
-        self.interval = interval
-        self.dumper = Dumper(thread_id)
-
-        self.running = True
+        self.dumper = dumper.Dumper(thread_id)
+        self.timeout = timeout or self.dumper.timeout
+        self.interval = interval or self.dumper.interval
         self.running_condition = Condition()
-        self.start()
+        if self.dumper.is_enabled():
+            self.running = True
+            self.start()
 
     def stop(self):
         """Stop monitoring the other thread"""
@@ -65,6 +64,8 @@ class Monitor(Thread):
         # being monitored
         self.running_condition.acquire()
         try:
+            if not self.running:
+                return # yes, the finally clause will be run, don't worry
             self.running = False
             self.running_condition.notify()
         finally:

Modified: erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/tests/testLongRequestLogger.py
URL: http://svn.erp5.org/erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/tests/testLongRequestLogger.py?rev=38012&r1=38011&r2=38012&view=diff
==============================================================================
--- erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/tests/testLongRequestLogger.py [utf8] (original)
+++ erp5/trunk/utils/Products.LongRequestLogger/Products/LongRequestLogger/tests/testLongRequestLogger.py [utf8] Wed Aug 25 14:26:47 2010
@@ -32,6 +32,7 @@ from doctest import OutputChecker
 from doctest import REPORT_UDIFF, NORMALIZE_WHITESPACE, ELLIPSIS
 
 from Products.LongRequestLogger.tests.common import Sleeper
+import os
 
 class SimpleOutputChecker(OutputChecker):
     # for certain inputs the doctest output checker is much more convenient
@@ -66,7 +67,7 @@ Products.LongRequestLogger.dumper WARNIN
 Traceback:
 ...
   File ".../LongRequestLogger/dumper.py", line ..., in __call__
-    log.warning(self.format_thread())
+    self.log.warning(self.format_thread())
   File ".../LongRequestLogger/dumper.py", line ..., in format_thread
     traceback.print_stack(frame, file=output)
   File ".../LongRequestLogger/dumper.py", line ..., in get_top_thread_frame
@@ -188,16 +189,45 @@ other: {'RESPONSE': HTTPResponse(''),
  'method': 'GET'}
 ''')
 
+config_env_variables = dict(
+    longrequestlogger_file='null',
+    longrequestlogger_timeout=None,
+    longrequestlogger_interval=None,
+)
+
 class TestLongRequestLogger(unittest.TestCase):
 
     def setUp(self):
         from Products.LongRequestLogger.patch import do_patch
         from Products.LongRequestLogger.dumper import logger_name
         from zope.testing.loggingsupport import InstalledHandler
+        self.setTestEnvironment()
         do_patch()
         self.loghandler = InstalledHandler(logger_name)
         self.requests = []
 
+    def tearDown(self):
+        from Products.LongRequestLogger.patch import do_unpatch
+        do_unpatch()
+        self.restoreTestEnvironment()
+        self.loghandler.uninstall()
+        for request in self.requests:
+            request.response.stdout.close()
+            request.clear()
+
+    def setTestEnvironment(self):
+        self.old_env = {}
+        for var, value in config_env_variables.items():
+            self.old_env[var] = os.environ.pop(var, None)
+            if value:
+                os.environ[var] = value
+
+    def restoreTestEnvironment(self):
+        for var, value in self.old_env.items():
+            os.environ.pop(var, None)
+            if value is not None:
+                os.environ[var] = value
+
     def makeRequest(self, path='/', **kw):
         # create fake request and response for convenience
         from ZPublisher.HTTPRequest import HTTPRequest
@@ -214,14 +244,6 @@ class TestLongRequestLogger(unittest.Tes
         self.requests.append(request)
         return request
 
-    def tearDown(self):
-        from Products.LongRequestLogger.patch import do_unpatch
-        do_unpatch()
-        self.loghandler.uninstall()
-        for request in self.requests:
-            request.response.stdout.close()
-            request.clear()
-
     def testDumperFormat(self):
         from Products.LongRequestLogger.dumper import Dumper
         dumper = Dumper()
@@ -256,6 +278,9 @@ class TestLongRequestLogger(unittest.Tes
     def testMonitorStopBeforeTimeout(self):
         from Products.LongRequestLogger.monitor import Monitor
         m = Monitor()
+        # sleep just a little to let the other thread start
+        s = Sleeper(0.01)
+        s.sleep()
         self.assertTrue(m.isAlive())
         self.assertTrue(m.running)
         m.stop()
@@ -279,12 +304,32 @@ class TestLongRequestLogger(unittest.Tes
         from Products.LongRequestLogger.monitor import Monitor
         m = Monitor()
         s = Sleeper(m.timeout + 2 * m.interval + 0.5) 
-        # sleep a little more than the timeout + intervals to be on the safe
+        # sleep a little more than timeout + intervals to be on the safe
         # side
         s.sleep()
         m.stop()
         check_monitor_2_intervals_log(str(self.loghandler))
 
+    def testMonitorDisabled(self):
+        from Products.LongRequestLogger.monitor import Monitor
+        os.environ['longrequestlogger_file'] = ''
+        m = Monitor()
+        s = Sleeper(m.timeout + 2 * m.interval + 0.5) 
+        # sleep a little more than timeout + intervals
+        s.sleep()
+        # the thread shouldn't run disabled
+        self.assertFalse(m.isAlive())
+        self.assertFalse(m.running)
+        # stopping shouldn't break nonetheless
+        m.stop()
+        self.assertFalse(m.running)
+        self.assertFalse(m.isAlive())
+        # and there should be no records
+        self.assertFalse(self.loghandler.records)
+
+    def testMonitorWithEnvorinmentConfigutation(self):
+        self.fail('TODO')
+
     def testIsPatched(self):
         import ZPublisher.Publish
         import Products.LongRequestLogger




More information about the Erp5-report mailing list