Skip to content

Commit 4cc1787

Browse files
committed
Implement extra properties from json-logging-py
Addresses feature request #77 Taken from: https://github.com/sebest/json-logging-py/blob/master/jsonlogging.py#L40 Original work by Sebastien Estienne <sebastien.estienne@gmail.com> Distributed under the MIT license
1 parent 2f4bfb6 commit 4cc1787

File tree

4 files changed

+91
-8
lines changed

4 files changed

+91
-8
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,14 @@ correlation_id_without_request_obj = json_logging.get_correlation_id()
245245

246246
## 2.4 Log extra properties
247247

248-
Extra property can be added to logging statement as follow:
248+
Extra property can be added to logging statement as follows:
249249

250250
```python
251-
logger.info("test log statement", extra={'props': {'extra_property': 'extra_value'}})
251+
logger.info("test log statement", extra={'props': {'extra_property': 'extra_value'}, 'tags': ['app:name']})
252252
```
253253

254+
_For backwards compatibility, properties added in `props` take precedence over those set in the root of `extra`. This is relevant only for logs formatted by `JSONLogFormatter`, other formatters never printed the `props` details._
255+
254256
### Forced overriding correlation-id
255257
A custom correlation id can be passed to logging statement as follow:
256258
```

json_logging/__init__.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@
2525
COMPONENT_NAME = EMPTY_VALUE
2626
COMPONENT_INSTANCE_INDEX = 0
2727

28+
# The list contains all the attributes listed in
29+
# http://docs.python.org/library/logging.html#logrecord-attributes
30+
RECORD_ATTR_SKIP_LIST = [
31+
'asctime', 'created', 'exc_info', 'exc_text', 'filename', 'args',
32+
'funcName', 'id', 'levelname', 'levelno', 'lineno', 'module', 'msg',
33+
'msecs', 'msecs', 'message', 'name', 'pathname', 'process',
34+
'processName', 'props', 'relativeCreated', 'thread', 'threadName',
35+
'extra',
36+
]
37+
38+
if sys.version_info < (3, 0):
39+
EASY_TYPES = (basestring, bool, dict, float, int, list, type(None))
40+
else:
41+
RECORD_ATTR_SKIP_LIST.append('stack_info')
42+
EASY_TYPES = (str, bool, dict, float, int, list, type(None))
43+
2844
_framework_support_map = {}
2945
_current_framework = None
3046
_logger = get_library_logger(__name__)
@@ -268,8 +284,29 @@ def _format_log_object(self, record, request_util):
268284
"written_ts": util.epoch_nano_second(utcnow),
269285
}
270286
base_obj.update(self.base_object_common)
287+
# Add extra fields
288+
base_obj.update(self._get_extra_fields(record))
271289
return base_obj
272290

291+
def _get_extra_fields(self, record):
292+
fields = {}
293+
294+
if record.args:
295+
fields['msg'] = record.msg
296+
297+
for key, value in record.__dict__.items():
298+
if key not in RECORD_ATTR_SKIP_LIST:
299+
if isinstance(value, EASY_TYPES):
300+
fields[key] = value
301+
else:
302+
fields[key] = repr(value)
303+
304+
# Always add 'props' to the root of the log, assumes props is a dict
305+
if hasattr(record, 'props') and isinstance(record.props, dict):
306+
fields.update(record.props)
307+
308+
return fields
309+
273310

274311
class JSONRequestLogFormatter(BaseJSONFormatter):
275312
"""
@@ -342,8 +379,6 @@ def _format_log_object(self, record, request_util):
342379
"module": record.module,
343380
"line_no": record.lineno,
344381
})
345-
if hasattr(record, 'props'):
346-
json_log_object.update(record.props)
347382

348383
if record.exc_info or record.exc_text:
349384
json_log_object.update(self.get_exc_fields(record))

tests/test_fastapi.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ async def log_error():
5151
async def extra_property():
5252
logger.info(
5353
"test log statement with extra props",
54-
extra={"props": {"extra_property": "extra_value"}},
54+
extra={"props": {"extra_property": "extra_value"}, "tags": ["app:name"], "extra_property": "extra_value2"},
55+
)
56+
return {}
57+
58+
@app.get("/log/extra_property_no_props")
59+
async def extra_property_no_props():
60+
logger.info(
61+
"test log statement with extra and no 'props' property",
62+
extra={"tags": ["app:name"], "extra_property": "extra_value2"},
5563
)
5664
return {}
5765

@@ -148,8 +156,23 @@ def test_extra_property(client_and_log_handler):
148156
assert response.status_code == 200
149157
assert len(handler.messages) == 1
150158
msg = json.loads(handler.messages[0])
151-
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property"})
159+
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property", "tags"})
152160
assert msg["extra_property"] == "extra_value"
161+
assert msg["tags"] == ["app:name"]
162+
163+
164+
def test_extra_property_no_props(client_and_log_handler):
165+
"""Test adding an extra property to a log message"""
166+
api_client, handler = client_and_log_handler
167+
168+
response = api_client.get("/log/extra_property_no_props")
169+
170+
assert response.status_code == 200
171+
assert len(handler.messages) == 1
172+
msg = json.loads(handler.messages[0])
173+
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property", "tags"})
174+
assert msg["extra_property"] == "extra_value2"
175+
assert msg["tags"] == ["app:name"]
153176

154177

155178
def test_exception_logged_with_stack_trace(client_and_log_handler):

tests/test_flask.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ def log_error():
5151
def extra_property():
5252
logger.info(
5353
"test log statement with extra props",
54-
extra={"props": {"extra_property": "extra_value"}},
54+
extra={"props": {"extra_property": "extra_value"}, "tags": ["app:name"], "extra_property": "extra_value2"},
55+
)
56+
return {}
57+
58+
@app.route("/log/extra_property_no_props")
59+
def extra_property_no_props():
60+
logger.info(
61+
"test log statement with extra and no 'props' property",
62+
extra={"tags": ["app:name"], "extra_property": "extra_value2"},
5563
)
5664
return {}
5765

@@ -148,8 +156,23 @@ def test_extra_property(client_and_log_handler):
148156
assert response.status_code == 200
149157
assert len(handler.messages) == 1
150158
msg = json.loads(handler.messages[0])
151-
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property"})
159+
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property", "tags"})
152160
assert msg["extra_property"] == "extra_value"
161+
assert msg["tags"] == ["app:name"]
162+
163+
164+
def test_extra_property_no_props(client_and_log_handler):
165+
"""Test adding an extra property to a log message"""
166+
api_client, handler = client_and_log_handler
167+
168+
response = api_client.get("/log/extra_property_no_props")
169+
170+
assert response.status_code == 200
171+
assert len(handler.messages) == 1
172+
msg = json.loads(handler.messages[0])
173+
assert set(msg.keys()) == constants.STANDARD_MSG_ATTRIBUTES.union({"extra_property", "tags"})
174+
assert msg["extra_property"] == "extra_value2"
175+
assert msg["tags"] == ["app:name"]
153176

154177

155178
def test_exception_logged_with_stack_trace(client_and_log_handler):

0 commit comments

Comments
 (0)