Source code for deadbeat.log

# -*- coding: utf-8 -*-

""" Simplified logging initialization and configuration for DeadBeat """

from __future__ import absolute_import, division, print_function, unicode_literals

import sys
import os
import stat
import socket as sock
import logging
import logging.handlers
import os.path as pth
from . import conf

__all__ = ["cast_loglevel", "cast_facility", "log_type", "log_base_config", "configure"]

_usual_sockets = ["/var/run/log", "/var/run/syslog", "/dev/log"]
_form = "%(name)s[%(process)d]: (%(levelname)s) %(methodName)s %(message)s"
_form_time = "%(asctime)s " + _form

[docs]def cast_loglevel(lev): """ Translate loglevel name to int """ try: return int(getattr(logging, lev.upper())) except (AttributeError, ValueError): if lev: sys.stderr.write('Unknown loglevel "%s", using "debug".\n' % lev) return logging.DEBUG
[docs]def cast_facility(fac): """ Translate syslog facility to int """ try: return int(getattr(logging.handlers.SysLogHandler, "LOG_" + fac.upper())) except (AttributeError, ValueError): if fac: sys.stderr.write('Unknown syslog facility "%s", using "local7".\n' % fac) return logging.handlers.SysLogHandler.LOG_LOCAL7
[docs]def log_type(s): """ Validate log type. """ s = str(s).lower() if s not in ["file", "syslog"]: sys.stderr.write('Unknown log type, using "file".\n') s = "file" return s
try: get_frame = sys._getframe except AttributeError: def get_frame(): try: raise Exception except Exception: return sys.exc_info()[2].tb_frame.f_back class _IntrospectFilter(logging.Filter): """ Logging filter which adds object name path as methodName. Logging itself can add funcName, however it's without class name. """ def get_caller_info(self): frame = get_frame() while hasattr(frame, "f_code"): code = frame.f_code filename = pth.normcase(code.co_filename) if filename == logging._srcfile: frame = frame.f_back continue name = code.co_name cls = None try: code_self_name = code.co_varnames[0] code_self = frame.f_locals[code_self_name] if code_self is self: frame = frame.f_back continue method = getattr(code_self, name) if method.__func__.__code__ is code: cls = type(code_self).__name__ except (AttributeError, IndexError, KeyError): pass break return cls, name def filter(self, record): cls, name = self.get_caller_info() labels = [] if cls: labels.append(cls) if name != '__call__': labels.append(name) record.methodName = ".".join(labels) return True #: Log configuration insert log_base_config = [ conf.cfg_item("type", log_type, 'Use "syslog" or "file"', default="file"), conf.cfg_item( "filename", str, "Plain log filename path", default=pth.splitext(sys.argv[0])[0] + ".log"), conf.cfg_item("socket", str, "Syslog socket path", default=None), conf.cfg_item("level", cast_loglevel, "Least message level to be logged", default="info"), conf.cfg_item("facility", cast_facility, "Syslog facility", default="local7"), conf.cfg_item( "stderr_level", cast_loglevel, "Least message level to be written to stderr", default="error") ]
[docs]def configure( type="file", filename=None, socket=None, level=logging.INFO, facility=logging.handlers.SysLogHandler.LOG_LOCAL7, stderr_level="error"): """ Simplify logging initialization. :param type: 'file' or 'syslog'. :param filename: Plain log filename path. :param socket: Syslog socket path. If not defined, various well known paths are tried. :param level: Least message level to be logged. :param facility: Syslog facility. :param stderr_level: Least message level to be logged to stderr. """ formatter_time = logging.Formatter(_form_time) logger = logging.getLogger() logger.propagate = False logger.addFilter(_IntrospectFilter()) logger.setLevel(min(stderr_level, level)) err_handler = logging.StreamHandler(sys.stderr) err_handler.setFormatter(formatter_time) err_handler.setLevel(stderr_level) logger.addHandler(err_handler) handler = None if type=="syslog": if not socket: for socket in _usual_sockets: if os.access(socket, os.W_OK) and stat.S_ISSOCK(os.stat(socket).st_mode): break try: handler = logging.handlers.SysLogHandler( address=socket, facility=facility) formatter = logging.Formatter(_form) except sock.error: logger.exception("Syslog socket not found.") else: try: handler = logging.handlers.WatchedFileHandler(filename, encoding="utf-8") formatter = formatter_time except IOError: logger.exception('Unable to open log file "%s".' % filename) if handler: handler.setFormatter(formatter) handler.setLevel(level) logger.addHandler(handler) return logger