Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit abd5cf1

Browse files
Merge pull request #22 from marselester/verbose
VerboseJSONFormatter logs most of the built-in attributes
2 parents 39a0818 + c10b66f commit abd5cf1

File tree

3 files changed

+147
-8
lines changed

3 files changed

+147
-8
lines changed

‎README.rst

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,35 @@ The log file will contain the following log record (inline).
4747
"exc_info": "Traceback (most recent call last): ..."
4848
}
4949
50+
If you use a log collection and analysis system,
51+
you might need to include the built-in
52+
`log record attributes <https://docs.python.org/3/library/logging.html#logrecord-attributes>`_
53+
with ``VerboseJSONFormatter``.
54+
55+
.. code-block:: python
56+
57+
json_handler.setFormatter(json_log_formatter.VerboseJSONFormatter())
58+
logger.error('An error has occured')
59+
60+
.. code-block:: json
61+
62+
{
63+
"filename": "tests.py",
64+
"funcName": "test_file_name_is_testspy",
65+
"levelname": "ERROR",
66+
"lineno": 276,
67+
"module": "tests",
68+
"name": "my_json",
69+
"pathname": "/Users/bob/json-log-formatter/tests.py",
70+
"process": 3081,
71+
"processName": "MainProcess",
72+
"stack_info": null,
73+
"thread": 4664270272,
74+
"threadName": "MainThread",
75+
"message": "An error has occured",
76+
"time": "2021年07月04日T21:05:42.767726"
77+
}
78+
5079
JSON libraries
5180
--------------
5281

@@ -60,8 +89,8 @@ You can use **ujson** or **simplejson** instead of built-in **json** library.
6089
formatter = json_log_formatter.JSONFormatter()
6190
formatter.json_lib = ujson
6291
63-
Note, **ujson** doesn't support `dumps(default=f)` argument:
64-
if it can't serialize an attribute, it might fail with `TypeError` or skip an attribute.
92+
Note, **ujson** doesn't support ``dumps(default=f)`` argument:
93+
if it can't serialize an attribute, it might fail with ``TypeError`` or skip an attribute.
6594

6695
Django integration
6796
------------------
@@ -97,7 +126,7 @@ Let's try to log something.
97126
Custom formatter
98127
----------------
99128

100-
You will likely need a custom log format. For instance, you want to log
129+
You will likely need a custom log formatter. For instance, you want to log
101130
a user ID, an IP address and ``time`` as ``django.utils.timezone.now()``.
102131
To do so you should override ``JSONFormatter.json_record()``.
103132

@@ -108,17 +137,17 @@ To do so you should override ``JSONFormatter.json_record()``.
108137
extra['message'] = message
109138
extra['user_id'] = current_user_id()
110139
extra['ip'] = current_ip()
111-
140+
112141
# Include builtins
113142
extra['level'] = record.levelname
114143
extra['name'] = record.name
115-
144+
116145
if 'time' not in extra:
117146
extra['time'] = django.utils.timezone.now()
118-
147+
119148
if record.exc_info:
120149
extra['exc_info'] = self.formatException(record.exc_info)
121-
150+
122151
return extra
123152
124153
Let's say you want ``datetime`` to be serialized as timestamp.

‎json_log_formatter/__init__.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,62 @@ def _json_serializable(obj):
141141
return obj.__dict__
142142
except AttributeError:
143143
return str(obj)
144+
145+
146+
class VerboseJSONFormatter(JSONFormatter):
147+
"""JSON log formatter with built-in log record attributes such as log level.
148+
149+
Usage example::
150+
151+
import logging
152+
153+
import json_log_formatter
154+
155+
json_handler = logging.FileHandler(filename='/var/log/my-log.json')
156+
json_handler.setFormatter(json_log_formatter.VerboseJSONFormatter())
157+
158+
logger = logging.getLogger('my_verbose_json')
159+
logger.addHandler(json_handler)
160+
161+
logger.error('An error has occured')
162+
163+
The log file will contain the following log record (inline)::
164+
165+
{
166+
"filename": "tests.py",
167+
"funcName": "test_file_name_is_testspy",
168+
"levelname": "ERROR",
169+
"lineno": 276,
170+
"module": "tests",
171+
"name": "my_verbose_json",
172+
"pathname": "/Users/bob/json-log-formatter/tests.py",
173+
"process": 3081,
174+
"processName": "MainProcess",
175+
"stack_info": null,
176+
"thread": 4664270272,
177+
"threadName": "MainThread",
178+
"message": "An error has occured",
179+
"time": "2021年07月04日T21:05:42.767726"
180+
}
181+
182+
Read more about the built-in log record attributes
183+
https://docs.python.org/3/library/logging.html#logrecord-attributes.
184+
185+
"""
186+
def json_record(self, message, extra, record):
187+
extra['filename'] = record.filename
188+
extra['funcName'] = record.funcName
189+
extra['levelname'] = record.levelname
190+
extra['lineno'] = record.lineno
191+
extra['module'] = record.module
192+
extra['name'] = record.name
193+
extra['pathname'] = record.pathname
194+
extra['process'] = record.process
195+
extra['processName'] = record.processName
196+
if hasattr(record, 'stack_info'):
197+
extra['stack_info'] = record.stack_info
198+
else:
199+
extra['stack_info'] = None
200+
extra['thread'] = record.thread
201+
extra['threadName'] = record.threadName
202+
return super(VerboseJSONFormatter, self).json_record(message, extra, record)

‎tests.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
except ImportError:
1717
from io import StringIO
1818

19-
from json_log_formatter import JSONFormatter
19+
from json_log_formatter import JSONFormatter, VerboseJSONFormatter
2020

2121
log_buffer = StringIO()
2222
json_handler = logging.StreamHandler(log_buffer)
@@ -266,3 +266,54 @@ def test_django_wsgi_request_is_serialized_as_dict(self):
266266
self.assertEqual(json_record['status_code'], 500)
267267
self.assertEqual(json_record['request']['path'], '/bogus')
268268
self.assertEqual(json_record['request']['method'], 'BOGUS')
269+
270+
271+
class VerboseJSONFormatterTest(TestCase):
272+
def setUp(self):
273+
json_handler.setFormatter(VerboseJSONFormatter())
274+
275+
def test_file_name_is_testspy(self):
276+
logger.error('An error has occured')
277+
json_record = json.loads(log_buffer.getvalue())
278+
print(json_record)
279+
self.assertEqual(json_record['filename'], 'tests.py')
280+
281+
def test_function_name(self):
282+
logger.error('An error has occured')
283+
json_record = json.loads(log_buffer.getvalue())
284+
self.assertEqual(json_record['funcName'], 'test_function_name')
285+
286+
def test_level_name_is_error(self):
287+
logger.error('An error has occured')
288+
json_record = json.loads(log_buffer.getvalue())
289+
self.assertEqual(json_record['levelname'], 'ERROR')
290+
291+
def test_module_name_is_tests(self):
292+
logger.error('An error has occured')
293+
json_record = json.loads(log_buffer.getvalue())
294+
self.assertEqual(json_record['module'], 'tests')
295+
296+
def test_logger_name_is_test(self):
297+
logger.error('An error has occured')
298+
json_record = json.loads(log_buffer.getvalue())
299+
self.assertEqual(json_record['name'], 'test')
300+
301+
def test_path_name_is_test(self):
302+
logger.error('An error has occured')
303+
json_record = json.loads(log_buffer.getvalue())
304+
self.assertIn('json-log-formatter/tests.py', json_record['pathname'])
305+
306+
def test_process_name_is_MainProcess(self):
307+
logger.error('An error has occured')
308+
json_record = json.loads(log_buffer.getvalue())
309+
self.assertEqual(json_record['processName'], 'MainProcess')
310+
311+
def test_thread_name_is_MainThread(self):
312+
logger.error('An error has occured')
313+
json_record = json.loads(log_buffer.getvalue())
314+
self.assertEqual(json_record['threadName'], 'MainThread')
315+
316+
def test_stack_info_is_none(self):
317+
logger.error('An error has occured')
318+
json_record = json.loads(log_buffer.getvalue())
319+
self.assertIsNone(json_record['stack_info'])

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /