Last active
January 4, 2018 18:50
-
-
Save sveinse/041c69b6bbdde83cf5b00fecae3dcd50 to your computer and use it in GitHub Desktop.
Proposed syslog observer for Twisted
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (c) Twisted Matrix Laboratories. | |
# See LICENSE for details. | |
""" | |
Syslog log observer. | |
""" | |
syslog = __import__('syslog') | |
from zope.interface import implementer | |
from ._observer import ILogObserver | |
from ._format import formatEvent | |
from ._logger import LogLevel | |
# These defaults come from the Python syslog docs. | |
DEFAULT_OPTIONS = 0 | |
DEFAULT_FACILITY = syslog.LOG_USER | |
# Map the twisted LogLevels up against the syslog values | |
LOGLEVEL_MAP = { | |
LogLevel.debug: syslog.LOG_DEBUG, | |
LogLevel.info: syslog.LOG_INFO, | |
LogLevel.warn: syslog.LOG_WARNING, | |
LogLevel.error: syslog.LOG_ERR, | |
LogLevel.critical: syslog.LOG_CRIT, | |
} | |
@implementer(ILogObserver) | |
class SyslogObserver(object): | |
""" | |
A log observer for logging to syslog. | |
See L{twisted.logger} for context. | |
The observer will use 'C{log_level}' to determine syslog loglevel. If no | |
'C{log_level}' is in the event, it will use 'C{critical}' for failures, | |
and 'C{info}' for others. The syslog facility can be set using | |
'C{log_facility}', otherwise the default value in the constructor will be | |
used. | |
""" | |
openlog = syslog.openlog | |
syslog = syslog.syslog | |
def __init__(self, prefix, options=DEFAULT_OPTIONS, | |
facility=DEFAULT_FACILITY, dumpTraceback=False): | |
""" | |
@type prefix: C{str} | |
@param prefix: The syslog prefix to use. | |
@type options: C{int} | |
@param options: A bitvector represented as an integer of the syslog | |
options to use. | |
@type facility: C{int} | |
@param facility: An indication to the syslog daemon of what sort of | |
program this is (essentially, an additional arbitrary metadata | |
classification for messages sent to syslog by this observer). | |
@type dumpTraceback: C{bool} | |
@param dumpTraceback: If the traceback shall be printed to the syslog | |
in the case of failure log events. | |
""" | |
self.openlog(prefix, options, facility) | |
self.dumpTraceback = dumpTraceback | |
def __call__(self, event): | |
""" | |
Write event to syslog. | |
@param event: An event. | |
@type event: L{dict} | |
""" | |
# Figure out what the message-text is. | |
text = formatEvent(event) | |
if self.dumpTraceback and "log_failure" in event: | |
try: | |
traceback = event["log_failure"].getTraceback() | |
except: | |
traceback = u"(UNABLE TO OBTAIN TRACEBACK FROM EVENT)\n" | |
text = u"\n".join((text, traceback)) | |
if text is None: | |
return | |
# Figure out what syslog parameters we might need to use. | |
level = event.get("log_level", None) | |
if level is None: | |
if 'log_failure' in event: | |
level = LogLevel.critical | |
else: | |
level = LogLevel.info | |
priority = LOGLEVEL_MAP[level] | |
facility = int(event.get('log_facility', DEFAULT_FACILITY)) | |
# Break the message up into lines and send them. | |
lines = text.split('\n') | |
while lines[-1:] == ['']: | |
lines.pop() | |
firstLine = True | |
for line in lines: | |
if firstLine: | |
firstLine = False | |
else: | |
line = ' ' + line | |
self.syslog(priority | facility, | |
'[%s] %s' % (event.get('log_system', '-'), line)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#-*- python -*- | |
""" | |
Log functions. | |
""" | |
import sys | |
from zope.interface import implementer | |
from twisted.logger import Logger | |
from twisted.logger import ILogObserver | |
from twisted.logger import formatTime | |
from twisted.logger import formatEvent | |
from twisted.logger import globalLogBeginner | |
from twisted.logger import globalLogPublisher | |
from twisted.logger import FileLogObserver | |
from twisted.python.compat import unicode | |
from _syslog import SyslogObserver | |
__all__ = ["start", "Logger"] | |
# Mod of twisted.logger.formatEventAsClassicLogText() | |
def formatLuminaLogText(event, formatTime=formatTime): | |
""" | |
Format an event as a line of human-readable text for, e.g. traditional log | |
file output. | |
""" | |
eventText = formatEvent(event) | |
if "log_failure" in event: | |
try: | |
traceback = event["log_failure"].getTraceback() | |
except: | |
traceback = u"(UNABLE TO OBTAIN TRACEBACK FROM EVENT)\n" | |
eventText = u"\n".join((eventText, traceback)) | |
if not eventText: | |
return None | |
eventText = eventText.replace(u"\n", u"\n\t") | |
timeStamp = formatTime(event.get("log_time", None)) | |
level = event.get("log_level", None) | |
if level is None: | |
levelName = u"-" | |
else: | |
levelName = level.name | |
system = event.get("log_system", None) | |
return u"{timeStamp} {level:<8} [{system}] {event}\n".format( | |
timeStamp=timeStamp, | |
level=levelName, | |
system=system, | |
event=eventText, | |
) | |
@implementer(ILogObserver) | |
class LuminaLogFormatter(object): | |
''' Logging observer for Lumina ''' | |
def __call__(self, event): | |
''' Main dispatcher for log-events ''' | |
# We want log_system to always be valid (SyslogObserver uses it) | |
system = event.get("log_system", None) | |
if system is None: | |
system = event.get("log_namespace", u"-") | |
else: | |
try: | |
system = unicode(system) | |
except Exception: | |
system = u"UNFORMATTABLE" | |
event['log_system'] = system | |
def start(syslog=False, logfile=None, syslog_prefix='lumina', redirect_stdio=True): | |
''' Start the custom logger ''' | |
# System defaults from twisted.logger._global.py: | |
# globalLogPublisher = LogPublisher() | |
# globalLogBeginner = LogBeginner(globalLogPublisher, sys.stderr, sys, warnings) | |
if logfile is None: | |
logfile = sys.stdout | |
# Lumina log observers | |
if syslog: | |
out_observer = SyslogObserver(prefix=syslog_prefix, dumpTraceback=True) | |
else: | |
out_observer = FileLogObserver(sys.stdout, formatLuminaLogText) | |
observers = [ | |
LuminaLogFormatter(), | |
out_observer, | |
] | |
# This logger will take over the system (the default LogPublisher). It will | |
# iterate over any messages that has already been logged prior to | |
# this registration. However, any errors in the observers will be silently | |
# ignored because the observers are no longer run through the | |
# LogPublisher() | |
globalLogBeginner.beginLoggingTo(observers, | |
redirectStandardIO=redirect_stdio) | |
# Alternatively, register the observers using the default (global) | |
# LogPublisher (found in twisted.logger._observer.py) . The upside with | |
# this approach is that any errors in the observes will be catched and | |
# printed. The downside is that it is not possible to prevent redirection | |
# of stdio and it will not replay pre-logger entries prior to installation. | |
# It will also print failure messages >=critical to sys.stderr because | |
# _temporaryObserver is still installed. (see self._temporaryObserver in | |
# LogBeginner) | |
#for obs in observers: | |
# globalLogPublisher.addObserver(obs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment