OLD | NEW |
(Empty) | |
| 1 # -*- coding: utf-8 -*- |
| 2 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
| 3 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 4 # |
| 5 # This file is part of logilab-common. |
| 6 # |
| 7 # logilab-common is free software: you can redistribute it and/or modify it unde
r |
| 8 # the terms of the GNU Lesser General Public License as published by the Free |
| 9 # Software Foundation, either version 2.1 of the License, or (at your option) an
y |
| 10 # later version. |
| 11 # |
| 12 # logilab-common is distributed in the hope that it will be useful, but WITHOUT |
| 13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 14 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
| 15 # details. |
| 16 # |
| 17 # You should have received a copy of the GNU Lesser General Public License along |
| 18 # with logilab-common. If not, see <http://www.gnu.org/licenses/>. |
| 19 """Extends the logging module from the standard library.""" |
| 20 |
| 21 __docformat__ = "restructuredtext en" |
| 22 |
| 23 import os |
| 24 import sys |
| 25 import logging |
| 26 |
| 27 from six import string_types |
| 28 |
| 29 from logilab.common.textutils import colorize_ansi |
| 30 |
| 31 |
| 32 def set_log_methods(cls, logger): |
| 33 """bind standard logger's methods as methods on the class""" |
| 34 cls.__logger = logger |
| 35 for attr in ('debug', 'info', 'warning', 'error', 'critical', 'exception'): |
| 36 setattr(cls, attr, getattr(logger, attr)) |
| 37 |
| 38 |
| 39 def xxx_cyan(record): |
| 40 if 'XXX' in record.message: |
| 41 return 'cyan' |
| 42 |
| 43 class ColorFormatter(logging.Formatter): |
| 44 """ |
| 45 A color Formatter for the logging standard module. |
| 46 |
| 47 By default, colorize CRITICAL and ERROR in red, WARNING in orange, INFO in |
| 48 green and DEBUG in yellow. |
| 49 |
| 50 self.colors is customizable via the 'color' constructor argument (dictionary
). |
| 51 |
| 52 self.colorfilters is a list of functions that get the LogRecord |
| 53 and return a color name or None. |
| 54 """ |
| 55 |
| 56 def __init__(self, fmt=None, datefmt=None, colors=None): |
| 57 logging.Formatter.__init__(self, fmt, datefmt) |
| 58 self.colorfilters = [] |
| 59 self.colors = {'CRITICAL': 'red', |
| 60 'ERROR': 'red', |
| 61 'WARNING': 'magenta', |
| 62 'INFO': 'green', |
| 63 'DEBUG': 'yellow', |
| 64 } |
| 65 if colors is not None: |
| 66 assert isinstance(colors, dict) |
| 67 self.colors.update(colors) |
| 68 |
| 69 def format(self, record): |
| 70 msg = logging.Formatter.format(self, record) |
| 71 if record.levelname in self.colors: |
| 72 color = self.colors[record.levelname] |
| 73 return colorize_ansi(msg, color) |
| 74 else: |
| 75 for cf in self.colorfilters: |
| 76 color = cf(record) |
| 77 if color: |
| 78 return colorize_ansi(msg, color) |
| 79 return msg |
| 80 |
| 81 def set_color_formatter(logger=None, **kw): |
| 82 """ |
| 83 Install a color formatter on the 'logger'. If not given, it will |
| 84 defaults to the default logger. |
| 85 |
| 86 Any additional keyword will be passed as-is to the ColorFormatter |
| 87 constructor. |
| 88 """ |
| 89 if logger is None: |
| 90 logger = logging.getLogger() |
| 91 if not logger.handlers: |
| 92 logging.basicConfig() |
| 93 format_msg = logger.handlers[0].formatter._fmt |
| 94 fmt = ColorFormatter(format_msg, **kw) |
| 95 fmt.colorfilters.append(xxx_cyan) |
| 96 logger.handlers[0].setFormatter(fmt) |
| 97 |
| 98 |
| 99 LOG_FORMAT = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s' |
| 100 LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' |
| 101 |
| 102 def get_handler(debug=False, syslog=False, logfile=None, rotation_parameters=Non
e): |
| 103 """get an apropriate handler according to given parameters""" |
| 104 if os.environ.get('APYCOT_ROOT'): |
| 105 handler = logging.StreamHandler(sys.stdout) |
| 106 if debug: |
| 107 handler = logging.StreamHandler() |
| 108 elif logfile is None: |
| 109 if syslog: |
| 110 from logging import handlers |
| 111 handler = handlers.SysLogHandler() |
| 112 else: |
| 113 handler = logging.StreamHandler() |
| 114 else: |
| 115 try: |
| 116 if rotation_parameters is None: |
| 117 if os.name == 'posix' and sys.version_info >= (2, 6): |
| 118 from logging.handlers import WatchedFileHandler |
| 119 handler = WatchedFileHandler(logfile) |
| 120 else: |
| 121 handler = logging.FileHandler(logfile) |
| 122 else: |
| 123 from logging.handlers import TimedRotatingFileHandler |
| 124 handler = TimedRotatingFileHandler( |
| 125 logfile, **rotation_parameters) |
| 126 except IOError: |
| 127 handler = logging.StreamHandler() |
| 128 return handler |
| 129 |
| 130 def get_threshold(debug=False, logthreshold=None): |
| 131 if logthreshold is None: |
| 132 if debug: |
| 133 logthreshold = logging.DEBUG |
| 134 else: |
| 135 logthreshold = logging.ERROR |
| 136 elif isinstance(logthreshold, string_types): |
| 137 logthreshold = getattr(logging, THRESHOLD_MAP.get(logthreshold, |
| 138 logthreshold)) |
| 139 return logthreshold |
| 140 |
| 141 def _colorable_terminal(): |
| 142 isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty() |
| 143 if not isatty: |
| 144 return False |
| 145 if os.name == 'nt': |
| 146 try: |
| 147 from colorama import init as init_win32_colors |
| 148 except ImportError: |
| 149 return False |
| 150 init_win32_colors() |
| 151 return True |
| 152 |
| 153 def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT): |
| 154 if _colorable_terminal(): |
| 155 fmt = ColorFormatter(logformat, logdateformat) |
| 156 def col_fact(record): |
| 157 if 'XXX' in record.message: |
| 158 return 'cyan' |
| 159 if 'kick' in record.message: |
| 160 return 'red' |
| 161 fmt.colorfilters.append(col_fact) |
| 162 else: |
| 163 fmt = logging.Formatter(logformat, logdateformat) |
| 164 return fmt |
| 165 |
| 166 def init_log(debug=False, syslog=False, logthreshold=None, logfile=None, |
| 167 logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT, fmt=None, |
| 168 rotation_parameters=None, handler=None): |
| 169 """init the log service""" |
| 170 logger = logging.getLogger() |
| 171 if handler is None: |
| 172 handler = get_handler(debug, syslog, logfile, rotation_parameters) |
| 173 # only addHandler and removeHandler method while I would like a setHandler |
| 174 # method, so do it this way :$ |
| 175 logger.handlers = [handler] |
| 176 logthreshold = get_threshold(debug, logthreshold) |
| 177 logger.setLevel(logthreshold) |
| 178 if fmt is None: |
| 179 if debug: |
| 180 fmt = get_formatter(logformat=logformat, logdateformat=logdateformat
) |
| 181 else: |
| 182 fmt = logging.Formatter(logformat, logdateformat) |
| 183 handler.setFormatter(fmt) |
| 184 return handler |
| 185 |
| 186 # map logilab.common.logger thresholds to logging thresholds |
| 187 THRESHOLD_MAP = {'LOG_DEBUG': 'DEBUG', |
| 188 'LOG_INFO': 'INFO', |
| 189 'LOG_NOTICE': 'INFO', |
| 190 'LOG_WARN': 'WARNING', |
| 191 'LOG_WARNING': 'WARNING', |
| 192 'LOG_ERR': 'ERROR', |
| 193 'LOG_ERROR': 'ERROR', |
| 194 'LOG_CRIT': 'CRITICAL', |
| 195 } |
OLD | NEW |