/usr/lib/python2.7/dist-packages/graypy/handler.py is in python-graypy 0.2.11-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | import sys
import logging
import json
import zlib
import traceback
import struct
import random
import socket
import math
from logging.handlers import DatagramHandler
WAN_CHUNK, LAN_CHUNK = 1420, 8154
PY3 = sys.version_info[0] == 3
if PY3:
string_type = str
integer_type = int,
else:
string_type = basestring
integer_type = (int, long)
class GELFHandler(DatagramHandler):
"""Graylog Extended Log Format handler
:param host: The host of the graylog server.
:param port: The port of the graylog server (default 12201).
:param chunk_size: Message chunk size. Messages larger than this
size will be sent to graylog in multiple chunks. Defaults to
`WAN_CHUNK=1420`.
:param debugging_fields: Send debug fields if true (the default).
:param extra_fields: Send extra fields on the log record to graylog
if true (the default).
:param fqdn: Use fully qualified domain name of localhost as source
host (socket.getfqdn()).
:param localname: Use specified hostname as source host.
:param facility: Replace facility with specified value. If specified,
record.name will be passed as `logger` parameter.
"""
def __init__(self, host, port=12201, chunk_size=WAN_CHUNK,
debugging_fields=True, extra_fields=True, fqdn=False,
localname=None, facility=None):
self.debugging_fields = debugging_fields
self.extra_fields = extra_fields
self.chunk_size = chunk_size
self.fqdn = fqdn
self.localname = localname
self.facility = facility
DatagramHandler.__init__(self, host, port)
def send(self, s):
if len(s) < self.chunk_size:
DatagramHandler.send(self, s)
else:
for chunk in ChunkedGELF(s, self.chunk_size):
DatagramHandler.send(self, chunk)
def makePickle(self, record):
message_dict = make_message_dict(
record, self.debugging_fields, self.extra_fields, self.fqdn,
self.localname, self.facility)
return zlib.compress(json.dumps(message_dict).encode('utf-8'))
class ChunkedGELF(object):
def __init__(self, message, size):
self.message = message
self.size = size
self.pieces = struct.pack('B', int(math.ceil(len(message) * 1.0/size)))
self.id = struct.pack('Q', random.randint(0, 0xFFFFFFFFFFFFFFFF))
def message_chunks(self):
return (self.message[i:i + self.size] for i
in range(0, len(self.message), self.size))
def encode(self, sequence, chunk):
return b''.join([
b'\x1e\x0f',
self.id,
struct.pack('B', sequence),
self.pieces,
chunk
])
def __iter__(self):
for sequence, chunk in enumerate(self.message_chunks()):
yield self.encode(sequence, chunk)
def make_message_dict(record, debugging_fields, extra_fields, fqdn, localname, facility=None):
if fqdn:
host = socket.getfqdn()
elif localname:
host = localname
else:
host = socket.gethostname()
fields = {'version': "1.0",
'host': host,
'short_message': record.getMessage(),
'full_message': get_full_message(record.exc_info, record.getMessage()),
'timestamp': record.created,
'level': SYSLOG_LEVELS.get(record.levelno, record.levelno),
'facility': facility or record.name,
}
if facility is not None:
fields.update({
'_logger': record.name
})
if debugging_fields:
fields.update({
'file': record.pathname,
'line': record.lineno,
'_function': record.funcName,
'_pid': record.process,
'_thread_name': record.threadName,
})
# record.processName was added in Python 2.6.2
pn = getattr(record, 'processName', None)
if pn is not None:
fields['_process_name'] = pn
if extra_fields:
fields = add_extra_fields(fields, record)
return fields
SYSLOG_LEVELS = {
logging.CRITICAL: 2,
logging.ERROR: 3,
logging.WARNING: 4,
logging.INFO: 6,
logging.DEBUG: 7,
}
def get_full_message(exc_info, message):
return '\n'.join(traceback.format_exception(*exc_info)) if exc_info else message
def add_extra_fields(message_dict, record):
# skip_list is used to filter additional fields in a log message.
# It contains all attributes listed in
# http://docs.python.org/library/logging.html#logrecord-attributes
# plus exc_text, which is only found in the logging module source,
# and id, which is prohibited by the GELF format.
skip_list = (
'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',
'funcName', 'id', 'levelname', 'levelno', 'lineno', 'module',
'msecs', 'message', 'msg', 'name', 'pathname', 'process',
'processName', 'relativeCreated', 'thread', 'threadName')
for key, value in record.__dict__.items():
if key not in skip_list and not key.startswith('_'):
if isinstance(value, (string_type, float) + integer_type):
message_dict['_%s' % key] = value
else:
message_dict['_%s' % key] = repr(value)
return message_dict
|