1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
# -*- coding: utf-8 -*-
# docs/COPYING 2a + DRY: https://github.com/getmail6/getmail6
# Please refer to the git history regarding who changed what and when in this file.
'''Logging support for getmail.
The new standard Python library module logging didn't cut it for me; it doesn't
seem capable of handling some very simple requirements like logging messages of
a certain level to one fd, and other messages of higher levels to a different fd
(i.e. info to stdout, warnings to stderr).
'''
import sys
import os.path
import traceback
from getmailcore.constants import *
__all__ = [
'Logger',
]
#######################################
class _Logger(object):
'''Class for logging. Do not instantiate directly; use Logger() instead,
to keep this a singleton.
'''
def __init__(self):
'''Create a logger.'''
self.handlers = []
self.newline = False
def __call__(self):
return self
def addhandler(self, stream, minlevel, maxlevel=CRITICAL):
'''Add a handler for logged messages.
Logged messages of at least level <minlevel> (and at most level
<maxlevel>, default CRITICAL) will be output to <stream>.
If no handlers are specified, messages of all levels will be output to
stdout.
'''
self.handlers.append({'minlevel' : minlevel, 'stream' : stream,
'newline' : True, 'maxlevel' : maxlevel})
def clearhandlers(self):
'''Clear the list of handlers.
There should be a way to remove only one handler from a list. But that
would require an easy way for the caller to distinguish between them.
'''
self.handlers = []
def log(self, msglevel, msgtxt):
'''Log a message of level <msglevel> containing text <msgtxt>.'''
if isinstance(msgtxt,bytes):
msgtxt = msgtxt.decode()
for handler in self.handlers:
if msglevel < handler['minlevel'] or msglevel > handler['maxlevel']:
continue
if not handler['newline'] and msglevel == DEBUG:
handler['stream'].write('\n')
try:
handler['stream'].write(msgtxt)
except UnicodeError:
try:
handler['stream'].write(
msgtxt.encode('ascii',errors='backslashreplace').decode('ascii')
)
except AttributeError:
handler['stream'].write(msgtxt.decode('ascii',errors='backslashreplace'))
handler['stream'].flush()
if msgtxt.endswith('\n'):
handler['newline'] = True
else:
handler['newline'] = False
if not self.handlers:
if not self.newline and msglevel == DEBUG:
sys.stdout.write('\n')
sys.stdout.write(msgtxt)
sys.stdout.flush()
if msgtxt.endswith('\n'):
self.newline = True
else:
self.newline = False
def trace(self, msg='trace\n'):
'''Log a message with level TRACE.
The message will be prefixed with filename, line number, and function
name of the calling code.
'''
trace = traceback.extract_stack()[-2]
msg = '%s [%s:%i] %s' % (trace[FUNCNAME] + '()',
os.path.basename(trace[FILENAME]),
trace[LINENO],
msg
)
self.log(TRACE, msg)
def debug(self, msg):
'''Log a message with level DEBUG.'''
self.log(DEBUG, msg)
def moreinfo(self, msg):
'''Log a message with level MOREINFO.'''
self.log(MOREINFO, msg)
def info(self, msg):
'''Log a message with level INFO.'''
self.log(INFO, msg)
def warning(self, msg):
'''Log a message with level WARNING.'''
self.log(WARNING, msg)
def error(self, msg):
'''Log a message with level ERROR.'''
self.log(ERROR, msg)
def critical(self, msg):
'''Log a message with level CRITICAL.'''
self.log(CRITICAL, msg)
# aliases
warn = warning
Logger = _Logger()
|