Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/supervisor/loggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def getLevelNumByDescription(description):
num = getattr(LevelsByDescription, description, None)
return num

class Handler:
class Handler(object):
fmt = '%(message)s'
level = LevelsByName.INFO
def setFormat(self, fmt):
Expand Down Expand Up @@ -151,8 +151,15 @@ def getvalue(self):

def clear(self):
self.buf = ''





class RotatingFileHandler(FileHandler):

open_streams = {}

def __init__(self, filename, mode='a', maxBytes=512*1024*1024,
backupCount=10):
"""
Expand All @@ -177,12 +184,39 @@ def __init__(self, filename, mode='a', maxBytes=512*1024*1024,
"""
if maxBytes > 0:
mode = 'a' # doesn't make sense otherwise!
FileHandler.__init__(self, filename, mode)

self.mode = mode
self.baseFilename = filename
self.stream = self.stream or open(filename, mode)

self.maxBytes = maxBytes
self.backupCount = backupCount
self.counter = 0
self.every = 10

class stream(object):
"""
Descriptor for managing open filehandles so that only one
filehandle per file path ever receives logging.
"""
def __get__(self, obj, objtype):
"""
Return open filehandle or None
"""
return objtype.open_streams.get(obj.baseFilename)

def __set__(self, obj, stream):
"""
Set open filehandle for filename defined on the
RotatingFileHandler
"""
obj.open_streams[obj.baseFilename] = stream

stream = stream()

def close(self):
if self.stream: self.stream.close()

def emit(self, record):
"""
Emit a record.
Expand Down
33 changes: 33 additions & 0 deletions src/supervisor/tests/test_loggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,39 @@ def test_ctor(self):
self.assertEqual(handler.maxBytes, 512*1024*1024)
self.assertEqual(handler.backupCount, 10)

def test_emit_tracks_correct_file_for_multiple_handlers(self):
"""
When more than one process logs to a singlefile, we want to
make sure that files get rotated properly.

When the file rotates, all handlers should start writing to
the file specified by handler.baseFilename.
"""
handler1 = self._makeOne(self.filename, maxBytes=10, backupCount=2)
handler2 = self._makeOne(self.filename, maxBytes=10, backupCount=2)
record = self._makeLogRecord('a' * 4)
handler1.emit(record) #4 bytes
handler2.emit(record) #8 bytes
self.assertFalse(os.path.exists(self.filename + '.1'))
handler1.emit(record) #12 bytes
self.assertTrue(os.path.exists(self.filename + '.1'))
self.assertTrue(handler1.stream == handler2.stream)
new_record = self._makeLogRecord("NEW")
handler2.emit(new_record)
self.assertTrue(open(self.filename).read().endswith("NEW"))
handler1.emit(record)
self.assertTrue(open(self.filename).read().endswith("aaaa"))
handler2.emit(new_record)
self.assertTrue(open(self.filename).read().endswith(""))

def test_reopen_raises(self):
handler = self._makeOne(self.filename)
stream = DummyStream()
handler.baseFilename = os.path.join(self.basedir, 'notthere', 'a.log')
handler.open_streams[handler.baseFilename] = stream
self.assertRaises(IOError, handler.reopen)
self.assertEqual(stream.closed, True)

def test_emit_does_rollover(self):
handler = self._makeOne(self.filename, maxBytes=10, backupCount=2)
record = self._makeLogRecord('a' * 4)
Expand Down