logger.py 5.83 KB
Newer Older
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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
import os
import logging, logging.handlers
import traceback

DEBUG = logging.DEBUG
INFO = logging.INFO
WARNING = logging.WARNING
ERROR = logging.ERROR
CRITICAL = logging.CRITICAL


class Logger(object):
    """This class is adapter for standard logging library - it has function for both service-wise logging
		and tas-wise logging. The interface is ver similliar to standard logging package."""

    def __init__(self, lvl, main_log_path, task_logs_path, log_to_console):
        """Constructor
			lvl - logging level (string), possible values: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
			main_log_path - path to service-wise logfile
			task_logs_path - path to directory containing task specific logfiles
		"""
        self._task_logs_path = task_logs_path
        self._main_log_path = main_log_path
        self._log_to_console = log_to_console
        self.default_logging_lvl = lvl
        logger_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        self._default_formatter = logging.Formatter(logger_format)

        self._main_logger = None

    def log(self, lvl, exception):
        """Log message in main logfile
			lvl - message level, as in constructor
			exception - string message or exception to log
			If the exception is WARNING, ERROR or CRITICAL, than logs traceback as well.
		"""
        if self._main_logger is None:
            self._main_logger = self._get_main_logger(
                self._main_log_path, self._log_to_console
            )

        if lvl in [WARNING, ERROR, CRITICAL]:
            self._log_traceback(self._main_logger, lvl, exception)
        self._log(self._main_logger, lvl, exception)

    def debug(self, exception):
        self.log(DEBUG, exception)

    def info(self, exception):
        self.log(INFO, exception)

    def warning(self, exception):
        self.log(WARNING, exception)

    def error(self, exception):
        self.log(ERROR, exception)

    def task_log(self, task_token, lvl, exception):
        """Log message in task specific logfile
			lvl - message level, as in constructor
			exception - string message or exception to log
			If the exception is WARNING, ERROR or CRITICAL, than logs traceback with main logger.
		"""
        if lvl in [WARNING, ERROR, CRITICAL]:
            self._log_traceback(self._main_logger, lvl, exception)
        task_logger, _ = self._get_task_logger(task_token)
        self._log(task_logger, lvl, exception)

    def task_debug(self, task_token, exception):
        self.task_log(task_token, DEBUG, exception)

    def task_info(self, task_token, exception):
        self.task_log(task_token, INFO, exception)

    def task_warning(self, task_token, exception):
        self.task_log(task_token, WARNING, exception)

    def task_error(self, task_token, exception):
        self.task_log(task_token, ERROR, exception)

    def shutdown(self):
        logging.shutdown()

    @staticmethod
    def str2logging_lvl(str_level):
        """Translates logging level in string into logging module const"""
        str_level = str_level.lower()
        return {
            "debug": DEBUG,
            "info": INFO,
            "warning": WARNING,
            "error": ERROR,
            "critical": CRITICAL,
        }[str_level]

    def _get_main_logger(self, main_log_path, log_to_console):
        """Creates /retrieves main logger - global for whole service and a parent to all task loggers,
			which writes to console and main logfile."""
        logger, descriptor = self._get_logger(
            self.default_logging_lvl, "service", main_log_path
        )
        self.main_logfile_descr = descriptor

        if log_to_console and self._check_logger_inited("service"):
            console_handler = logging.StreamHandler()
            console_handler.setLevel(self.default_logging_lvl)
            console_handler.setFormatter(self._default_formatter)
            logger.addHandler(console_handler)
        return logger

    def _get_task_logger(self, task_token):
        """Creates/retrieves logging.Logger instance with certain level for task with supplied token"""
        task_logger_name = "service.task-" + task_token
        task_log_file = os.path.join(self._task_logs_path, task_token)
        return self._get_logger(
            self.default_logging_lvl,
            task_logger_name,
            task_log_file,
            delay=True,
        )

    def _get_logger(self, logging_lvl, name, filepath, delay=False):
        """Creates/retreives logging.Logger instance with certain level and name that writes into files
			given by filepath (rotating).Returns logger and it's file descriptor."""
        logger = logging.getLogger(name)

        if not self._check_logger_inited(name):
            handler = logging.handlers.RotatingFileHandler(
                filepath, delay=delay, maxBytes=1024000, backupCount=10
            )
            handler.setLevel(logging_lvl)
            handler.setFormatter(self._default_formatter)
            logger.setLevel(logging_lvl)
            logger.addHandler(handler)
        else:
            handler = logger.handlers[0]

        descriptor = handler.stream.fileno() if handler.stream else None
        return logger, descriptor

    def _log(self, logger, lvl, exception):
        """Logs given exception to given logger with given level."""
        logger.log(lvl, str(exception))

    def _log_traceback(self, logger, lvl, exception):
        """If exception's traceback can be extracted, it logs this traceback."""
        if isinstance(exception, Exception):
            if hasattr(exception, "traceback"):
                traceback_str = exception.traceback
            else:
                traceback_str = traceback.format_exc()

            if traceback_str is not None:
                self._log(self._main_logger, lvl, traceback_str)

    def _check_logger_inited(self, name):
        logger = logging.getLogger(name)
        return len(logger.handlers) != 0