py.lib
2019-11-11
Parent:syslogger.py@b18b6e30d6ba
py.lib/log/syslogger.py
+ Добавлены простые логгеры
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/log/syslogger.py Mon Nov 11 21:18:14 2019 +0300 1.3 @@ -0,0 +1,278 @@ 1.4 +#!/usr/bin/env python 1.5 +# -*- coding: utf-8 -*- 1.6 +""" Логирование в системный журнал Unix 1.7 + 1.8 +Метки в журнале о уровне сообщения: 1.9 + "`": Debug 1.10 + ".": Info 1.11 + "*": Warning 1.12 + "!": Error 1.13 + "#": Alert 1.14 +""" 1.15 +import syslog 1.16 +from sys import argv, version_info, exc_info 1.17 +from time import time 1.18 +from traceback import extract_tb 1.19 + 1.20 +LOG_FACILITY = { 1.21 + 'auth': syslog.LOG_AUTH, 1.22 + 'authpriv': syslog.LOG_AUTH, 1.23 + 'cron': syslog.LOG_CRON, 1.24 + 'daemon': syslog.LOG_DAEMON, 1.25 + 'ftp': syslog.LOG_DAEMON, 1.26 + 'kern': syslog.LOG_KERN, 1.27 + 'lpr': syslog.LOG_LPR, 1.28 + 'mail': syslog.LOG_MAIL, 1.29 + 'news': syslog.LOG_NEWS, 1.30 + 'syslog': syslog.LOG_SYSLOG, 1.31 + 'user': syslog.LOG_USER, 1.32 + 'uucp': syslog.LOG_UUCP, 1.33 + 'local0': syslog.LOG_LOCAL0, 1.34 + 'local1': syslog.LOG_LOCAL1, 1.35 + 'local2': syslog.LOG_LOCAL2, 1.36 + 'local3': syslog.LOG_LOCAL3, 1.37 + 'local4': syslog.LOG_LOCAL4, 1.38 + 'local5': syslog.LOG_LOCAL5, 1.39 + 'local6': syslog.LOG_LOCAL6, 1.40 + 'local7': syslog.LOG_LOCAL7 1.41 +} 1.42 + 1.43 +# --- INTERFACE --- # 1.44 +FACILITY = LOG_FACILITY['user'] 1.45 +def logPrep(ident, facility='user'): 1.46 + global FACILITY 1.47 + if not facility.lower() in LOG_FACILITY: 1.48 + raise LoggerError('Unknown facility') 1.49 + syslog.openlog(ident, syslog.LOG_PID) 1.50 + 1.51 + FACILITY = LOG_FACILITY[facility] 1.52 + 1.53 + 1.54 +# --- ABSTRACT PART --- # 1.55 + 1.56 +LOG_DEBUG = "`" 1.57 +LOG_INFO = "." 1.58 +LOG_WARN = "*" 1.59 +LOG_ERR = "!" 1.60 +LOG_ALERT = "#" 1.61 + 1.62 +class LoggerError(Exception): pass 1.63 + 1.64 +if version_info.major == 3: 1.65 + def _strWrap(msg): 1.66 + return str(msg) 1.67 +elif version_info.major == 2: 1.68 + def _strWrap(msg): 1.69 + if isinstance(msg, unicode): 1.70 + return msg 1.71 + else: 1.72 + return str(msg).decode('utf-8') 1.73 +else: 1.74 + raise LoggerError('Unknown major version of Python') 1.75 + 1.76 + 1.77 +def _splitLines(obj): 1.78 + if isinstance(obj, (list, tuple)): 1.79 + for i in _parseList(obj): 1.80 + yield i 1.81 + elif isinstance(obj, dict): 1.82 + for i in _parseDict(obj): 1.83 + yield i 1.84 + else: 1.85 + buf = _strWrap(obj).splitlines() 1.86 + for i in buf: 1.87 + if len(i) < 401: 1.88 + yield i 1.89 + else: 1.90 + i = '<| ' + i + ' |>' 1.91 + lenI = len(i) 1.92 + c = 0 1.93 + while (c + 401) < lenI: 1.94 + yield i[c:c+401] 1.95 + c += 401 1.96 + yield i[c:] 1.97 + 1.98 +def _parseList(lst): 1.99 + for i in lst: 1.100 + i = _splitLines(i) 1.101 + try: 1.102 + yield '- %s' % next(i) 1.103 + for l in i: 1.104 + yield ' %s' % l 1.105 + except StopIteration: pass 1.106 + 1.107 +def _parseDict(dct): 1.108 + for key in sorted(dct): 1.109 + yield '%s:' % key 1.110 + for l in _splitLines(dct[key]): 1.111 + yield ' %s' % l 1.112 + 1.113 +def _parseArgs(a, kwa): 1.114 + if a: 1.115 + yield '| --- ARGS:' 1.116 + for i in _parseList(a): 1.117 + yield '| %s' % i 1.118 + 1.119 + if kwa: 1.120 + yield '| --- NAMED ARGS:' 1.121 + for i in _parseDict(kwa): 1.122 + yield '| %s' % i 1.123 + 1.124 + if a or kwa: 1.125 + yield '| ---' 1.126 + 1.127 +class TimingLog(): 1.128 + def __init__(self, logproc, opName): 1.129 + self.logproc = logproc 1.130 + self.opName = opName 1.131 + self.dt = time() 1.132 + 1.133 + def __str__(self): 1.134 + return '%s :: %.4f' % (self.opName, (time() - self.dt)) 1.135 + 1.136 + def __call__(self, msg, *a, **kwa): 1.137 + self.logproc('%s | %s' % (self, msg)) 1.138 + for i in _parseArgs(a, kwa): 1.139 + self.logproc('%s | %s' % (self, i)) 1.140 + 1.141 + def reset(self): 1.142 + self.dt = time() 1.143 + 1.144 + 1.145 +class OperationContext(object): 1.146 + def __init__(self, log, opName): 1.147 + self.opName = opName 1.148 + self.log = log 1.149 + self.msgBuf = [] 1.150 + 1.151 + def __call__(self, msg, *a, **kwa): 1.152 + self.msgBuf.append((msg, [ i for i in map(_strWrap, a)])) 1.153 + 1.154 + def __enter__(self): 1.155 + return self 1.156 + 1.157 + def __exit__(self, exc_type, exc_val, exc_tb): 1.158 + if exc_type is not None: 1.159 + self.log.err("Operation '%s' fail" % self.opName) 1.160 + self.log.excpt_handler(exc_type, exc_val, exc_tb) 1.161 + 1.162 + if self.msgBuf: 1.163 + self.log.err('--- MESSAGES ---') 1.164 + for msg, a, kwa in self.msgBuf: 1.165 + self.log.err(msg, a) 1.166 + self.log.err('--- END MESSAGES ---') 1.167 + 1.168 + 1.169 +class AbstractLogger(object): 1.170 + def err(self, msg, *a, **kwa): 1.171 + raise LoggerError('NotImplemented') 1.172 + 1.173 + def __call__(self, msg, *a, **kwa): 1.174 + raise LoggerError('NotImplemented') 1.175 + 1.176 + def excpt_handler(self, eType, eObj, eTb): 1.177 + eTb = extract_tb(eTb) 1.178 + eType = str(eType) 1.179 + try: 1.180 + eType = eType.split("'")[1] 1.181 + except IndexError: 1.182 + pass 1.183 + 1.184 + try: 1.185 + eArgs = eObj.args[1:] 1.186 + except: 1.187 + eArgs = [] 1.188 + 1.189 + try: 1.190 + eKwa = eObj.kwa 1.191 + except: 1.192 + eKwa = {} 1.193 + 1.194 + eObj = str(eObj) 1.195 + 1.196 + self.err('--- EXCEPTION ---') 1.197 + self.err(eType) 1.198 + 1.199 + for l in _splitLines(eObj): 1.200 + self.err(' ' + l) 1.201 + 1.202 + for l in _parseArgs(eArgs, eKwa): 1.203 + self.err(l) 1.204 + 1.205 + self.err('--- TRACEBACK ---') 1.206 + for _tbFile, _tbLine, _tbFunc, _tbText in eTb: 1.207 + self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 1.208 + self.err(' %s' % _tbText) 1.209 + self.err('--- END EXCEPTION ---') 1.210 + 1.211 + def excpt(self, msg, *a, **kwa): 1.212 + eType, eObj, eTb = exc_info() 1.213 + self.err(msg, *a, **kwa) 1.214 + self.excpt_handler(eType, eObj, eTb) 1.215 + 1.216 + def cntxt(self, opName): 1.217 + """ 1.218 + Если операцию нужно залогировать но при этом совершенно не хочется покрывать код 1.219 + толстым слоем try ... except можно завернуть операцию в контекст, и даже сохранить 1.220 + возвращаемом логером объекте отладочную информацию. При падении и только при нём 1.221 + это обязательно попадёт в лог. 1.222 + 1.223 + ОСТОРОЖНО ПАМЯТЬ!!!!! 1.224 + Объект копирует себе все переданные данные и опреобразует их сразу в строки 1.225 + Если объекту передать одни и те же параметры они всё равно будут храниться 1.226 + по копии на каждый вызов в виде строк, что может сьесть много памяти и вызвать 1.227 + болезненное её особождение. 1.228 + 1.229 + :param opName: членораздельное имя операции 1.230 + """ 1.231 + return OperationContext(self, opName) 1.232 + 1.233 + def getTiming(self, opName): 1.234 + return TimingLog(self, opName) 1.235 + 1.236 + def dumpDict(self, msg, dct): 1.237 + self('--- ' + _strWrap(msg)) 1.238 + for i in _parseDict(dct): 1.239 + self('| ' + i) 1.240 + self('---') 1.241 + 1.242 + @staticmethod 1.243 + def logPrep(*a, **kwa): 1.244 + logPrep(*a, **kwa) 1.245 + 1.246 +# --- END ABSTRACT PART --- # 1.247 + 1.248 +class Logger(AbstractLogger): 1.249 + facility = FACILITY 1.250 + def __init__(self, modName = 'main'): 1.251 + self.name = modName 1.252 + 1.253 + def _write(self, flags, prefix, msg): 1.254 + syslog.syslog(flags, u"%s %s: %s" % (prefix, self.name, msg)) 1.255 + 1.256 + def __call__(self, msg, *a, **kwa): 1.257 + _flags = self.facility | syslog.LOG_INFO 1.258 + _prefix = LOG_INFO + ' ' + self.name + ': ' 1.259 + syslog.syslog(_flags, _prefix + _strWrap(msg)) 1.260 + for i in _parseArgs(a, kwa): 1.261 + syslog.syslog(_flags, _prefix + i) 1.262 + 1.263 + def warn(self, msg, *a, **kwa): 1.264 + _flags = self.facility | syslog.LOG_WARNING 1.265 + _prefix = LOG_WARN + ' ' + self.name + ': ' 1.266 + syslog.syslog(_flags, _prefix + _strWrap(msg)) 1.267 + for i in _parseArgs(a, kwa): 1.268 + syslog.syslog(_flags, _prefix + i) 1.269 + 1.270 + def err(self, msg, *a, **kwa): 1.271 + _flags = self.facility | syslog.LOG_ERR 1.272 + _prefix = LOG_ERR + ' ' + self.name + ': ' 1.273 + syslog.syslog(_flags, _prefix + _strWrap(msg)) 1.274 + for i in _parseArgs(a, kwa): 1.275 + syslog.syslog(_flags, _prefix + i) 1.276 + 1.277 + 1.278 + 1.279 + 1.280 + 1.281 +