frappy/secop/logging.py
Markus Zolliker 8cb3ba7dfc fix error in secop.logging
Implement LogfileHandler.getChild. This is needed to inherit
the configured level from the parent handler.

+ remove redundant assignmet of logfile_handler.max_days

Change-Id: I7277c28221bbb6108d75f2437634e9db9bf6761e
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/28140
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2022-04-22 16:35:24 +02:00

172 lines
5.7 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
import os
from os.path import dirname, join
from logging import DEBUG, INFO, addLevelName
import mlzlog
from secop.lib import generalConfig
from secop.datatypes import BoolType
from secop.properties import Property
OFF = 99
COMLOG = 15
addLevelName(COMLOG, 'COMLOG')
assert DEBUG < COMLOG < INFO
LOG_LEVELS = dict(mlzlog.LOGLEVELS, off=OFF, comlog=COMLOG)
LEVEL_NAMES = {v: k for k, v in LOG_LEVELS.items()}
def check_level(level):
try:
if isinstance(level, str):
return LOG_LEVELS[level.lower()]
if level in LEVEL_NAMES:
return level
except KeyError:
pass
raise ValueError('%r is not a valid level' % level)
class RemoteLogHandler(mlzlog.Handler):
"""handler for remote logging"""
def __init__(self):
super().__init__()
self.subscriptions = {} # dict[modname] of tuple(mobobj, dict [conn] of level)
def emit(self, record):
"""unused"""
def handle(self, record):
modname = record.name.split('.')[-1]
try:
modobj, subscriptions = self.subscriptions[modname]
except KeyError:
return
for conn, lev in subscriptions.items():
if record.levelno >= lev:
modobj.DISPATCHER.send_log_msg(
conn, modobj.name, LEVEL_NAMES[record.levelno],
record.getMessage())
def set_conn_level(self, modobj, conn, level):
level = check_level(level)
modobj, subscriptions = self.subscriptions.setdefault(modobj.name, (modobj, {}))
if level == OFF:
subscriptions.pop(conn, None)
else:
subscriptions[conn] = level
def __repr__(self):
return 'RemoteLogHandler()'
class LogfileHandler(mlzlog.LogfileHandler):
def __init__(self, logdir, rootname, max_days=0):
self.rootname = rootname
self.max_days = max_days
super().__init__(logdir, rootname)
def emit(self, record):
if record.levelno != COMLOG:
super().emit(record)
def getChild(self, name):
child = type(self)(dirname(self.baseFilename), name, self.max_days)
child.setLevel(self.level)
return child
def doRollover(self):
super().doRollover()
if self.max_days:
# keep only the last max_days files
with os.scandir(dirname(self.baseFilename)) as it:
files = sorted(entry.path for entry in it if entry.name != 'current')
for filepath in files[-self.max_days:]:
os.remove(filepath)
class ComLogfileHandler(LogfileHandler):
"""handler for logging communication"""
def format(self, record):
return '%s %s' % (self.formatter.formatTime(record), record.getMessage())
class HasComlog:
"""mixin for modules with comlog"""
comlog = Property('whether communication is logged ', BoolType(),
default=True, export=False)
_comLog = None
def earlyInit(self):
super().earlyInit()
if self.comlog and generalConfig.initialized and generalConfig.comlog:
self._comLog = mlzlog.Logger('COMLOG.%s' % self.name)
self._comLog.handlers[:] = []
directory = join(logger.logdir, logger.rootname, 'comlog', self.DISPATCHER.name)
self._comLog.addHandler(ComLogfileHandler(
directory, self.name, max_days=generalConfig.getint('comlog_days', 7)))
return
def comLog(self, msg, *args, **kwds):
self.log.log(COMLOG, msg, *args, **kwds)
if self._comLog:
self._comLog.info(msg, *args)
class MainLogger:
def __init__(self):
self.log = None
self.logdir = None
self.rootname = None
self.console_handler = None
def init(self, console_level='info'):
self.rootname = generalConfig.get('logger_root', 'frappy')
# set log level to minimum on the logger, effective levels on the handlers
# needed also for RemoteLogHandler
# modified from mlzlog.initLogging
mlzlog.setLoggerClass(mlzlog.MLZLogger)
assert self.log is None
self.log = mlzlog.log = mlzlog.MLZLogger(self.rootname)
self.log.setLevel(DEBUG)
self.log.addHandler(mlzlog.ColoredConsoleHandler())
self.logdir = generalConfig.get('logdir', '/tmp/log')
if self.logdir:
logfile_days = generalConfig.getint('logfile_days')
logfile_handler = LogfileHandler(self.logdir, self.rootname, max_days=logfile_days)
logfile_handler.setLevel(LOG_LEVELS[generalConfig.get('logfile_level', 'info')])
self.log.addHandler(logfile_handler)
self.log.addHandler(RemoteLogHandler())
self.log.handlers[0].setLevel(LOG_LEVELS[console_level])
logger = MainLogger()