Skip to content

Instantly share code, notes, and snippets.

@hectcastro
Created April 5, 2016 01:16
Show Gist options
  • Save hectcastro/26ac42abf54834df39b24297298aa422 to your computer and use it in GitHub Desktop.
Save hectcastro/26ac42abf54834df39b24297298aa422 to your computer and use it in GitHub Desktop.
A Python log handler for Papertrail.
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)
@kitos9112
Copy link

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,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment