Skip to content

Commit 5b135d6

Browse files
committed
add new config for limits
1 parent 687c0dd commit 5b135d6

10 files changed

+84
-22
lines changed

README.rst

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This fork has some added features:
77
- support for nested data structures
88
- support for set as datatype
99
- new http handler with support for ssl and basic auth
10+
- New config for limiting strings, containers and stacktraces
11+
- Version 0 Logging has been removed
1012

1113
http://logstash.net/
1214

example1.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
test_logger = logging.getLogger('python-logstash-logger')
88
test_logger.setLevel(logging.INFO)
9-
test_logger.addHandler(logstash.LogstashHandler(host, 5959, version=1))
10-
# test_logger.addHandler(logstash.TCPLogstashHandler(host, 5959, version=1))
9+
# test_logger.addHandler(logstash.LogstashHandler(host, 5959, version=1))
10+
test_logger.addHandler(logstash.TCPLogstashHandler(host, 5959, limit_containers=6, limit_stacktrace=10000,
11+
limit_string_fields=1000))
1112

1213
test_logger.error('python-logstash: test logstash error message.')
1314
test_logger.info('python-logstash: test logstash info message.')
@@ -20,6 +21,18 @@
2021
'test_dict': {'a': 1, 'b': set(['a'])},
2122
'test_float': 1.23,
2223
'test_integer': 123,
23-
'test_list': [1, 2, 3],
24+
'test_list': [1, 2, 3, 4, 5, 6, 7, 8 ],
2425
}
2526
test_logger.info('python-logstash: test extra fields', extra=extra)
27+
28+
extra['test_string'] = "A" * 2000
29+
extra['test_dict'].update({
30+
'c': 3,
31+
'd': 4,
32+
'e': 5,
33+
'f': 6,
34+
'g': 7,
35+
'h': 8
36+
})
37+
test_logger.info('python-logstash: test extra fields 2', extra=extra)
38+

example2.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
test_logger.setLevel(logging.INFO)
1313

1414
# add the handler
15-
test_logger.addHandler(logstash.AMQPLogstashHandler(version=1,
16-
host=host,
15+
test_logger.addHandler(logstash.AMQPLogstashHandler(host=host,
1716
durable=True,
1817
username=username,
1918
password=password,

example3.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
test_logger = logging.getLogger('python-logstash-logger')
88
test_logger.setLevel(logging.INFO)
9-
test_logger.addHandler(logstash.HTTPLogstashHandler(host, 1337, version=1, ssl=True, verify=True, username="user", password="pw"))
9+
test_logger.addHandler(logstash.HTTPLogstashHandler(host, 1337, ssl=True, verify=True, username="user", password="pw"))
1010
# test_logger.addHandler(logstash.TCPLogstashHandler(host, 5959, version=1))
1111

1212
test_logger.error('python-logstash: test logstash error message.')

logstash/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
from logstash.formatter import LogstashFormatterVersion0, LogstashFormatterVersion1
2+
from logstash.formatter import LogstashFormatter
33

44
from logstash.handler_tcp import TCPLogstashHandler
55
from logstash.handler_udp import UDPLogstashHandler, LogstashHandler

logstash/formatter.py

+36-9
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,25 @@
1111

1212
class LogstashFormatterBase(logging.Formatter):
1313

14-
def __init__(self, message_type='Logstash', tags=None, fqdn=False):
14+
limit_stacktrace = 0
15+
16+
def __init__(self, message_type='Logstash', tags=None, fqdn=False, limit_stacktrace=0, limit_string_fields=0, limit_containers=0):
1517
self.message_type = message_type
1618
self.tags = tags if tags is not None else []
19+
LogstashFormatterBase.limit_stacktrace = limit_stacktrace
20+
self.limit_string_fields = limit_string_fields
21+
self.limit_containers = limit_containers
1722

1823
if fqdn:
1924
self.host = socket.getfqdn()
2025
else:
2126
self.host = socket.gethostname()
2227

28+
def limit_string_field(self, value):
29+
if self.limit_string_fields == 0:
30+
return value
31+
return value[:self.limit_string_fields] + (value[self.limit_string_fields:] and '...Truncated...')
32+
2333
def get_extra_fields(self, record, first=True):
2434
# The list contains all the attributes listed in
2535
# http://docs.python.org/library/logging.html#logrecord-attributes
@@ -31,40 +41,54 @@ def get_extra_fields(self, record, first=True):
3141
'auth_token', 'password')
3242

3343
if sys.version_info < (3, 0):
34-
easy_types = (basestring, bool, dict, float, int, long, list, type(None))
44+
easy_types = (basestring, bool, float, int, long, type(None))
3545
else:
36-
easy_types = (str, bool, dict, float, int, list, type(None))
46+
easy_types = (str, bool, float, int, type(None))
3747
containers = (dict, set, list)
3848
fields = {}
3949
if first:
4050
for key, value in record.__dict__.items():
4151
if key not in skip_list or not first:
4252
if isinstance(value, containers):
4353
fields[key] = self.get_extra_fields(value, first=False)
54+
elif type(value) == str:
55+
fields[key] = self.limit_string_field(value)
4456
elif isinstance(value, easy_types):
4557
fields[key] = value
4658
else:
47-
fields[key] = repr(value)
59+
fields[key] = self.limit_string_field(repr(value))
4860
elif type(record) == dict:
61+
counter = 0
4962
for key, value in record.items():
63+
counter += 1
64+
if self.limit_containers != 0 and counter > self.limit_containers:
65+
fields['WARNING'] = "...Truncated..."
66+
break
5067
if isinstance(value, containers):
5168
fields[key] = self.get_extra_fields(value, first=False)
69+
elif type(value) == str:
70+
fields[key] = self.limit_string_field(value)
5271
elif isinstance(value, easy_types):
5372
fields[key] = value
5473
else:
55-
fields[key] = repr(value)
74+
fields[key] = self.limit_string_field(repr(value))
5675
elif type(record) == list or type(record) == set:
5776
tmp = []
77+
counter = 0
5878
for value in record:
79+
counter += 1
80+
if self.limit_containers != 0 and counter > self.limit_containers:
81+
tmp.append("...Truncated...")
82+
break
5983
tmp.append(self.get_extra_fields(value, first=False))
6084
return tmp
6185
else:
86+
if type(record) == str:
87+
return self.limit_string_field(record)
6288
if isinstance(record, easy_types):
6389
return record
6490
else:
65-
return repr(record)
66-
67-
91+
return self.limit_string_field(repr(record))
6892

6993
return fields
7094

@@ -97,7 +121,10 @@ def format_timestamp(cls, time):
97121

98122
@classmethod
99123
def format_exception(cls, exc_info):
100-
return ''.join(traceback.format_exception(*exc_info)) if exc_info else ''
124+
stacktrace = ''.join(traceback.format_exception(*exc_info)) if exc_info else ''
125+
if cls.limit_stacktrace == 0:
126+
return stacktrace
127+
return stacktrace[:cls.limit_stacktrace] + (stacktrace[cls.limit_stacktrace:] and '..')
101128

102129
@classmethod
103130
def serialize(cls, message):

logstash/handler_amqp.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@ class AMQPLogstashHandler(SocketHandler, object):
4040
host (socket.getfqdn()).
4141
:param facility: Replace facility with specified value. If specified,
4242
record.name will be passed as `logger` parameter.
43+
:param limit_stacktrace: limit characters for stacktraces
44+
:param limit_string_fields: limit characters for string fields
45+
:param limit_containers: limit length of containers (dict, list, set)
4346
"""
4447

4548
def __init__(self, host='localhost', port=5672, username='guest',
4649
password='guest', exchange='logstash', exchange_type='fanout',
4750
virtual_host='/', message_type='logstash', tags=None,
4851
durable=False, passive=False, extra_fields=True,
49-
fqdn=False, facility=None, exchange_routing_key=''):
52+
fqdn=False, facility=None, exchange_routing_key='',
53+
limit_stacktrace=0, limit_string_fields=0, limit_containers=0):
5054

5155

5256
# AMQP parameters
@@ -65,7 +69,9 @@ def __init__(self, host='localhost', port=5672, username='guest',
6569

6670
# Extract Logstash paramaters
6771
self.tags = tags or []
68-
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn)
72+
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn, limit_stacktrace=limit_stacktrace,
73+
limit_string_fields=limit_string_fields,
74+
limit_containers=limit_containers)
6975

7076
# Standard logging parameters
7177
self.extra_fields = extra_fields

logstash/handler_http.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ class HTTPLogstashHandler(NullHandler, object):
1818
:param verify: verify ssl (default is True)
1919
:param username: basic_auth user (default is None)
2020
:param password: basic_auth user (default is None)
21+
:param limit_stacktrace: limit characters for stacktraces
22+
:param limit_string_fields: limit characters for string fields
23+
:param limit_containers: limit length of containers (dict, list, set)
2124
"""
2225

23-
def __init__(self, host, port=80, ssl=False, message_type='logstash', tags=None, fqdn=False, verify=True, username=None, password=None):
26+
def __init__(self, host, port=80, ssl=False, message_type='logstash', tags=None, fqdn=False, verify=True,
27+
username=None, password=None, limit_stacktrace=0, limit_string_fields=0, limit_containers=0):
2428
super(NullHandler, self).__init__()
2529

26-
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn)
30+
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn, limit_stacktrace=limit_stacktrace,
31+
limit_string_fields=limit_string_fields,
32+
limit_containers=limit_containers)
2733

2834
if username and password:
2935
self.auth = HTTPBasicAuth(username, password)

logstash/handler_tcp.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ class TCPLogstashHandler(SocketHandler, object):
1111
:param message_type: The type of the message (default logstash).
1212
:param fqdn; Indicates whether to show fully qualified domain name or not (default False).
1313
:param tags: list of tags for a logger (default is None).
14+
:param limit_stacktrace: limit characters for stacktraces
15+
:param limit_string_fields: limit characters for string fields
16+
:param limit_containers: limit length of containers (dict, list, set)
1417
"""
1518

16-
def __init__(self, host, port=5959, message_type='logstash', tags=None, fqdn=False):
19+
def __init__(self, host, port=5959, message_type='logstash', tags=None, fqdn=False,
20+
limit_stacktrace=0, limit_string_fields=0, limit_containers=0):
1721
super(TCPLogstashHandler, self).__init__(host, port)
1822

19-
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn)
23+
self.formatter = formatter.LogstashFormatter(message_type, tags, fqdn, limit_stacktrace=limit_stacktrace,
24+
limit_string_fields=limit_string_fields,
25+
limit_containers=limit_containers)
2026

2127
def makePickle(self, record):
2228
return self.formatter.format(record) + b'\n'

logstash/handler_udp.py

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class UDPLogstashHandler(TCPLogstashHandler, DatagramHandler):
1010
:param message_type: The type of the message (default logstash).
1111
:param fqdn; Indicates whether to show fully qualified domain name or not (default False).
1212
:param tags: list of tags for a logger (default is None).
13+
:param limit_stacktrace: limit characters for stacktraces
14+
:param limit_string_fields: limit characters for string fields
15+
:param limit_containers: limit length of containers (dict, list, set)
1316
"""
1417

1518
def makePickle(self, record):

0 commit comments

Comments
 (0)