Skip to content
This repository was archived by the owner on Dec 21, 2024. It is now read-only.

Commit eebe45e

Browse files
authored
Merge pull request #60 from UseAlloy/extend-extensibility
Extend extensibility
2 parents f7fd6ee + 67dad89 commit eebe45e

File tree

2 files changed

+54
-16
lines changed

2 files changed

+54
-16
lines changed

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
'Programming Language :: Python :: 3.2',
3333
'Programming Language :: Python :: 3.3',
3434
'Programming Language :: Python :: 3.4',
35+
'Programming Language :: Python :: 3.5',
36+
'Programming Language :: Python :: 3.6',
3537
'Topic :: System :: Logging',
3638
]
3739
)

src/pythonjsonlogger/jsonlogger.py

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
import json
77
import re
8-
import datetime
8+
from datetime import date, datetime, time
99
import traceback
1010

1111
from inspect import istraceback
@@ -24,10 +24,8 @@
2424
'msecs', 'message', 'msg', 'name', 'pathname', 'process',
2525
'processName', 'relativeCreated', 'stack_info', 'thread', 'threadName')
2626

27-
RESERVED_ATTR_HASH = dict(zip(RESERVED_ATTRS, RESERVED_ATTRS))
2827

29-
30-
def merge_record_extra(record, target, reserved=RESERVED_ATTR_HASH):
28+
def merge_record_extra(record, target, reserved):
3129
"""
3230
Merges extra attributes from LogRecord object into target dictionary
3331
@@ -44,6 +42,36 @@ def merge_record_extra(record, target, reserved=RESERVED_ATTR_HASH):
4442
return target
4543

4644

45+
class JsonEncoder(json.JSONEncoder):
46+
"""
47+
A custom encoder extending the default JSONEncoder
48+
"""
49+
def default(self, obj):
50+
if isinstance(obj, (date, datetime, time)):
51+
return self.format_datetime_obj(obj)
52+
53+
elif istraceback(obj):
54+
return ''.join(traceback.format_tb(obj)).strip()
55+
56+
elif type(obj) == Exception \
57+
or isinstance(obj, Exception) \
58+
or type(obj) == type:
59+
return str(obj)
60+
61+
try:
62+
return super(JsonEncoder, self).default(obj)
63+
64+
except TypeError:
65+
try:
66+
return str(obj)
67+
68+
except Exception:
69+
return None
70+
71+
def format_datetime_obj(self, obj):
72+
return obj.isoformat()
73+
74+
4775
class JsonFormatter(logging.Formatter):
4876
"""
4977
A custom formatter to format logging records as json strings.
@@ -58,32 +86,36 @@ def __init__(self, *args, **kwargs):
5886
:param json_encoder: optional custom encoder
5987
:param json_serializer: a :meth:`json.dumps`-compatible callable
6088
that will be used to serialize the log record.
89+
:param json_indent: an optional :meth:`json.dumps`-compatible numeric value
90+
that will be used to customize the indent of the output json.
6191
:param prefix: an optional string prefix added at the beginning of
6292
the formatted string
93+
:param reserved_attrs: an optional list of fields that will be skipped when
94+
outputting json log record. Defaults to all log record attributes:
95+
http://docs.python.org/library/logging.html#logrecord-attributes
96+
:param timestamp: an optional string/boolean field to add a timestamp when
97+
outputting the json log record. If string is passed, timestamp will be added
98+
to log record using string as key. If True boolean is passed, timestamp key
99+
will be "timestamp". Defaults to False/off.
63100
"""
64101
self.json_default = kwargs.pop("json_default", None)
65102
self.json_encoder = kwargs.pop("json_encoder", None)
66103
self.json_serializer = kwargs.pop("json_serializer", json.dumps)
67104
self.json_indent = kwargs.pop("json_indent", None)
68105
self.prefix = kwargs.pop("prefix", "")
106+
reserved_attrs = kwargs.pop("reserved_attrs", RESERVED_ATTRS)
107+
self.reserved_attrs = dict(zip(reserved_attrs, reserved_attrs))
108+
self.timestamp = kwargs.pop("timestamp", False)
109+
69110
#super(JsonFormatter, self).__init__(*args, **kwargs)
70111
logging.Formatter.__init__(self, *args, **kwargs)
71112
if not self.json_encoder and not self.json_default:
72-
def _default_json_handler(obj):
73-
'''Prints dates in ISO format'''
74-
if isinstance(obj, (datetime.date, datetime.time)):
75-
return obj.isoformat()
76-
elif istraceback(obj):
77-
tb = ''.join(traceback.format_tb(obj))
78-
return tb.strip()
79-
elif isinstance(obj, Exception):
80-
return "Exception: %s" % str(obj)
81-
return str(obj)
82-
self.json_default = _default_json_handler
113+
self.json_encoder = JsonEncoder
114+
83115
self._required_fields = self.parse()
84116
self._skip_fields = dict(zip(self._required_fields,
85117
self._required_fields))
86-
self._skip_fields.update(RESERVED_ATTR_HASH)
118+
self._skip_fields.update(self.reserved_attrs)
87119

88120
def parse(self):
89121
"""
@@ -104,6 +136,10 @@ def add_fields(self, log_record, record, message_dict):
104136
log_record.update(message_dict)
105137
merge_record_extra(record, log_record, reserved=self._skip_fields)
106138

139+
if self.timestamp:
140+
key = self.timestamp if type(self.timestamp) == str else 'timestamp'
141+
log_record[key] = datetime.utcnow()
142+
107143
def process_log_record(self, log_record):
108144
"""
109145
Override this method to implement custom logic

0 commit comments

Comments
 (0)