| # -*- coding: utf-8 -*- |
| # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| # |
| # This file is part of logilab-common. |
| # |
| # logilab-common is free software: you can redistribute it and/or modify it under |
| # the terms of the GNU Lesser General Public License as published by the Free |
| # Software Foundation, either version 2.1 of the License, or (at your option) any |
| # later version. |
| # |
| # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
| # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| # details. |
| # |
| # You should have received a copy of the GNU Lesser General Public License along |
| # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| """Extends the logging module from the standard library.""" |
| |
| __docformat__ = "restructuredtext en" |
| |
| import os |
| import sys |
| import logging |
| |
| from logilab.common.textutils import colorize_ansi |
| |
| |
| def set_log_methods(cls, logger): |
| """bind standard logger's methods as methods on the class""" |
| cls.__logger = logger |
| for attr in ('debug', 'info', 'warning', 'error', 'critical', 'exception'): |
| setattr(cls, attr, getattr(logger, attr)) |
| |
| |
| def xxx_cyan(record): |
| if 'XXX' in record.message: |
| return 'cyan' |
| |
| class ColorFormatter(logging.Formatter): |
| """ |
| A color Formatter for the logging standard module. |
| |
| By default, colorize CRITICAL and ERROR in red, WARNING in orange, INFO in |
| green and DEBUG in yellow. |
| |
| self.colors is customizable via the 'color' constructor argument (dictionary). |
| |
| self.colorfilters is a list of functions that get the LogRecord |
| and return a color name or None. |
| """ |
| |
| def __init__(self, fmt=None, datefmt=None, colors=None): |
| logging.Formatter.__init__(self, fmt, datefmt) |
| self.colorfilters = [] |
| self.colors = {'CRITICAL': 'red', |
| 'ERROR': 'red', |
| 'WARNING': 'magenta', |
| 'INFO': 'green', |
| 'DEBUG': 'yellow', |
| } |
| if colors is not None: |
| assert isinstance(colors, dict) |
| self.colors.update(colors) |
| |
| def format(self, record): |
| msg = logging.Formatter.format(self, record) |
| if record.levelname in self.colors: |
| color = self.colors[record.levelname] |
| return colorize_ansi(msg, color) |
| else: |
| for cf in self.colorfilters: |
| color = cf(record) |
| if color: |
| return colorize_ansi(msg, color) |
| return msg |
| |
| def set_color_formatter(logger=None, **kw): |
| """ |
| Install a color formatter on the 'logger'. If not given, it will |
| defaults to the default logger. |
| |
| Any additional keyword will be passed as-is to the ColorFormatter |
| constructor. |
| """ |
| if logger is None: |
| logger = logging.getLogger() |
| if not logger.handlers: |
| logging.basicConfig() |
| format_msg = logger.handlers[0].formatter._fmt |
| fmt = ColorFormatter(format_msg, **kw) |
| fmt.colorfilters.append(xxx_cyan) |
| logger.handlers[0].setFormatter(fmt) |
| |
| |
| LOG_FORMAT = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s' |
| LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' |
| |
| def get_handler(debug=False, syslog=False, logfile=None, rotation_parameters=None): |
| """get an apropriate handler according to given parameters""" |
| if os.environ.get('APYCOT_ROOT'): |
| handler = logging.StreamHandler(sys.stdout) |
| if debug: |
| handler = logging.StreamHandler() |
| elif logfile is None: |
| if syslog: |
| from logging import handlers |
| handler = handlers.SysLogHandler() |
| else: |
| handler = logging.StreamHandler() |
| else: |
| try: |
| if rotation_parameters is None: |
| handler = logging.FileHandler(logfile) |
| else: |
| from logging.handlers import TimedRotatingFileHandler |
| handler = TimedRotatingFileHandler( |
| logfile, **rotation_parameters) |
| except IOError: |
| handler = logging.StreamHandler() |
| return handler |
| |
| def get_threshold(debug=False, logthreshold=None): |
| if logthreshold is None: |
| if debug: |
| logthreshold = logging.DEBUG |
| else: |
| logthreshold = logging.ERROR |
| elif isinstance(logthreshold, basestring): |
| logthreshold = getattr(logging, THRESHOLD_MAP.get(logthreshold, |
| logthreshold)) |
| return logthreshold |
| |
| def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT): |
| isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty() |
| if isatty and sys.platform != 'win32': |
| fmt = ColorFormatter(logformat, logdateformat) |
| def col_fact(record): |
| if 'XXX' in record.message: |
| return 'cyan' |
| if 'kick' in record.message: |
| return 'red' |
| fmt.colorfilters.append(col_fact) |
| else: |
| fmt = logging.Formatter(logformat, logdateformat) |
| return fmt |
| |
| def init_log(debug=False, syslog=False, logthreshold=None, logfile=None, |
| logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT, fmt=None, |
| rotation_parameters=None, handler=None): |
| """init the log service""" |
| logger = logging.getLogger() |
| if handler is None: |
| handler = get_handler(debug, syslog, logfile, rotation_parameters) |
| # only addHandler and removeHandler method while I would like a setHandler |
| # method, so do it this way :$ |
| logger.handlers = [handler] |
| logthreshold = get_threshold(debug, logthreshold) |
| logger.setLevel(logthreshold) |
| if fmt is None: |
| if debug: |
| fmt = get_formatter(logformat=logformat, logdateformat=logdateformat) |
| else: |
| fmt = logging.Formatter(logformat, logdateformat) |
| handler.setFormatter(fmt) |
| return handler |
| |
| # map logilab.common.logger thresholds to logging thresholds |
| THRESHOLD_MAP = {'LOG_DEBUG': 'DEBUG', |
| 'LOG_INFO': 'INFO', |
| 'LOG_NOTICE': 'INFO', |
| 'LOG_WARN': 'WARNING', |
| 'LOG_WARNING': 'WARNING', |
| 'LOG_ERR': 'ERROR', |
| 'LOG_ERROR': 'ERROR', |
| 'LOG_CRIT': 'CRITICAL', |
| } |