mirror of https://github.com/fail2ban/fail2ban
LogCaptureTestCase: use almost non-blocking handling by getvalue/_is_logged (especially important in tests with waiting for logged via `assertLogged(..., wait=TO)`):
- try to acquire lock without blocking, if not possible - return cached/empty (max 5 times, otherwise do lock); - minimized time of the lock of messages list; - avoid sporadic dead-locking during cross lock together with lock within handling of self._strm.pull/2034/head
parent
5f3ba289d6
commit
f547a7c7b1
|
@ -640,7 +640,9 @@ class LogCaptureTestCase(unittest.TestCase):
|
||||||
def __init__(self, lazy=True):
|
def __init__(self, lazy=True):
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self._val = None
|
self._val = None
|
||||||
|
self._dirty = True
|
||||||
self._recs = list()
|
self._recs = list()
|
||||||
|
self._nolckCntr = 0
|
||||||
self._strm = StringIO()
|
self._strm = StringIO()
|
||||||
logging.Handler.__init__(self)
|
logging.Handler.__init__(self)
|
||||||
if lazy:
|
if lazy:
|
||||||
|
@ -650,10 +652,11 @@ class LogCaptureTestCase(unittest.TestCase):
|
||||||
"""Truncate the internal buffer and records."""
|
"""Truncate the internal buffer and records."""
|
||||||
if size:
|
if size:
|
||||||
raise Exception('invalid size argument: %r, should be None or 0' % size)
|
raise Exception('invalid size argument: %r, should be None or 0' % size)
|
||||||
|
self._val = None
|
||||||
|
self._dirty = True
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._strm.truncate(0)
|
|
||||||
self._val = None
|
|
||||||
self._recs = list()
|
self._recs = list()
|
||||||
|
self._strm.truncate(0)
|
||||||
|
|
||||||
def __write(self, record):
|
def __write(self, record):
|
||||||
msg = record.getMessage() + '\n'
|
msg = record.getMessage() + '\n'
|
||||||
|
@ -664,29 +667,42 @@ class LogCaptureTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def getvalue(self):
|
def getvalue(self):
|
||||||
"""Return current buffer as whole string."""
|
"""Return current buffer as whole string."""
|
||||||
with self._lock:
|
# if cached (still unchanged/no write operation), we don't need to enter lock:
|
||||||
# cached:
|
if not self._dirty:
|
||||||
if self._val is not None:
|
|
||||||
return self._val
|
|
||||||
# submit already emitted (delivered to handle) records:
|
|
||||||
for record in self._recs:
|
|
||||||
self.__write(record)
|
|
||||||
self._recs = list()
|
|
||||||
# cache and return:
|
|
||||||
self._val = self._strm.getvalue()
|
|
||||||
return self._val
|
return self._val
|
||||||
|
# try to lock, if not possible - return cached/empty (max 5 times):
|
||||||
|
lck = self._lock.acquire(False)
|
||||||
|
if not lck: # pargma: no cover (may be too sporadic on slow systems)
|
||||||
|
self._nolckCntr += 1
|
||||||
|
if self._nolckCntr <= 5:
|
||||||
|
return self._val if self._val is not None else ''
|
||||||
|
self._nolckCntr = 0
|
||||||
|
self._lock.acquire()
|
||||||
|
# minimize time of lock, avoid dead-locking during cross lock within self._strm ...
|
||||||
|
try:
|
||||||
|
recs = self._recs
|
||||||
|
self._recs = list()
|
||||||
|
finally:
|
||||||
|
self._lock.release()
|
||||||
|
# submit already emitted (delivered to handle) records:
|
||||||
|
for record in recs:
|
||||||
|
self.__write(record)
|
||||||
|
# cache and return:
|
||||||
|
self._val = self._strm.getvalue()
|
||||||
|
self._dirty = False
|
||||||
|
return self._val
|
||||||
|
|
||||||
def handle(self, record): # pragma: no cover
|
def handle(self, record): # pragma: no cover
|
||||||
"""Handle the specified record direct (not lazy)"""
|
"""Handle the specified record direct (not lazy)"""
|
||||||
with self._lock:
|
self.__write(record)
|
||||||
self._val = None
|
self._dirty = True
|
||||||
self.__write(record)
|
|
||||||
|
|
||||||
def _handle_lazy(self, record):
|
def _handle_lazy(self, record):
|
||||||
"""Lazy handle the specified record on demand"""
|
"""Lazy handle the specified record on demand"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._val = None
|
|
||||||
self._recs.append(record)
|
self._recs.append(record)
|
||||||
|
# logged - causes changed string buffer (signal by set _dirty):
|
||||||
|
self._dirty = True
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue