Skip to content

Commit b3fdb97

Browse files
author
mbirger
committed
Add serialization of logging arguments
1 parent 5211b55 commit b3fdb97

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

examples/serialize.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Simple usage example of kafka-logging-handler."""
2+
3+
import json
4+
import logging
5+
import os
6+
import sys
7+
8+
from kafka_logger.handlers import KafkaLoggingHandler
9+
10+
REQUIRED_ENV_VARS = ['KAFKA_SERVER', 'KAFKA_CERT', 'KAFKA_TOPIC']
11+
12+
13+
class CustomClass:
14+
"""Dummy class to demo logging."""
15+
16+
def __init__(self, value):
17+
"""Initialize CustomClass object."""
18+
self._value = value
19+
20+
def __str__(self):
21+
"""Convert CustomClass to string."""
22+
return "CustomClass: {}".format(self._value)
23+
24+
25+
def main():
26+
"""Setup logger and test logging."""
27+
# validate that Kafka configuration is available
28+
assert all([(key in os.environ) for key in REQUIRED_ENV_VARS])
29+
30+
logger = logging.getLogger("test.logger")
31+
logger.propagate = False
32+
log_level = logging.DEBUG
33+
34+
log_format = logging.Formatter(
35+
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
36+
'%Y-%m-%dT%H:%M:%S')
37+
38+
# create handler to show logs at stdout
39+
stdout_handler = logging.StreamHandler(sys.stdout)
40+
stdout_handler.setLevel(log_level)
41+
stdout_handler.setFormatter(log_format)
42+
logger.addHandler(stdout_handler)
43+
44+
# create Kafka logging handler
45+
kafka_handler = KafkaLoggingHandler(
46+
os.environ['KAFKA_SERVER'],
47+
os.environ['KAFKA_TOPIC'],
48+
security_protocol='SSL',
49+
ssl_cafile=os.environ['KAFKA_CERT'],
50+
unhandled_exception_logger=logger,
51+
)
52+
kafka_handler.setFormatter(log_format)
53+
logger.addHandler(kafka_handler)
54+
55+
logger.setLevel(log_level)
56+
57+
logger.info("Test log with int parameter: %d", 42)
58+
logger.info("Test log with multiple parameters: %d %f", 42, 43.2)
59+
60+
logger.info("Test log with str parameter: %s", "test1")
61+
logger.info("Test log with multiple str parameters: %s %s",
62+
"test1", "test2")
63+
64+
custom_object = CustomClass('test')
65+
logger.info("Test logging of custom obj: %s", custom_object)
66+
# log record will contain the following values:
67+
# args: <__main__.CustomClass object at 0x7f3147041c88>
68+
# message: Test logging of custom obj: CustomClass: test
69+
70+
# extra values have to be JSON serializable
71+
try:
72+
json.dumps(custom_object)
73+
# TypeError: Object of type 'CustomClass' is not JSON serializable
74+
except TypeError:
75+
logger.exception("Attempt to log non JSON serializable data")
76+
# please transform extra values to JSON
77+
logger.info("Test custom objects in extra argument", extra={
78+
"custom_field_number": 42,
79+
"custom_field_json": {"a": "test", "b": "test"}
80+
})
81+
82+
83+
if __name__ == '__main__':
84+
main()

kafka_logger/handlers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ def emit(self, record):
126126
rec = self.additional_fields.copy()
127127
for key, value in record.__dict__.items():
128128
if key not in KafkaLoggingHandler.__LOGGING_FILTER_FIELDS:
129+
if key == "args":
130+
# convert ALL argument to a str representation
131+
# Elasticsearch supports number datatypes
132+
# but it is not 1:1 - logging "inf" float
133+
# causes _jsonparsefailure error in ELK
134+
value = tuple(arg.__repr__() for arg in value)
129135
rec[key] = "" if value is None else value
130136

131137
with self.buffer_lock:

0 commit comments

Comments
 (0)