991
|
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) |