py.lib

Yohn Y. 2019-09-30

9:b18b6e30d6ba Go to Latest

py.lib/syslogger.py

.

History
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """ Логирование в системный журнал Unix
5 Метки в журнале о уровне сообщения:
6 "`": Debug
7 ".": Info
8 "*": Warning
9 "!": Error
10 "#": Alert
11 """
12 import syslog
13 from sys import argv, version_info, exc_info
14 from time import time
15 from traceback import extract_tb
17 LOG_FACILITY = {
18 'auth': syslog.LOG_AUTH,
19 'authpriv': syslog.LOG_AUTH,
20 'cron': syslog.LOG_CRON,
21 'daemon': syslog.LOG_DAEMON,
22 'ftp': syslog.LOG_DAEMON,
23 'kern': syslog.LOG_KERN,
24 'lpr': syslog.LOG_LPR,
25 'mail': syslog.LOG_MAIL,
26 'news': syslog.LOG_NEWS,
27 'syslog': syslog.LOG_SYSLOG,
28 'user': syslog.LOG_USER,
29 'uucp': syslog.LOG_UUCP,
30 'local0': syslog.LOG_LOCAL0,
31 'local1': syslog.LOG_LOCAL1,
32 'local2': syslog.LOG_LOCAL2,
33 'local3': syslog.LOG_LOCAL3,
34 'local4': syslog.LOG_LOCAL4,
35 'local5': syslog.LOG_LOCAL5,
36 'local6': syslog.LOG_LOCAL6,
37 'local7': syslog.LOG_LOCAL7
38 }
40 # --- INTERFACE --- #
41 FACILITY = LOG_FACILITY['user']
42 def logPrep(ident, facility='user'):
43 global FACILITY
44 if not facility.lower() in LOG_FACILITY:
45 raise LoggerError('Unknown facility')
46 syslog.openlog(ident, syslog.LOG_PID)
48 FACILITY = LOG_FACILITY[facility]
51 # --- ABSTRACT PART --- #
53 LOG_DEBUG = "`"
54 LOG_INFO = "."
55 LOG_WARN = "*"
56 LOG_ERR = "!"
57 LOG_ALERT = "#"
59 class LoggerError(Exception): pass
61 if version_info.major == 3:
62 def _strWrap(msg):
63 return str(msg)
64 elif version_info.major == 2:
65 def _strWrap(msg):
66 if isinstance(msg, unicode):
67 return msg
68 else:
69 return str(msg).decode('utf-8')
70 else:
71 raise LoggerError('Unknown major version of Python')
74 def _splitLines(obj):
75 if isinstance(obj, (list, tuple)):
76 for i in _parseList(obj):
77 yield i
78 elif isinstance(obj, dict):
79 for i in _parseDict(obj):
80 yield i
81 else:
82 buf = _strWrap(obj).splitlines()
83 for i in buf:
84 if len(i) < 401:
85 yield i
86 else:
87 i = '<| ' + i + ' |>'
88 lenI = len(i)
89 c = 0
90 while (c + 401) < lenI:
91 yield i[c:c+401]
92 c += 401
93 yield i[c:]
95 def _parseList(lst):
96 for i in lst:
97 i = _splitLines(i)
98 try:
99 yield '- %s' % next(i)
100 for l in i:
101 yield ' %s' % l
102 except StopIteration: pass
104 def _parseDict(dct):
105 for key in sorted(dct):
106 yield '%s:' % key
107 for l in _splitLines(dct[key]):
108 yield ' %s' % l
110 def _parseArgs(a, kwa):
111 if a:
112 yield '| --- ARGS:'
113 for i in _parseList(a):
114 yield '| %s' % i
116 if kwa:
117 yield '| --- NAMED ARGS:'
118 for i in _parseDict(kwa):
119 yield '| %s' % i
121 if a or kwa:
122 yield '| ---'
124 class TimingLog():
125 def __init__(self, logproc, opName):
126 self.logproc = logproc
127 self.opName = opName
128 self.dt = time()
130 def __str__(self):
131 return '%s :: %.4f' % (self.opName, (time() - self.dt))
133 def __call__(self, msg, *a, **kwa):
134 self.logproc('%s | %s' % (self, msg))
135 for i in _parseArgs(a, kwa):
136 self.logproc('%s | %s' % (self, i))
138 def reset(self):
139 self.dt = time()
142 class OperationContext(object):
143 def __init__(self, log, opName):
144 self.opName = opName
145 self.log = log
146 self.msgBuf = []
148 def __call__(self, msg, *a, **kwa):
149 self.msgBuf.append((msg, [ i for i in map(_strWrap, a)]))
151 def __enter__(self):
152 return self
154 def __exit__(self, exc_type, exc_val, exc_tb):
155 if exc_type is not None:
156 self.log.err("Operation '%s' fail" % self.opName)
157 self.log.excpt_handler(exc_type, exc_val, exc_tb)
159 if self.msgBuf:
160 self.log.err('--- MESSAGES ---')
161 for msg, a, kwa in self.msgBuf:
162 self.log.err(msg, a)
163 self.log.err('--- END MESSAGES ---')
166 class AbstractLogger(object):
167 def err(self, msg, *a, **kwa):
168 raise LoggerError('NotImplemented')
170 def __call__(self, msg, *a, **kwa):
171 raise LoggerError('NotImplemented')
173 def excpt_handler(self, eType, eObj, eTb):
174 eTb = extract_tb(eTb)
175 eType = str(eType)
176 try:
177 eType = eType.split("'")[1]
178 except IndexError:
179 pass
181 try:
182 eArgs = eObj.args[1:]
183 except:
184 eArgs = []
186 try:
187 eKwa = eObj.kwa
188 except:
189 eKwa = {}
191 eObj = str(eObj)
193 self.err('--- EXCEPTION ---')
194 self.err(eType)
196 for l in _splitLines(eObj):
197 self.err(' ' + l)
199 for l in _parseArgs(eArgs, eKwa):
200 self.err(l)
202 self.err('--- TRACEBACK ---')
203 for _tbFile, _tbLine, _tbFunc, _tbText in eTb:
204 self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc))
205 self.err(' %s' % _tbText)
206 self.err('--- END EXCEPTION ---')
208 def excpt(self, msg, *a, **kwa):
209 eType, eObj, eTb = exc_info()
210 self.err(msg, *a, **kwa)
211 self.excpt_handler(eType, eObj, eTb)
213 def cntxt(self, opName):
214 """
215 Если операцию нужно залогировать но при этом совершенно не хочется покрывать код
216 толстым слоем try ... except можно завернуть операцию в контекст, и даже сохранить
217 возвращаемом логером объекте отладочную информацию. При падении и только при нём
218 это обязательно попадёт в лог.
220 ОСТОРОЖНО ПАМЯТЬ!!!!!
221 Объект копирует себе все переданные данные и опреобразует их сразу в строки
222 Если объекту передать одни и те же параметры они всё равно будут храниться
223 по копии на каждый вызов в виде строк, что может сьесть много памяти и вызвать
224 болезненное её особождение.
226 :param opName: членораздельное имя операции
227 """
228 return OperationContext(self, opName)
230 def getTiming(self, opName):
231 return TimingLog(self, opName)
233 def dumpDict(self, msg, dct):
234 self('--- ' + _strWrap(msg))
235 for i in _parseDict(dct):
236 self('| ' + i)
237 self('---')
239 @staticmethod
240 def logPrep(*a, **kwa):
241 logPrep(*a, **kwa)
243 # --- END ABSTRACT PART --- #
245 class Logger(AbstractLogger):
246 facility = FACILITY
247 def __init__(self, modName = 'main'):
248 self.name = modName
250 def _write(self, flags, prefix, msg):
251 syslog.syslog(flags, u"%s %s: %s" % (prefix, self.name, msg))
253 def __call__(self, msg, *a, **kwa):
254 _flags = self.facility | syslog.LOG_INFO
255 _prefix = LOG_INFO + ' ' + self.name + ': '
256 syslog.syslog(_flags, _prefix + _strWrap(msg))
257 for i in _parseArgs(a, kwa):
258 syslog.syslog(_flags, _prefix + i)
260 def warn(self, msg, *a, **kwa):
261 _flags = self.facility | syslog.LOG_WARNING
262 _prefix = LOG_WARN + ' ' + self.name + ': '
263 syslog.syslog(_flags, _prefix + _strWrap(msg))
264 for i in _parseArgs(a, kwa):
265 syslog.syslog(_flags, _prefix + i)
267 def err(self, msg, *a, **kwa):
268 _flags = self.facility | syslog.LOG_ERR
269 _prefix = LOG_ERR + ' ' + self.name + ': '
270 syslog.syslog(_flags, _prefix + _strWrap(msg))
271 for i in _parseArgs(a, kwa):
272 syslog.syslog(_flags, _prefix + i)