# -*- 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")
]