core (log): logging with Twisted now uses the new twisted.logger
This commit is contained in:
parent
4fdf49f4cb
commit
c0a3f8d141
2 changed files with 51 additions and 144 deletions
|
|
@ -37,7 +37,7 @@ class Filtered(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class Logger(object):
|
||||
class Logger:
|
||||
"""High level logging class"""
|
||||
fmt = None # format option as given by user (e.g. SAT_LOG_LOGGER)
|
||||
filter_name = None # filter to call
|
||||
|
|
@ -163,7 +163,7 @@ class FilterName(object):
|
|||
return self.filter(log_record) == 1
|
||||
|
||||
|
||||
class ConfigureBase(object):
|
||||
class ConfigureBase:
|
||||
LOGGER_CLASS = Logger
|
||||
# True if color location is specified in fmt (with COLOR_START)
|
||||
_color_location = False
|
||||
|
|
|
|||
|
|
@ -29,20 +29,22 @@ class TwistedLogger(log.Logger):
|
|||
force_colors = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TwistedLogger, self).__init__(*args, **kwargs)
|
||||
from twisted.python import log as twisted_log
|
||||
|
||||
self.twisted_log = twisted_log
|
||||
super().__init__(*args, **kwargs)
|
||||
from twisted.logger import Logger
|
||||
self.twisted_log = Logger()
|
||||
|
||||
def out(self, message, level=None, **kwargs):
|
||||
"""Actually log the message
|
||||
|
||||
@param message: formatted message
|
||||
"""
|
||||
if kwargs.get('exc_info', False):
|
||||
if kwargs.pop('exc_info', False):
|
||||
message = self.addTraceback(message)
|
||||
self.twisted_log.msg(
|
||||
message.encode("utf-8", "ignore"), sat_logged=True, level=level
|
||||
self.twisted_log.emit(
|
||||
level=self.level_map[level],
|
||||
format=message,
|
||||
sat_logged=True,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -107,148 +109,52 @@ class ConfigureBasic(log.ConfigureBase):
|
|||
class ConfigureTwisted(ConfigureBasic):
|
||||
LOGGER_CLASS = TwistedLogger
|
||||
|
||||
def changeObserver(self, observer, can_colors=False):
|
||||
"""Install a hook on observer to manage SàT specificities
|
||||
|
||||
@param observer: original observer to hook
|
||||
@param can_colors: True if observer can display ansi colors
|
||||
"""
|
||||
|
||||
def observer_hook(event):
|
||||
"""redirect non SàT log to twisted_logger, and add colors when possible"""
|
||||
if (
|
||||
"sat_logged" in event
|
||||
): # we only want our own logs, other are managed by twistedObserver
|
||||
# we add colors if possible
|
||||
if (
|
||||
can_colors and self.LOGGER_CLASS.colors
|
||||
) or self.LOGGER_CLASS.force_colors:
|
||||
message = event.get("message", tuple())
|
||||
if message:
|
||||
event["message"] = (b"".join(message),) # must be a tuple
|
||||
observer(event) # we can now call the original observer
|
||||
|
||||
return observer_hook
|
||||
|
||||
def changeFileLogObserver(self, observer):
|
||||
"""Install SàT hook for FileLogObserver
|
||||
|
||||
if the output is a tty, we allow colors, else we don't
|
||||
@param observer: original observer to hook
|
||||
"""
|
||||
log_obs = observer.__self__
|
||||
log_file = log_obs.write.__self__
|
||||
try:
|
||||
can_colors = log_file.isatty()
|
||||
except AttributeError:
|
||||
can_colors = False
|
||||
return self.changeObserver(observer, can_colors=can_colors)
|
||||
|
||||
def installObserverHook(self, observer):
|
||||
"""Check observer type and install SàT hook when possible
|
||||
|
||||
@param observer: observer to hook
|
||||
@return: hooked observer or original one
|
||||
"""
|
||||
if hasattr(observer, "__self__"):
|
||||
ori = observer
|
||||
if isinstance(observer.__self__, self.twisted_log.FileLogObserver):
|
||||
observer = self.changeFileLogObserver(observer)
|
||||
elif isinstance(observer.__self__, self.twisted_log.DefaultObserver):
|
||||
observer = self.changeObserver(observer, can_colors=True)
|
||||
else:
|
||||
# we use print because log system is not fully initialized
|
||||
print(("Unmanaged observer [%s]" % observer))
|
||||
return observer
|
||||
self.observers[ori] = observer
|
||||
return observer
|
||||
|
||||
def preTreatment(self):
|
||||
"""initialise needed attributes, and install observers hooks"""
|
||||
self.observers = {}
|
||||
from twisted.python import log as twisted_log
|
||||
|
||||
self.twisted_log = twisted_log
|
||||
self.log_publisher = twisted_log.msg.__self__
|
||||
|
||||
def addObserverObserver(self_logpub, other):
|
||||
"""Install hook so we know when a new observer is added"""
|
||||
other = self.installObserverHook(other)
|
||||
return self_logpub._originalAddObserver(other)
|
||||
|
||||
def removeObserverObserver(self_logpub, ori):
|
||||
"""removeObserver hook fix
|
||||
|
||||
As we wrap the original observer, the original removeObserver may want to remove the original object instead of the wrapper, this method fix this
|
||||
"""
|
||||
if ori in self.observers:
|
||||
self_logpub._originalRemoveObserver(self.observers[ori])
|
||||
else:
|
||||
try:
|
||||
self_logpub._originalRemoveObserver(ori)
|
||||
except ValueError:
|
||||
try:
|
||||
ori in self.cleared_observers
|
||||
except AttributeError:
|
||||
raise ValueError("Unknown observer")
|
||||
|
||||
# we replace addObserver/removeObserver by our own
|
||||
twisted_log.LogPublisher._originalAddObserver = (
|
||||
twisted_log.LogPublisher.addObserver
|
||||
)
|
||||
twisted_log.LogPublisher._originalRemoveObserver = (
|
||||
twisted_log.LogPublisher.removeObserver
|
||||
)
|
||||
import types # see https://stackoverflow.com/a/4267590 (thx Chris Morgan/aaronasterling)
|
||||
|
||||
twisted_log.addObserver = types.MethodType(
|
||||
addObserverObserver, self.log_publisher
|
||||
)
|
||||
twisted_log.removeObserver = types.MethodType(
|
||||
removeObserverObserver, self.log_publisher
|
||||
)
|
||||
|
||||
# we now change existing observers
|
||||
for idx, observer in enumerate(self.log_publisher.observers):
|
||||
self.log_publisher.observers[idx] = self.installObserverHook(observer)
|
||||
from twisted import logger
|
||||
global logger
|
||||
self.level_map = {
|
||||
C.LOG_LVL_DEBUG: logger.LogLevel.debug,
|
||||
C.LOG_LVL_INFO: logger.LogLevel.info,
|
||||
C.LOG_LVL_WARNING: logger.LogLevel.warn,
|
||||
C.LOG_LVL_ERROR: logger.LogLevel.error,
|
||||
C.LOG_LVL_CRITICAL: logger.LogLevel.critical,
|
||||
}
|
||||
self.LOGGER_CLASS.level_map = self.level_map
|
||||
|
||||
def configureLevel(self, level):
|
||||
self.LOGGER_CLASS.level = level
|
||||
super(ConfigureTwisted, self).configureLevel(level)
|
||||
self.level = self.level_map[level]
|
||||
|
||||
def configureOutput(self, output):
|
||||
import sys
|
||||
from twisted.python import logfile
|
||||
self.log_publisher = logger.LogPublisher()
|
||||
|
||||
if output is None:
|
||||
output = C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT
|
||||
self.manageOutputs(output)
|
||||
addObserver = self.twisted_log.addObserver
|
||||
|
||||
if C.LOG_OPT_OUTPUT_DEFAULT in log.handlers:
|
||||
# default output is already managed, we just add output to stdout if we are in debug or nodaemon mode
|
||||
if self.backend_data is None:
|
||||
raise ValueError(
|
||||
"You must pass options as backend_data with Twisted backend"
|
||||
)
|
||||
options = self.backend_data
|
||||
log_file = logfile.LogFile.fromFullPath(options['logfile'])
|
||||
self.log_publisher.addObserver(
|
||||
logger.FileLogObserver(log_file, self.textFormatter))
|
||||
# we also want output to stdout if we are in debug or nodaemon mode
|
||||
if options.get("nodaemon", False) or options.get("debug", False):
|
||||
addObserver(self.twisted_log.FileLogObserver(sys.stdout).emit)
|
||||
else:
|
||||
# \\default is not in the output, so we remove current observers
|
||||
self.cleared_observers = self.log_publisher.observers
|
||||
self.observers.clear()
|
||||
del self.log_publisher.observers[:]
|
||||
# and we forbid twistd to add any observer
|
||||
self.twisted_log.addObserver = lambda other: None
|
||||
self.log_publisher.addObserver(
|
||||
logger.FileLogObserver(sys.stdout, self.textFormatter))
|
||||
|
||||
if C.LOG_OPT_OUTPUT_FILE in log.handlers:
|
||||
from twisted.python import logfile
|
||||
|
||||
for path in log.handlers[C.LOG_OPT_OUTPUT_FILE]:
|
||||
log_file = (
|
||||
sys.stdout if path == "-" else logfile.LogFile.fromFullPath(path)
|
||||
)
|
||||
addObserver(self.twisted_log.FileLogObserver(log_file).emit)
|
||||
self.log_publisher.addObserver(
|
||||
logger.FileLogObserver(log_file, self.textFormatter))
|
||||
|
||||
if C.LOG_OPT_OUTPUT_MEMORY in log.handlers:
|
||||
raise NotImplementedError(
|
||||
|
|
@ -266,25 +172,26 @@ class ConfigureTwisted(ConfigureBasic):
|
|||
|
||||
def postTreatment(self):
|
||||
"""Install twistedObserver which manage non SàT logs"""
|
||||
# from twisted import logger
|
||||
import sys
|
||||
filtering_obs = logger.FilteringLogObserver(
|
||||
observer=self.log_publisher,
|
||||
predicates=[
|
||||
logger.LogLevelFilterPredicate(self.level),
|
||||
]
|
||||
)
|
||||
logger.globalLogBeginner.beginLoggingTo([filtering_obs])
|
||||
|
||||
def twistedObserver(event):
|
||||
"""Observer which redirect log message not produced by SàT to SàT logging system"""
|
||||
if not "sat_logged" in event:
|
||||
# this log was not produced by SàT
|
||||
from twisted.python import log as twisted_log
|
||||
|
||||
text = twisted_log.textFromEventDict(event)
|
||||
if text is None:
|
||||
return
|
||||
twisted_logger = log.getLogger(C.LOG_TWISTED_LOGGER)
|
||||
log_method = (
|
||||
twisted_logger.error
|
||||
if event.get("isError", False)
|
||||
else twisted_logger.info
|
||||
)
|
||||
log_method(text)
|
||||
|
||||
self.log_publisher._originalAddObserver(twistedObserver)
|
||||
def textFormatter(self, event):
|
||||
if event.get('sat_logged', False):
|
||||
timestamp = ''.join([logger.formatTime(event.get("log_time", None)), " "])
|
||||
return f"{timestamp}{event.get('log_format', '')}\n"
|
||||
else:
|
||||
eventText = logger.eventAsText(
|
||||
event, includeSystem=True)
|
||||
if not eventText:
|
||||
return None
|
||||
return eventText.replace("\n", "\n\t") + "\n"
|
||||
|
||||
|
||||
class ConfigureStandard(ConfigureBasic):
|
||||
|
|
|
|||
Loading…
Reference in a new issue