Skip to content

Commit db18f62

Browse files
authored
Merge pull request #14 from kbakk/custom_log_format
Custom log format
2 parents be0e64e + 2219954 commit db18f62

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

example/custom_log_format.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# This example shows how the logger can be set up to use a custom JSON format.
2+
import logging
3+
import json
4+
import traceback
5+
from datetime import datetime
6+
import copy
7+
import json_logging
8+
import sys
9+
10+
json_logging.ENABLE_JSON_LOGGING = True
11+
12+
13+
def extra(**kw):
14+
'''Add the required nested props layer'''
15+
return {'extra': {'props': kw}}
16+
17+
18+
class CustomJSONLog(logging.Formatter):
19+
"""
20+
Customized logger
21+
"""
22+
python_log_prefix = 'python.'
23+
def get_exc_fields(self, record):
24+
if record.exc_info:
25+
exc_info = self.format_exception(record.exc_info)
26+
else:
27+
exc_info = record.exc_text
28+
return {f'{self.python_log_prefix}exc_info': exc_info}
29+
30+
@classmethod
31+
def format_exception(cls, exc_info):
32+
return ''.join(traceback.format_exception(*exc_info)) if exc_info else ''
33+
34+
def format(self, record):
35+
json_log_object = {"@timestamp": datetime.utcnow().isoformat(),
36+
"level": record.levelname,
37+
"message": record.getMessage(),
38+
"caller": record.filename + '::' + record.funcName
39+
}
40+
json_log_object['data'] = {
41+
f'{self.python_log_prefix}logger_name': record.name,
42+
f'{self.python_log_prefix}module': record.module,
43+
f'{self.python_log_prefix}funcName': record.funcName,
44+
f'{self.python_log_prefix}filename': record.filename,
45+
f'{self.python_log_prefix}lineno': record.lineno,
46+
f'{self.python_log_prefix}thread': f'{record.threadName}[{record.thread}]',
47+
f'{self.python_log_prefix}pid': record.process
48+
}
49+
if hasattr(record, 'props'):
50+
json_log_object['data'].update(record.props)
51+
52+
if record.exc_info or record.exc_text:
53+
json_log_object['data'].update(self.get_exc_fields(record))
54+
55+
return json.dumps(json_log_object)
56+
57+
58+
def logger_init():
59+
json_logging.init(custom_formatter=CustomJSONLog)
60+
61+
# You would normally import logger_init and setup the logger in your main module - e.g.
62+
# main.py
63+
64+
logger_init()
65+
66+
logger = logging.getLogger(__name__)
67+
logger.setLevel(logging.DEBUG)
68+
logger.addHandler(logging.StreamHandler(sys.stderr))
69+
70+
logger.info('Starting')
71+
try:
72+
1/0
73+
except: # noqa pylint: disable=bare-except
74+
logger.exception('You can\t divide by zero')

json_logging/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,15 @@ def config_root_logger():
9595
"logging.basicConfig() or logging.getLogger('root')")
9696

9797

98-
def init(framework_name=None):
98+
def init(framework_name=None, custom_formatter=None):
9999
"""
100100
Initialize JSON logging support, if no **framework_name** passed, logging will be initialized in non-web context.
101101
This is supposed to be called only one time.
102102
103+
If **custom_formatter** is passed, it will (in non-web context) use this formatter over the default.
104+
103105
:param framework_name: type of framework logging should support.
106+
:param custom_formatter: formatter to override default JSONLogFormatter.
104107
"""
105108

106109
global _current_framework
@@ -123,6 +126,10 @@ def init(framework_name=None):
123126
_current_framework['app_configurator']().config()
124127

125128
formatter = JSONLogWebFormatter
129+
elif custom_formatter:
130+
if not issubclass(custom_formatter, logging.Formatter):
131+
raise ValueError('custom_formatter is not subclass of logging.Formatter', custom_formatter)
132+
formatter = custom_formatter
126133
else:
127134
formatter = JSONLogFormatter
128135

0 commit comments

Comments
 (0)