py.lib

Yohn Y. 2019-09-30 Parent:a1729effde3c Child:af2bf518950a

9:b18b6e30d6ba Browse Files

.

syslogger.py

     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/syslogger.py	Mon Sep 30 23:52:23 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 +