py.lib

Yohn Y. 2019-09-30

9:b18b6e30d6ba Go to Latest

py.lib/syslogger.py

.

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