95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
from logging import Handler, LogRecord
|
|
from time import time
|
|
|
|
class DedupLogHandler(Handler):
|
|
"""
|
|
This handler suppresses repeated log messages after a set
|
|
number of duplicates.
|
|
Can optionally be configured to forcibly un-suppress a single
|
|
duplicate message on a certain time interval, in order to get
|
|
periodic updates on suppressed messages.
|
|
"""
|
|
def __init__(self, target, max_repeats, report_interval=None):
|
|
"""
|
|
Initialize the handler with the target handler to forward
|
|
non-suppressed messages to, the threshold for suppressing
|
|
duplicates, and optionally the time interval on which to
|
|
report the suppression count.
|
|
"""
|
|
Handler.__init__(self)
|
|
self.target = target
|
|
self.max_repeats = max_repeats
|
|
self.report_interval = report_interval
|
|
self.last = None
|
|
self.dupcount = 0
|
|
self.supcount = 0
|
|
self.suppressed_last = False
|
|
self.last_emittime = time()
|
|
|
|
def emit(self, record):
|
|
"""
|
|
Emit a record.
|
|
|
|
A record is emitted if it has been repeated less than
|
|
`max_repeats` times in a row, or if it is time to provide
|
|
a status update according to `report_interval`.
|
|
|
|
Records are compared by passing them to `target.format()`
|
|
and comparing the results.
|
|
"""
|
|
identical = (
|
|
self.last
|
|
and self.target.format(self.last) == self.target.format(record))
|
|
if identical:
|
|
self.dupcount += 1
|
|
else:
|
|
self.dupcount = 0
|
|
|
|
should_suppress = self.dupcount >= self.max_repeats
|
|
|
|
now = time()
|
|
force_emit = False
|
|
if self.report_interval is not None:
|
|
force_emit_at = self.last_emittime + self.report_interval
|
|
force_emit = force_emit_at < now
|
|
|
|
if should_suppress and not force_emit:
|
|
self.suppressed_last = True
|
|
self.supcount += 1
|
|
return
|
|
|
|
if self.suppressed_last:
|
|
self.print_repeats()
|
|
|
|
self.last = record
|
|
self.suppressed_last = False
|
|
self.last_emittime = now
|
|
self.target.emit(record)
|
|
|
|
def print_repeats(self):
|
|
"""
|
|
Emit a log message indicating the number of suppressed records.
|
|
|
|
All attributes of this new record are derived from the latest
|
|
record, except the message and its arguments.
|
|
"""
|
|
msg = "Suppressed %s identical messages"
|
|
self.target.emit(LogRecord(self.last.name,
|
|
self.last.levelno,
|
|
self.last.pathname,
|
|
self.last.lineno,
|
|
msg,
|
|
(self.supcount,),
|
|
None))
|
|
self.supcount = 0
|
|
|
|
def close(self):
|
|
"""
|
|
Close this hander. If messages are currently being suppressed,
|
|
the number of suppressed messages is printed before chaining
|
|
to the parent class' `close()`.
|
|
"""
|
|
if self.suppressed_last:
|
|
self.print_repeats()
|
|
Handler.close(self)
|