Created
April 5, 2016 01:16
-
-
Save hectcastro/26ac42abf54834df39b24297298aa422 to your computer and use it in GitHub Desktop.
A Python log handler for Papertrail.
This file contains hidden or 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
import logging | |
import socket | |
class PapertrailHandler(logging.Handler): | |
"""An RFC 5425 log handler that has been tested to interact | |
with Papertrail. | |
""" | |
# RFC 5424 Severities | |
LOG_EMERG = 0 | |
LOG_ALERT = 1 | |
LOG_CRIT = 2 | |
LOG_ERR = 3 | |
LOG_WARNING = 4 | |
LOG_NOTICE = 5 | |
LOG_INFO = 6 | |
LOG_DEBUG = 7 | |
# RFC 5424 Facilities | |
LOG_KERN = 0 | |
LOG_USER = 1 | |
LOG_MAIL = 2 | |
LOG_DAEMON = 3 | |
LOG_AUTH = 4 | |
LOG_SYSLOG = 5 | |
LOG_LPR = 6 | |
LOG_NEWS = 7 | |
LOG_UUCP = 8 | |
LOG_CRON = 9 | |
LOG_AUTHPRIV = 10 | |
LOG_FTP = 11 | |
LOG_NTP = 12 | |
LOG_AUDIT = 13 | |
LOG_ALERT = 14 | |
LOG_AT = 15 | |
LOG_LOCAL0 = 16 | |
LOG_LOCAL1 = 17 | |
LOG_LOCAL2 = 18 | |
LOG_LOCAL3 = 19 | |
LOG_LOCAL4 = 20 | |
LOG_LOCAL5 = 21 | |
LOG_LOCAL6 = 22 | |
LOG_LOCAL7 = 23 | |
priority_names = { | |
'emerg': LOG_EMERG, | |
'alert': LOG_ALERT, | |
'crit': LOG_CRIT, | |
'err': LOG_ERR, | |
'warn': LOG_WARNING, | |
'notice': LOG_NOTICE, | |
'info': LOG_INFO, | |
'debug': LOG_DEBUG, | |
} | |
facility_names = { | |
'kern': LOG_KERN, | |
'user': LOG_USER, | |
'mail': LOG_MAIL, | |
'daemon': LOG_DAEMON, | |
'auth': LOG_AUTH, | |
'syslog': LOG_SYSLOG, | |
'lpr': LOG_LPR, | |
'news': LOG_NEWS, | |
'uucp': LOG_UUCP, | |
'cron': LOG_CRON, | |
'authpriv': LOG_AUTHPRIV, | |
'ftp': LOG_FTP, | |
'ntp': LOG_NTP, | |
'audit': LOG_AUDIT, | |
'alert': LOG_ALERT, | |
'at': LOG_AT, | |
'local0': LOG_LOCAL0, | |
'local1': LOG_LOCAL1, | |
'local2': LOG_LOCAL2, | |
'local3': LOG_LOCAL3, | |
'local4': LOG_LOCAL4, | |
'local5': LOG_LOCAL5, | |
'local6': LOG_LOCAL6, | |
'local7': LOG_LOCAL7, | |
} | |
def __init__(self, address=('localhost', 514), facility=LOG_USER, | |
socktype=socket.SOCK_STREAM): | |
"""Initialize the PapertrailHandler | |
Args: | |
address (tuple): First element is the host, second is the port | |
facility (int): Integer representation of the syslog facility | |
socktype (int): Integer representation of the socket type | |
""" | |
logging.Handler.__init__(self) | |
self.address = address | |
self.facility = facility | |
self.socktype = socktype | |
self.socket = socket.socket(socket.AF_INET, socktype) | |
if socktype == socket.SOCK_STREAM: | |
self.socket.connect(address) | |
def encode_priority(self, facility, priority): | |
"""Encode the syslog facility and priority. Strings or integers are | |
accepted. If strings are passed, the facility and priority mappings | |
are used to convert them to integers. | |
Args: | |
facility (str, int): A string or integer syslog facility | |
priority (str, int): A string or integer syslog priority | |
""" | |
if isinstance(facility, str): | |
facility = self.facility_names[facility] | |
if isinstance(priority, str): | |
priority = self.priority_names[priority] | |
return (facility << 3) | priority | |
def close(self): | |
"""Closes the socket.""" | |
self.acquire() | |
try: | |
self.socket.close() | |
logging.Handler.close(self) | |
finally: | |
self.release() | |
def clean_record(self, record): | |
"""Clean log entry by removing newlines and NUL bytes. | |
Args: | |
record (LogRecord): A log record | |
""" | |
return self.format( | |
record).replace('\n', ' ').replace('\r', ' ').replace('\x00', ' ') | |
def emit(self, record): | |
"""Emit a formatted record, which is then sent to the syslog server. | |
If exception information is present, the message is NOT sent to the | |
server. | |
Args: | |
record (str): A log entry | |
""" | |
try: | |
priority = self.encode_priority(self.facility, | |
self.priority_names.get( | |
record.levelname.lower(), | |
'warning')) | |
message = '<{}>{}\n'.format(priority, | |
self.clean_record(record)) | |
if self.socktype == socket.SOCK_DGRAM: | |
self.socket.sendto(message[:1024].encode('utf-8'), | |
self.address) | |
else: | |
self.socket.sendall('{}\n'.format(message).encode('utf-8')) | |
except Exception: | |
self.handleError(record) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @hectcastro,
This is great, however it does not use TLS to send logs over a secure TCP layer - Would you mind to include this configuration at some point within your class? It would be very useful!
Thanks in advance,