Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions example1.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@
'test_list': [1, 2, '3'],
}
test_logger.info('python-logstash: test extra fields', extra=extra)

class SomeClass:

def __init__(self):
self.a = 1
self.b = 1

def __repr__(self):
return "Test(a={}, b={})".format(self.a, self.b)

test_logger.info("test inner json: %s", SomeClass(), extra={"meta": {"test": SomeClass()}})

55 changes: 39 additions & 16 deletions logstash/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
import socket
import sys
from datetime import datetime
import six
from decimal import Decimal

try:
import json
except ImportError:
import simplejson as json


class LogstashFormatterBase(logging.Formatter):
skip_list = (
'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',
'funcName', 'id', 'levelname', 'levelno', 'lineno', 'module',
'msecs', 'msecs', 'message', 'msg', 'name', 'pathname', 'process',
'processName', 'relativeCreated', 'thread', 'threadName', 'extra',
'auth_token', 'password'
)

def __init__(self, message_type='Logstash', tags=None, fqdn=False):
self.message_type = message_type
Expand All @@ -20,29 +30,41 @@ def __init__(self, message_type='Logstash', tags=None, fqdn=False):
else:
self.host = socket.gethostname()

def _serialize_value(self, value):
# Recursively serializes value

if isinstance(value, dict):
return {
key: self._serialize_value(item)
for key, item in six.iteritems(value)
}
elif isinstance(value, (set, tuple, list, frozenset)):
return [
self._serialize_value(item)
for item in value
]
else:
if sys.version_info < (3, 0):
easy_types = (basestring, bool, dict, float, int, long, list, type(None))
else:
easy_types = (str, bool, dict, float, int, list, type(None))

if isinstance(value, easy_types):
return value
elif isinstance(value, Decimal):
return float(value)
else:
return repr(value)

def get_extra_fields(self, record):
# The list contains all the attributes listed in
# http://docs.python.org/library/logging.html#logrecord-attributes
skip_list = (
'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',
'funcName', 'id', 'levelname', 'levelno', 'lineno', 'module',
'msecs', 'msecs', 'message', 'msg', 'name', 'pathname', 'process',
'processName', 'relativeCreated', 'thread', 'threadName', 'extra',
'auth_token', 'password')

if sys.version_info < (3, 0):
easy_types = (basestring, bool, dict, float, int, long, list, type(None))
else:
easy_types = (str, bool, dict, float, int, list, type(None))

fields = {}

for key, value in record.__dict__.items():
if key not in skip_list:
if isinstance(value, easy_types):
fields[key] = value
else:
fields[key] = repr(value)
if key not in self.skip_list:
fields[key] = self._serialize_value(value)

return fields

Expand Down Expand Up @@ -140,3 +162,4 @@ def format(self, record):
message.update(self.get_debug_fields(record))

return self.serialize(message)

20 changes: 15 additions & 5 deletions logstash/handler_tcp.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from logging.handlers import DatagramHandler, SocketHandler
from logstash import formatter


# Derive from object to force a new-style class and thus allow super() to work
# on Python 2.6
from logstash import LogstashFormatterVersion0
from logstash import LogstashFormatterVersion1


class TCPLogstashHandler(SocketHandler, object):
"""Python logging handler for Logstash. Sends events over TCP.
:param host: The host of the logstash server.
Expand All @@ -12,14 +15,21 @@ class TCPLogstashHandler(SocketHandler, object):
:param fqdn; Indicates whether to show fully qualified domain name or not (default False).
:param version: version of logstash event schema (default is 0).
:param tags: list of tags for a logger (default is None).
:param fmt: custom formatter instance
"""

def __init__(self, host, port=5959, message_type='logstash', tags=None, fqdn=False, version=0):
def __init__(self, host, port=5959, message_type='logstash', tags=None, fqdn=False, version=0,
formatterCls=None):
super(TCPLogstashHandler, self).__init__(host, port)
if version == 1:
self.formatter = formatter.LogstashFormatterVersion1(message_type, tags, fqdn)

if not formatterCls:
if version == 1:
self.formatter = LogstashFormatterVersion1(message_type, tags, fqdn)
else:
self.formatter = LogstashFormatterVersion0(message_type, tags, fqdn)
else:
self.formatter = formatter.LogstashFormatterVersion0(message_type, tags, fqdn)
self.formatter = formatterCls(message_type, tags, fqdn)


def makePickle(self, record):
return self.formatter.format(record) + b'\n'
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from distutils.core import setup
from setuptools import setup

setup(
name='python-logstash',
packages=['logstash'],
version='0.4.7',
install_requires=["six"],
description='Python logging handler for Logstash.',
long_description=open('README.rst').read(),
license='MIT',
Expand Down