diff options
Diffstat (limited to 'deluge/log.py')
-rw-r--r-- | deluge/log.py | 228 |
1 files changed, 211 insertions, 17 deletions
diff --git a/deluge/log.py b/deluge/log.py index 327375e06..a0a4e1dbc 100644 --- a/deluge/log.py +++ b/deluge/log.py @@ -2,6 +2,7 @@ # log.py # # Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> +# Copyright (C) 2010 Pedro Algarvio <pedro@algarvio.me> # # Deluge is free software. # @@ -33,18 +34,100 @@ # # - """Logging functions""" +import os import logging +import inspect +import pkg_resources +from deluge import configmanager, common, component +from twisted.internet import defer +from twisted.python.log import PythonLoggingObserver + +__all__ = ["setupLogger", "setLoggerLevel", "getPluginLogger", "LOG"] + +LoggingLoggerClass = logging.getLoggerClass() + +if 'dev' in common.get_version(): + DEFAULT_LOGGING_FORMAT = "%%(asctime)s.%%(msecs)03.0f [%%(name)-%ds:%%(lineno)-4d][%%(levelname)-8s] %%(message)s" +else: + DEFAULT_LOGGING_FORMAT = "%%(asctime)s [%%(name)-%ds][%%(levelname)-8s] %%(message)s" +MAX_LOGGER_NAME_LENGTH = 3 + +class Logging(LoggingLoggerClass): + def __init__(self, logger_name): + LoggingLoggerClass.__init__(self, logger_name) + + # This makes module name padding increase to the biggest module name + # so that logs keep readability. + global MAX_LOGGER_NAME_LENGTH + if len(logger_name) > MAX_LOGGER_NAME_LENGTH: + MAX_LOGGER_NAME_LENGTH = len(logger_name) + for handler in logging.getLogger().handlers: + handler.setFormatter(logging.Formatter( + DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, + datefmt="%H:%M:%S" + )) + + @defer.inlineCallbacks + def garbage(self, msg, *args, **kwargs): + yield LoggingLoggerClass.log(self, 1, msg, *args, **kwargs) + + @defer.inlineCallbacks + def trace(self, msg, *args, **kwargs): + yield LoggingLoggerClass.log(self, 5, msg, *args, **kwargs) + + @defer.inlineCallbacks + def debug(self, msg, *args, **kwargs): + yield LoggingLoggerClass.debug(self, msg, *args, **kwargs) + + @defer.inlineCallbacks + def info(self, msg, *args, **kwargs): + yield LoggingLoggerClass.info(self, msg, *args, **kwargs) + + @defer.inlineCallbacks + def warning(self, msg, *args, **kwargs): + yield LoggingLoggerClass.warning(self, msg, *args, **kwargs) + + warn = warning + + @defer.inlineCallbacks + def error(self, msg, *args, **kwargs): + yield LoggingLoggerClass.error(self, msg, *args, **kwargs) + + @defer.inlineCallbacks + def critical(self, msg, *args, **kwargs): + yield LoggingLoggerClass.critical(self, msg, *args, **kwargs) + + @defer.inlineCallbacks + def exception(self, msg, *args, **kwargs): + yield LoggingLoggerClass.exception(self, msg, *args, **kwargs) + + def findCaller(self): + f = logging.currentframe().f_back + rv = "(unknown file)", 0, "(unknown function)" + while hasattr(f, "f_code"): + co = f.f_code + filename = os.path.normcase(co.co_filename) + if filename in (__file__.replace('.pyc', '.py'), + defer.__file__.replace('.pyc', '.py')): + f = f.f_back + continue + rv = (filename, f.f_lineno, co.co_name) + break + return rv levels = { "info": logging.INFO, "warning": logging.WARNING, "error": logging.ERROR, "none": logging.CRITICAL, - "debug": logging.DEBUG + "debug": logging.DEBUG, + "trace": 5, + "garbage": 1 } + + def setupLogger(level="error", filename=None, filemode="w"): """ Sets up the basic logger and if `:param:filename` is set, then it will log @@ -53,30 +136,141 @@ def setupLogger(level="error", filename=None, filemode="w"): :param level: str, the level to log :param filename: str, the file to log to """ + import logging + + if logging.getLoggerClass() is not Logging: + logging.setLoggerClass(Logging) + + level = levels.get(level, "error") - if not level or level not in levels: - level = "error" + rootLogger = logging.getLogger() - logging.basicConfig( - level=levels[level], - format="[%(levelname)-8s] %(asctime)s %(module)s:%(lineno)d %(message)s", - datefmt="%H:%M:%S", - filename=filename, - filemode=filemode + if filename and filemode=='a': + import logging.handlers + handler = logging.handlers.RotatingFileHandler( + filename, filemode, + maxBytes=5*1024*1024, # 5 Mb + backupCount=3, + encoding='utf-8', + delay=0 + ) + elif filename and filemode=='w': + handler = logging.FileHandler(filename, filemode, 'utf-8', delay=0) + else: + handler = logging.StreamHandler() + handler.setLevel(level) + + formatter = logging.Formatter( + DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH, + datefmt="%H:%M:%S" ) -def setLoggerLevel(level): + handler.setFormatter(formatter) + rootLogger.addHandler(handler) + rootLogger.setLevel(level) + + twisted_logging = PythonLoggingObserver('twisted') + twisted_logging.start() + logging.getLogger("twisted").setLevel(level) + +def tweak_logging_levels(): + """This function allows tweaking the logging levels for all or some loggers. + This is mostly usefull for developing purposes hence the contents of the + file are NOT like regular deluge config file's. + + To use is, create a file named "logging.conf" on your Deluge's config dir + with contents like for example: + deluge:warn + deluge.core:debug + deluge.plugin:error + + What the above mean is the logger "deluge" will be set to the WARN level, + the "deluge.core" logger will be set to the DEBUG level and the + "deluge.plugin" will be set to the ERROR level. + + Remember, one rule per line and this WILL override the setting passed from + the command line. + """ + logging_config_file = os.path.join(configmanager.get_config_dir(), + 'logging.conf') + if not os.path.isfile(logging_config_file): + return + log = logging.getLogger(__name__) + log.warn("logging.conf found! tweaking logging levels from %s", + logging_config_file) + for line in open(logging_config_file, 'r'): + if line.strip().startswith("#"): + continue + name, level = line.strip().split(':') + if level in levels: + log.warn("Setting logger \"%s\" to logging level \"%s\"", name, level) + logging.getLogger(name).setLevel(levels.get(level)) + + +def setLoggerLevel(level, logger_name=None): """ Sets the logger level. :param level: str, a string representing the desired level + :param logger_name: str, a string representing desired logger name for which + the level should change. The default is "None" will will + tweak the root logger level. """ - if level not in levels: - return + logging.getLogger(logger_name).setLevel(levels.get(level, "error")) + + +def getPluginLogger(logger_name): + return logging.getLogger("deluge.plugin.%s" % logger_name) + + +DEPRECATION_WARNING = """You seem to be using old style logging on your code, ie: + from deluge.log import LOG as log + +This has been deprecated in favour of an enhanced logging system and "LOG" will +be removed on the next major version release of Deluge, meaning, code will break, +specially plugins. +If you're seeing this message and you're not the developer of the plugin which +triggered this warning, please report to it's author. +If you're the developer, please stop using the above code and instead use: + + from deluge.log import getPluginLogger + log = getPluginLogger(__name__) + + +The above will result in, regarding the "Label" plugin for example a log message similar to: + 15:33:54 [deluge.plugin.label.core:78 ][INFO ] *** Start Label plugin *** + +If you wish not to have 'deluge.plugin' on the log message you can then instead use: + + import logging + log = logging.getLogger(__name__) + +The above will result in, regarding the "Label" plugin for example a log message similar to: + 15:33:54 [label.core:78 ][INFO ] *** Start Label plugin *** + +Triggering code:""" - global LOG - LOG.setLevel(levels[level]) +class __BackwardsCompatibleLOG(object): + def __getattribute__(self, name): + import warnings + logger_name = 'deluge' + stack = inspect.stack() + module_stack = stack.pop(1) + caller_module = inspect.getmodule(module_stack[0]) + warnings.warn_explicit(DEPRECATION_WARNING, DeprecationWarning, + module_stack[1], module_stack[2], + caller_module.__name__) + for member in stack: + module = inspect.getmodule(member[0]) + if not module: + continue + if module.__name__ in ('deluge.plugins.pluginbase', + 'deluge.plugins.init'): + logger_name += '.plugin.%s' % caller_module.__name__ + # Monkey Patch The Plugin Module + caller_module.log = logging.getLogger(logger_name) + break + return getattr(logging.getLogger(logger_name), name) -# Get the logger -LOG = logging.getLogger("deluge") +LOG = __BackwardsCompatibleLOG() |