comparison src/core/log.py @ 991:05e02f8b7eb4

core: logging refactoring, first step: - added a core.log module - 3 backends can be used: basic, standard (python's logging module) or twisted - colors can be used - the module has been made to be used by frontends, it should work in exotic environments like pyjamas - logging basic configuration is now made in sat.tac - core.log configuration is inspired from python standard logging, and use it when possible - getLogger should be used the same way as for standard logging
author Goffi <goffi@goffi.org>
date Sat, 19 Apr 2014 00:02:38 +0200
parents
children f51a1895275c
comparison
equal deleted inserted replaced
990:f0e407709d8e 991:05e02f8b7eb4
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # SàT: a XMPP client
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 """High level logging functions"""
21 # XXX: this module use standard logging module when possible, but as SàT can work in different cases where logging is not the best choice (twisted, pyjamas, etc), it is necessary to have a dedicated module. In addition additional feature like environment variable and color are also managed.
22
23 from sat.core.constants import Const as C
24 from sat.core import exceptions
25
26 _backend = None
27 _loggers = {}
28
29
30 class Logger(object):
31 """ High level logging class """
32
33 def __init__(self, name):
34 self._name = name
35
36 def debug(self, msg):
37 print msg
38
39 def info(self, msg):
40 print msg
41
42 def warning(self, msg):
43 print msg
44
45 def error(self, msg):
46 print msg
47
48 def critical(self, msg):
49 print msg
50
51
52 def _configureStdLogging(logging, level=None, colors=False, force_colors=False):
53 """Configure standard logging module
54 @param logging: standard logging module
55 @param colors: if True use ANSI colors to show log levels
56 @param force_colors: if True ANSI colors are used even if stdout is not a tty
57 """
58 FORMAT = '%(message)s'
59 if level is None:
60 level = logging.DEBUG
61 import sys
62 with_color = colors & (sys.stdout.isatty() or force_colors)
63 if not colors and force_colors:
64 raise ValueError("force_colors can't be used if colors is False")
65
66 class SatFormatter(logging.Formatter):
67 u"""Formatter which manage SàT specificities"""
68
69 def __init__(self, fmt=None, datefmt=None):
70 super(SatFormatter, self).__init__(fmt, datefmt)
71
72 def format(self, record):
73 s = super(SatFormatter, self).format(record)
74 if with_color:
75 if record.levelno == logging.DEBUG:
76 fmt = (C.ANSI_FG_CYAN, s, C.ANSI_RESET)
77 elif record.levelno == logging.WARNING:
78 fmt = (C.ANSI_FG_YELLOW, s, C.ANSI_RESET)
79 elif record.levelno == logging.ERROR:
80 fmt = (C.ANSI_FG_RED,
81 C.ANSI_BLINK,
82 r'/!\ ',
83 C.ANSI_BLINK_OFF,
84 s,
85 C.ANSI_RESET)
86 elif record.levelno == logging.CRITICAL:
87 fmt = (C.ANSI_BOLD,
88 C.ANSI_FG_RED,
89 'Guru Meditation ',
90 C.ANSI_NORMAL_WEIGHT,
91 s,
92 C.ANSI_RESET)
93 else:
94 fmt = s
95 s = ''.join(fmt)
96
97 return s
98
99
100 root_logger = logging.getLogger()
101 if len(root_logger.handlers) == 0:
102 hdlr = logging.StreamHandler()
103 formatter = SatFormatter(FORMAT)
104 hdlr.setFormatter(formatter)
105 root_logger.addHandler(hdlr)
106 root_logger.setLevel(level)
107 else:
108 root_logger.warning(u"Handler already set on root logger")
109
110 def configure(backend=C.LOG_BACKEND_STANDARD):
111 """Configure logging bejaviour
112 @param backend: can be:
113 C.LOG_BACKEND_STANDARD: use standard logging module
114 C.LOG_BACKEND_TWISTED: use twisted logging module (with standard logging observer)
115 C.LOG_BACKEND_BASIC: use a basic print based logging
116 """
117 global _backend
118 if _backend is not None:
119 raise exceptions.InternalError("Logging can only be configured once")
120 _backend = backend
121
122 if backend == C.LOG_BACKEND_TWISTED:
123 from twisted.python import log
124 import logging
125 _configureStdLogging(logging, colors=True)
126 def logMsg(self, msg, level):
127 log.msg(msg.encode('utf-8'), logLevel=level)
128 Logger.logMsg = logMsg
129 Logger.debug = lambda self, msg: self.logMsg(msg, logging.DEBUG)
130 Logger.info = lambda self, msg: self.logMsg(msg, logging.INFO)
131 Logger.warning = lambda self, msg: self.logMsg(msg, logging.WARNING)
132 Logger.error = lambda self, msg: self.logMsg(msg, logging.ERROR)
133 Logger.critical = lambda self, msg: self.logMsg(msg, logging.CRITICAL)
134 observer = log.PythonLoggingObserver()
135 observer.start()
136
137 elif backend == C.LOG_BACKEND_STANDARD:
138 global getLogger
139 global debug
140 global info
141 global warning
142 global error
143 global critical
144 import logging
145 _configureStdLogging(logging, colors=True)
146 getLogger = logging.getLogger
147 debug = logging.debug
148 info = logging.info
149 warning = logging.warning
150 error = logging.error
151 critical = logging.critical
152
153 elif backend == C.LOG_BACKEND_BASIC:
154 pass
155
156 else:
157 raise ValueError("unknown backend")
158
159
160 def getLogger(name=C.LOG_BASE_LOGGER):
161 return _loggers.setdefault(name, Logger(name))
162
163 _root_logger = getLogger()
164
165 def debug(msg):
166 _root_logger.debug(msg)
167
168 def info(msg):
169 _root_logger.info(msg)
170
171 def warning(msg):
172 _root_logger.warning(msg)
173
174 def error(msg):
175 _root_logger.error(msg)
176
177 def critical(msg):
178 _root_logger.critical(msg)