Skip to content

Commit 8fb226a

Browse files
0.3.0 release, add mix extra, add custom attr func kwargs params
1 parent 45726ab commit 8fb226a

File tree

6 files changed

+718
-93
lines changed

6 files changed

+718
-93
lines changed

README.md

Lines changed: 202 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
1-
Table of Contents
2-
=================
3-
4-
* [jsonformatter \-\- for python log json](#jsonformatter----for-python-log-json)
5-
* [Installation](#installation)
6-
* [Basic Usage](#basic-usage)
7-
* [Case 1\. Use default config](#case-1-use-default-config)
8-
* [Case 2\. config in python code](#case-2-config-in-python-code)
9-
* [Case 3\. config from config file](#case-3-config-from-config-file)
10-
* [More Usage](#more-usage)
11-
* [Case 1\. output multiple attributes in one key](#case-1-output-multiple-attributes-in-one-key)
12-
* [Case 2\. support json\.dumps all optional parameters](#case-2-support-jsondumps-all-optional-parameters)
13-
* [Case 3\. support cumtom(add/replace) LogRecord attribute](#case-3-support-cumtomaddreplace-logrecord--attribute)
14-
* [LogRecord Attributes](#logrecord-attributes)
1+
- [jsonformatter -- for python log json](#jsonformatter----for-python-log-json)
2+
- [Installation](#installation)
3+
- [Basic Usage](#basic-usage)
4+
- [Case 1. Initial root logger like `logging.basicConfig`](#case-1-initial-root-logger-like-loggingbasicconfig)
5+
- [Case 2. Complete config in python code](#case-2-complete-config-in-python-code)
6+
- [Case 3. Use config file](#case-3-use-config-file)
7+
- [Case 4. In `Flask` project, add `LogRecord` attribute for auto output](#case-4-in-flask-project-add-logrecord-attribute-for-auto-output)
8+
- [Case 5. In `Django` project, config `LOGGING`](#case-5-in-django-project-config-logging)
9+
- [More Usage](#more-usage)
10+
- [Case 1. Mix `extra` to output](#case-1-mix-extra-to-output)
11+
- [Case 2. output multiple attributes in one key](#case-2-output-multiple-attributes-in-one-key)
12+
- [Case 3. support `json.dumps` all optional parameters](#case-3-support-jsondumps-all-optional-parameters)
13+
- [Case 4. Solve cumtom `LogRecord` attribute is not `JSON serializable`](#case-4-solve-cumtom-logrecord-attribute-is-not-json-serializable)
14+
- [LogRecord Attributes](#logrecord-attributes)
1515

1616

1717

1818
# jsonformatter -- for python log json
1919

20-
**jsonformatter** is a formatter for python output json log, you can easily output **LogStash** needed log format or other **custom** json format and you can easily **custom(add/replace)** `LogRecord` attribute.
20+
**jsonformatter** is a formatter for python output json log, e.g. output **LogStash** needed log.
21+
22+
Easily **custom(add/replace)** `LogRecord` attribute, e.g. in `Flask` web project, add `username` attribute to `LogRecord` for auto output username.
23+
2124

22-
**Python 2.7** and **python 3** are supported from version 0.2.X, if you are using a version lower than 0.2.X, **python 3** is only supported.
25+
26+
**Python 2.7** and **python 3** are supported from version 0.2.X, if you are using a version lower than 0.2.X, Only **python 3** is supported.
2327

2428

2529

@@ -43,44 +47,34 @@ $ python setup.py install
4347

4448
## Basic Usage
4549

46-
### Case 1. Use default config
47-
50+
### Case 1. Initial root logger like `logging.basicConfig`
4851
```python
4952
import logging
5053

51-
from jsonformatter import JsonFormatter
52-
53-
root = logging.getLogger()
54-
root.setLevel(logging.INFO)
55-
56-
formatter = JsonFormatter()
57-
58-
sh = logging.StreamHandler()
59-
sh.setFormatter(formatter)
60-
sh.setLevel(logging.INFO)
54+
from jsonformatter import basicConfig
6155

62-
root.addHandler(sh)
63-
64-
root.info("test %s config", 'default')
56+
# default keyword parameter `format`: """{"levelname": "levelname", "name": "name", "message": "message"}"""
57+
basicConfig(level=logging.INFO)
58+
logging.info('hello, jsonformatter')
6559
```
6660

6761
output:
6862

6963
```shell
70-
{"levelname": "INFO", "name": "root", "message": "test default config"}
64+
{"levelname": "INFO", "name": "root", "message": "hello, jsonformatter"}
7165
```
7266

7367

7468

75-
### Case 2. config in python code
69+
### Case 2. Complete config in python code
7670

77-
```python3
71+
```python
7872
import logging
7973

8074
from jsonformatter import JsonFormatter
8175

82-
# `format` can be json, OrderedDict, dict.
83-
# If `format` is `dict` and python version<3.7.0, the output ordered is sorted keys, otherwise will same as define ordered.
76+
# `format` can be `json`, `OrderedDict`, `dict`.
77+
# If `format` is `dict` and python version < 3.7.0, the output order is sorted keys, otherwise will same as defined order.
8478
# key: string, can be whatever you like.
8579
# value: `LogRecord` attribute name.
8680
STRING_FORMAT = '''{
@@ -124,7 +118,7 @@ output:
124118

125119

126120

127-
### Case 3. config from config file
121+
### Case 3. Use config file
128122

129123
config file:
130124
```shell
@@ -133,7 +127,7 @@ $ cat logger_config.ini
133127
keys=root
134128

135129
[logger_root]
136-
level=DEBUG
130+
level=INFO
137131
handlers=infohandler
138132

139133

@@ -177,10 +171,172 @@ output:
177171

178172

179173

174+
### Case 4. In `Flask` project, add `LogRecord` attribute for auto output
175+
176+
flask_demo.py
177+
178+
```python
179+
import datetime
180+
import json
181+
import logging
182+
import random
183+
from collections import OrderedDict
184+
185+
from jsonformatter import JsonFormatter
186+
from flask import Flask, has_request_context, request, session
187+
from flask.logging import default_handler
188+
189+
app = Flask(__name__)
190+
191+
# the key will add/replace `LogRecord` attribute.
192+
# the value must be `callable` type and not support positional paramters, the returned value will be as the `LogRecord` attribute value.
193+
RECORD_CUSTOM_ATTRS = {
194+
# no parameters
195+
'url': lambda: request.url if has_request_context() else None,
196+
'username': lambda: session['username'] if has_request_context() and ('username' in session) else None,
197+
# Arbitrary keywords parameters
198+
'status': lambda **record_attrs: 'failed' if record_attrs['levelname'] in ['ERROR', 'CRITICAL'] else 'success'
199+
}
200+
201+
RECORD_CUSTOM_FORMAT = OrderedDict([
202+
# custom record attributes start
203+
("Url", "url"),
204+
("Username", "username"),
205+
("Status", "status"),
206+
# custom record attributes end
207+
("Name", "name"),
208+
("Levelno", "levelno"),
209+
("Levelname", "levelname"),
210+
("Pathname", "pathname"),
211+
("Filename", "filename"),
212+
("Module", "module"),
213+
("Lineno", "lineno"),
214+
("FuncName", "funcName"),
215+
("Created", "created"),
216+
("Asctime", "asctime"),
217+
("Msecs", "msecs"),
218+
("RelativeCreated", "relativeCreated"),
219+
("Thread", "thread"),
220+
("ThreadName", "threadName"),
221+
("Process", "process"),
222+
("Message", "message")
223+
])
224+
225+
226+
formatter = JsonFormatter(
227+
RECORD_CUSTOM_FORMAT,
228+
record_custom_attrs=RECORD_CUSTOM_ATTRS
229+
)
230+
231+
default_handler.setFormatter(formatter)
232+
app.logger.warning('hello, jsonformatter')
233+
```
234+
235+
output:
236+
237+
```shell
238+
{"Url": null, "Username": null, "Status": "success", "Name": "flask_demo", "Levelno": 30, "Levelname": "WARNING", "Pathname": "flask_demo.py", "Filename": "flask_demo.py", "Module": "flask_demo", "Lineno": 54, "FuncName": "<module>", "Created": 1595781463.3557186, "Asctime": "2020-07-27 00:37:43,355", "Msecs": 355.71861267089844, "RelativeCreated": 858.7081432342529, "Thread": 15584, "ThreadName": "MainThread", "Process": 17560, "Message": "hello, jsonformatter"}
239+
```
240+
241+
242+
243+
### Case 5. In `Django` project, config `LOGGING`
244+
245+
settings.py
246+
247+
```python
248+
LOGGING = {
249+
'version': 1,
250+
'disable_existing_loggers': False,
251+
'formatters': {
252+
'standard': {
253+
'class': 'jsonformatter.JsonFormatter',
254+
'format': OrderedDict([
255+
("Name", "name"),
256+
("Levelno", "levelno"),
257+
("Levelname", "levelname"),
258+
("Pathname", "pathname"),
259+
("Filename", "filename"),
260+
("Module", "module"),
261+
("Lineno", "lineno"),
262+
("FuncName", "funcName"),
263+
("Created", "created"),
264+
("Asctime", "asctime"),
265+
("Msecs", "msecs"),
266+
("RelativeCreated", "relativeCreated"),
267+
("Thread", "thread"),
268+
("ThreadName", "threadName"),
269+
("Process", "process"),
270+
("Message", "message")
271+
])
272+
},
273+
},
274+
'handlers': {
275+
'console': {
276+
'level': 'INFO',
277+
'formatter': 'standard',
278+
'class': 'logging.StreamHandler',
279+
},
280+
},
281+
'loggers': {
282+
'django': {
283+
'handlers': ['console'],
284+
'level': 'INFO',
285+
'propagate': False
286+
},
287+
}
288+
}
289+
```
290+
291+
292+
180293
## More Usage
181294

182-
### Case 1. output multiple attributes in one key
183-
```python3
295+
### Case 1. Mix `extra` to output
296+
297+
```python
298+
import logging
299+
300+
from jsonformatter import JsonFormatter
301+
302+
root = logging.getLogger()
303+
root.setLevel(logging.INFO)
304+
305+
sh = logging.StreamHandler()
306+
formatter = JsonFormatter(
307+
ensure_ascii=False,
308+
mix_extra=True,
309+
mix_extra_position='tail' # optional: head, mix
310+
)
311+
sh.setFormatter(formatter)
312+
sh.setLevel(logging.INFO)
313+
root.addHandler(sh)
314+
315+
root.info(
316+
'test mix extra in fmt',
317+
extra={
318+
'extra1': 'extra content 1',
319+
'extra2': 'extra content 2'
320+
})
321+
root.info(
322+
'test mix extra in fmt',
323+
extra={
324+
'extra3': 'extra content 3',
325+
'extra4': 'extra content 4'
326+
})
327+
```
328+
329+
output:
330+
331+
```shell
332+
{"levelname": "INFO", "name": "root", "message": "test mix extra in fmt", "extra1": "extra content 1", "extra2": "extra content 2"}
333+
{"levelname": "INFO", "name": "root", "message": "test mix extra in fmt", "extra3": "extra content 3", "extra4": "extra content 4"}
334+
```
335+
336+
337+
338+
### Case 2. output multiple attributes in one key
339+
```python
184340
import logging
185341

186342
from jsonformatter import JsonFormatter
@@ -207,9 +363,9 @@ root.info('test multi attributes in one key')
207363

208364

209365

210-
### Case 2. support `json.dumps` all optional parameters
366+
### Case 3. support `json.dumps` all optional parameters
211367

212-
```python3
368+
```python
213369
import logging
214370

215371
from jsonformatter import JsonFormatter
@@ -251,9 +407,9 @@ root.info('test json optional paramter: 中文')
251407

252408

253409

254-
### Case 3. support cumtom(add/replace) `LogRecord` attribute
410+
### Case 4. Solve cumtom `LogRecord` attribute is not `JSON serializable`
255411

256-
```python3
412+
```python
257413
import datetime
258414
import json
259415
import logging
@@ -263,10 +419,10 @@ from collections import OrderedDict
263419
from jsonformatter import JsonFormatter
264420

265421
# the key will add/replace `LogRecord` attribute.
266-
# the value must be `callable` type and not support paramters, the returned value will be as the `LogRecord` attribute value.
422+
# the value must be `callable` type and not support positional paramters, the returned value will be as the `LogRecord` attribute value.
267423
RECORD_CUSTOM_ATTRS = {
268424
# `datetime.datetime` type is not JSON serializable.
269-
# solve it in three ways.
425+
# solve it in three ways, choose which you like.
270426
# 1. use `LogRecord` attribute `Format`: %(asctme)s.
271427
# 2. use `json.dumps` optional parameter `default`.
272428
# 3. use `json.dumps` optional parameter `cls`.

jsonformatter/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
Github: https://github.com/yourname
99
Description: jsonformatter.py
1010
"""
11-
from .jsonformatter import JsonFormatter
11+
from .jsonformatter import JsonFormatter, basicConfig
1212

13-
__all__ = ['JsonFormatter']
13+
__all__ = ['JsonFormatter', 'basicConfig']
14+
15+
version = "0.3.0"
16+
version_info = (0, 3, 0)

0 commit comments

Comments
 (0)