Updated to make the formatting strings variable.

This commit is contained in:
Jeremy Fincher 2004-09-22 22:27:03 +00:00
parent fc8da1888c
commit 217799ad1c

View File

@ -57,6 +57,7 @@ deadlyExceptions = [KeyboardInterrupt, SystemExit]
testing = False testing = False
class Formatter(logging.Formatter): class Formatter(logging.Formatter):
_fmtConf = staticmethod(lambda : conf.supybot.log.format())
def formatTime(self, record, datefmt=None): def formatTime(self, record, datefmt=None):
return timestamp(record.created) return timestamp(record.created)
@ -66,6 +67,14 @@ class Formatter(logging.Formatter):
raise raise
return logging.Formatter.formatException(self, (E, e, tb)) return logging.Formatter.formatException(self, (E, e, tb))
def format(self, record):
self._fmt = self._fmtConf()
return logging.Formatter.format(self, record)
class PluginFormatter(Formatter):
_fmtConf = staticmethod(lambda : conf.supybot.log.plugins.format())
class Logger(logging.Logger): class Logger(logging.Logger):
def exception(self, *args): def exception(self, *args):
@ -97,7 +106,7 @@ class StdoutStreamHandler(logging.StreamHandler):
if record.levelname != 'ERROR' and conf.supybot.log.stdout.wrap(): if record.levelname != 'ERROR' and conf.supybot.log.stdout.wrap():
# We check for ERROR there because otherwise, tracebacks (which are # We check for ERROR there because otherwise, tracebacks (which are
# already wrapped by Python itself) wrap oddly. # already wrapped by Python itself) wrap oddly.
prefixLen = len(record.name) + 2 # ": " prefixLen = len(record.levelname) + 1 # ' '
s = textwrap.fill(s, width=78, subsequent_indent=' '*prefixLen) s = textwrap.fill(s, width=78, subsequent_indent=' '*prefixLen)
s.rstrip('\r\n') s.rstrip('\r\n')
return s return s
@ -129,6 +138,9 @@ class BetterFileHandler(logging.FileHandler):
class ColorizedFormatter(Formatter): class ColorizedFormatter(Formatter):
# This was necessary because these variables aren't defined until later.
# The staticmethod is necessary because they get treated like methods.
_fmtConf = staticmethod(lambda : conf.supybot.log.stdout.format())
def formatException(self, (E, e, tb)): def formatException(self, (E, e, tb)):
if conf.supybot.log.stdout.colorized(): if conf.supybot.log.stdout.colorized():
return ''.join([ansi.RED, return ''.join([ansi.RED,
@ -156,13 +168,100 @@ class ColorizedFormatter(Formatter):
return Formatter.format(self, record, *args, **kwargs) return Formatter.format(self, record, *args, **kwargs)
# These are public. # These are public.
formatter = Formatter('%(levelname)s %(asctime)s %(message)s') formatter = Formatter('NEVER SEEN')
pluginFormatter = Formatter('%(levelname)s %(asctime)s %(name)s %(message)s') pluginFormatter = PluginFormatter('NEVER SEEN')
# These are not. # These are not.
logging.setLoggerClass(Logger) logging.setLoggerClass(Logger)
_logger = logging.getLogger('supybot') _logger = logging.getLogger('supybot')
class ValidLogLevel(registry.String):
"""Invalid log level."""
minimumLevel = -1
def set(self, s):
s = s.upper()
try:
level = getattr(logging, s)
except AttributeError:
try:
level = int(s)
except ValueError:
self.error()
if level < self.minimumLevel:
self.error()
self.setValue(level)
def __str__(self):
level = logging.getLevelName(self.value)
if level.startswith('Level'):
level = level.split()[-1]
return level
class LogLevel(ValidLogLevel):
"""Invalid log level. Value must be either DEBUG, INFO, WARNING, ERROR,
or CRITICAL."""
def setValue(self, v):
ValidLogLevel.setValue(self, v)
_logger.setLevel(self.value) # _logger defined later.
conf.registerGlobalValue(conf.supybot.directories, 'log',
conf.Directory('logs', """Determines what directory the bot will store its
logfiles in."""))
conf.registerGroup(conf.supybot, 'log')
conf.registerGlobalValue(conf.supybot.log, 'format',
registry.String('%(levelname)s %(asctime)s %(name)s %(message)s',
"""Determines what the bot's logging format will be. The relevant
documentation on the available formattings is Python's documentation on
its logging module."""))
conf.registerGlobalValue(conf.supybot.log, 'level',
LogLevel(logging.INFO, """Determines what the minimum priority level logged
will be. Valid values are DEBUG, INFO, WARNING, ERROR, and CRITICAL, in
order of increasing priority."""))
conf.registerGlobalValue(conf.supybot.log, 'statistics',
ValidLogLevel(logging.DEBUG, """Determines what level statistics reporting
is to be logged at. Mostly, this just includes, for instance, the time it
took to parse a message, process a command, etc. You probably don't care
about this."""))
conf.registerGlobalValue(conf.supybot.log, 'timestampFormat',
registry.String('[%d-%b-%Y %H:%M:%S]', """Determines the format string for
timestamps in logfiles. Refer to the Python documentation for the time
module to see what formats are accepted. If you set this variable to the
empty string, times will be logged in a simple seconds-since-epoch
format."""))
class BooleanRequiredFalseOnWindows(registry.Boolean):
def set(self, s):
registry.Boolean.set(self, s)
if self.value and os.name == 'nt':
raise InvalidRegistryValue, 'Value cannot be true on Windows.'
conf.registerGlobalValue(conf.supybot.log, 'stdout',
registry.Boolean(True, """Determines whether the bot will log to
stdout."""))
conf.registerGlobalValue(conf.supybot.log.stdout, 'colorized',
BooleanRequiredFalseOnWindows(False, """Determines whether the bot's logs
to stdout (if enabled) will be colorized with ANSI color."""))
conf.registerGlobalValue(conf.supybot.log.stdout, 'wrap',
registry.Boolean(True, """Determines whether the bot will wrap its logs
when they're output to stdout."""))
conf.registerGlobalValue(conf.supybot.log.stdout, 'format',
registry.String('%(levelname)s %(asctime)s %(message)s',
"""Determines what the bot's logging format will be. The relevant
documentation on the available formattings is Python's documentation on
its logging module."""))
conf.registerGroup(conf.supybot.log, 'plugins')
conf.registerGlobalValue(conf.supybot.log.plugins, 'individualLogfiles',
registry.Boolean(False, """Determines whether the bot will separate plugin
logs into their own individual logfiles."""))
conf.registerGlobalValue(conf.supybot.log.plugins, 'format',
registry.String('%(levelname)s %(asctime)s %(message)s',
"""Determines what the bot's logging format will be. The relevant
documentation on the available formattings is Python's documentation on
its logging module."""))
# These just make things easier. # These just make things easier.
debug = _logger.debug debug = _logger.debug
info = _logger.info info = _logger.info
@ -183,7 +282,7 @@ atexit.register(logging.shutdown)
ircutils.debug = debug ircutils.debug = debug
def getPluginLogger(name): def getPluginLogger(name):
if not conf.supybot.log.individualPluginLogfiles(): if not conf.supybot.log.plugins.individualLogfiles():
return _logger return _logger
log = logging.getLogger('supybot.plugins.%s' % name) log = logging.getLogger('supybot.plugins.%s' % name)
if not log.handlers: if not log.handlers:
@ -260,77 +359,6 @@ class MetaFirewall(type):
#return type.__new__(cls, name, bases, dict) #return type.__new__(cls, name, bases, dict)
class ValidLogLevel(registry.String):
"""Invalid log level."""
minimumLevel = -1
def set(self, s):
s = s.upper()
try:
level = getattr(logging, s)
except AttributeError:
try:
level = int(s)
except ValueError:
self.error()
if level < self.minimumLevel:
self.error()
self.setValue(level)
def __str__(self):
level = logging.getLevelName(self.value)
if level.startswith('Level'):
level = level.split()[-1]
return level
class LogLevel(ValidLogLevel):
"""Invalid log level. Value must be either DEBUG, INFO, WARNING, ERROR,
or CRITICAL."""
def setValue(self, v):
ValidLogLevel.setValue(self, v)
_logger.setLevel(self.value) # _logger defined later.
conf.registerGlobalValue(conf.supybot.directories, 'log',
conf.Directory('logs', """Determines what directory the bot will store its
logfiles in."""))
conf.registerGroup(conf.supybot, 'log')
conf.registerGlobalValue(conf.supybot.log, 'level',
LogLevel(logging.INFO, """Determines what the minimum priority level logged
will be. Valid values are DEBUG, INFO, WARNING, ERROR, and CRITICAL, in
order of increasing priority."""))
conf.registerGlobalValue(conf.supybot.log, 'statistics',
ValidLogLevel(logging.DEBUG, """Determines what level statistics reporting
is to be logged at. Mostly, this just includes, for instance, the time it
took to parse a message, process a command, etc. You probably don't care
about this."""))
conf.registerGlobalValue(conf.supybot.log, 'stdout',
registry.Boolean(True, """Determines whether the bot will log to
stdout."""))
conf.registerGlobalValue(conf.supybot.log, 'individualPluginLogfiles',
registry.Boolean(False, """Determines whether the bot will separate plugin
logs into their own individual logfiles."""))
class BooleanRequiredFalseOnWindows(registry.Boolean):
def set(self, s):
registry.Boolean.set(self, s)
if self.value and os.name == 'nt':
raise InvalidRegistryValue, 'Value cannot be true on Windows.'
conf.registerGlobalValue(conf.supybot.log.stdout, 'colorized',
BooleanRequiredFalseOnWindows(False, """Determines whether the bot's logs
to stdout (if enabled) will be colorized with ANSI color."""))
conf.registerGlobalValue(conf.supybot.log.stdout, 'wrap',
registry.Boolean(True, """Determines whether the bot will wrap its logs
when they're output to stdout."""))
conf.registerGlobalValue(conf.supybot.log, 'timestampFormat',
registry.String('[%d-%b-%Y %H:%M:%S]', """Determines the format string for
timestamps in logfiles. Refer to the Python documentation for the time
module to see what formats are accepted. If you set this variable to the
empty string, times will be logged in a simple seconds-since-epoch
format."""))
_logDir = conf.supybot.directories.log() _logDir = conf.supybot.directories.log()
if not os.path.exists(_logDir): if not os.path.exists(_logDir):
os.mkdir(_logDir, 0755) os.mkdir(_logDir, 0755)
@ -348,8 +376,7 @@ _logger.setLevel(conf.supybot.log.level())
if not conf.daemonized: if not conf.daemonized:
_stdoutHandler = StdoutStreamHandler(sys.stdout) _stdoutHandler = StdoutStreamHandler(sys.stdout)
_formatString = '%(name)s: %(levelname)s %(message)s' _stdoutFormatter = ColorizedFormatter('YOU SHOULD NEVER SEE THIS!')
_stdoutFormatter = ColorizedFormatter(_formatString)
_stdoutHandler.setFormatter(_stdoutFormatter) _stdoutHandler.setFormatter(_stdoutFormatter)
_stdoutHandler.setLevel(-1) _stdoutHandler.setLevel(-1)
_logger.addHandler(_stdoutHandler) _logger.addHandler(_stdoutHandler)