[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