Skip to content

Commit fd02b0a

Browse files
commit code
1 parent 76a6a47 commit fd02b0a

File tree

7 files changed

+817
-1
lines changed

7 files changed

+817
-1
lines changed

README.md

Lines changed: 280 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,280 @@
1-
# jsonformatter
1+
# jsonformatter -- A formatter for python log json format
2+
3+
**jsonformatter** is a json formatter for python log handler, you can use it easily output LogStash needed format or other custom json format.
4+
5+
jsonformatter requires Python 3.X.
6+
7+
8+
9+
## Installation
10+
11+
jsonformatter is available on PyPI.
12+
Use pip to install:
13+
14+
```shell
15+
$ pip install jsonformatter
16+
```
17+
or:
18+
19+
```shell
20+
$ git clone https://github.com/MyColorfulDays/jsonformatter.git
21+
$ cd jsonformatter
22+
$ python setup.py install
23+
```
24+
25+
## LogRecord Attributes
26+
Offical url: https://docs.python.org/3/library/logging.html#logrecord-attributes
27+
28+
Attribute name|Format|Description
29+
-|-|-
30+
args|You shouldn’t need to format this yourself.|The tuple of arguments merged into msg to produce message, or a dict whose values are used for the merge (when there is only one argument, and it is a dictionary).
31+
asctime|%(asctime)s|Human-readable time when the LogRecord was created. By default this is of the form ‘2003-07-08 16:49:45,896’ (the numbers after the comma are millisecond portion of the time).
32+
created|%(created)f|Time when the LogRecord was created (as returned by time.time()).
33+
exc_info|You shouldn’t need to format this yourself.|Exception tuple (à la sys.exc_info) or, if no exception has occurred, None.
34+
filename|%(filename)s|Filename portion of pathname.
35+
funcName|%(funcName)s|Name of function containing the logging call.
36+
levelname|%(levelname)s|Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
37+
levelno|%(levelno)s|Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
38+
lineno|%(lineno)d|Source line number where the logging call was issued (if available).
39+
message|%(message)s|The logged message, computed as msg % args. This is set when Formatter.format() is invoked.
40+
module|%(module)s|Module (name portion of filename).
41+
msecs|%(msecs)d|Millisecond portion of the time when the LogRecord was created.
42+
msg|You shouldn’t need to format this yourself.|The format string passed in the original logging call. Merged with args to produce message, or an arbitrary object (see Using arbitrary objects as messages).
43+
name|%(name)s|Name of the logger used to log the call.
44+
pathname|%(pathname)s|Full pathname of the source file where the logging call was issued (if available).
45+
process|%(process)d|Process ID (if available).
46+
processName|%(processName)s|Process name (if available).
47+
relativeCreated|%(relativeCreated)d|Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
48+
stack_info|You shouldn’t need to format this yourself.|Stack frame information (where available) from the bottom of the stack in the current thread, up to and including the stack frame of the logging call which resulted in the creation of this record.
49+
thread|%(thread)d|Thread ID (if available).
50+
threadName|%(threadName)s|Thread name (if available).
51+
52+
## Basic Usage
53+
### Case 1. config in python code
54+
```python3
55+
import logging
56+
57+
from jsonformatter import JsonFormatter
58+
59+
# format can be json string, OrderedDict, dict.
60+
# if format is dict and python version<3.7.0, the output order is same of sorted keys.
61+
# the key needn't same as attribute name, can be whatever you like.
62+
# the value can be `Attribute name` or `Format`(`Attribute name` will diplay `LogRecord.attribute`, `Format` will diplay `str(LogRecord.attribute)`).
63+
STRING_FORMAT = '''{
64+
"Name": "name",
65+
"Levelno": "levelno",
66+
"Levelname": "levelname",
67+
"Pathname": "pathname",
68+
"Filename": "filename",
69+
"Module": "module",
70+
"Lineno": "lineno",
71+
"FuncName": "funcName",
72+
"Created": "created",
73+
"Asctime": "asctime",
74+
"Msecs": "msecs",
75+
"RelativeCreated": "relativeCreated",
76+
"Thread": "thread",
77+
"ThreadName": "threadName",
78+
"Process": "process",
79+
"Message": "message"
80+
}'''
81+
82+
83+
84+
root = logging.getLogger()
85+
root.setLevel(logging.INFO)
86+
87+
formatter = JsonFormatter(STRING_FORMAT)
88+
89+
sh = logging.StreamHandler()
90+
sh.setFormatter(formatter)
91+
sh.setLevel(logging.INFO)
92+
93+
root.addHandler(sh)
94+
95+
root.info("test %s format", 'string')
96+
```
97+
98+
### Case 2. config from config file
99+
config file:
100+
```shell
101+
$ cat logger_config.ini
102+
[loggers]
103+
keys=root
104+
105+
[logger_root]
106+
level=DEBUG
107+
handlers=infohandler
108+
109+
110+
###############################################
111+
112+
[handlers]
113+
keys=infohandler
114+
115+
[handler_infohandler]
116+
class=StreamHandler
117+
level=INFO
118+
formatter=form01
119+
args=(sys.stdout,)
120+
121+
###############################################
122+
123+
[formatters]
124+
keys=form01
125+
126+
[formatter_form01]
127+
class=jsonformatter.JsonFormatter
128+
format={"Name": "name","Levelno": "levelno","Levelname": "levelname","Pathname": "pathname","Filename": "filename","Module": "module","Lineno": "lineno","FuncName": "funcName","Created": "created","Asctime": "asctime","Msecs": "msecs","RelativeCreated": "relativeCreated","Thread": "thread","ThreadName": "threadName","Process": "process","Message": "message"}
129+
```
130+
python code:
131+
```python3
132+
import logging
133+
import os
134+
from logging.config import fileConfig
135+
136+
fileConfig(os.path.join(os.path.dirname(__file__), 'logger_config.ini'))
137+
root = logging.getLogger('root')
138+
root.info('test file config')
139+
140+
```
141+
142+
## More Usage
143+
144+
### Case 1. output multiple attributes in one key
145+
```python3
146+
import logging
147+
148+
from jsonformatter import JsonFormatter
149+
150+
MULTI_ATTRIBUTES_FORMAT = '''{
151+
"multi attributes in one key": "%(name)s - %(levelno)s - %(levelname)s - %(pathname)s - %(filename)s - %(module)s - %(lineno)d - %(funcName)s - %(created)f - %(asctime)s - %(msecs)d - %(relativeCreated)d - %(thread)d - %(threadName)s - %(process)d - %(message)s"
152+
}
153+
'''
154+
155+
156+
root = logging.getLogger()
157+
root.setLevel(logging.INFO)
158+
159+
formatter = JsonFormatter(MULTI_ATTRIBUTES_FORMAT)
160+
161+
sh = logging.StreamHandler()
162+
sh.setFormatter(formatter)
163+
164+
sh.setLevel(logging.INFO)
165+
166+
root.addHandler(sh)
167+
root.info('test multi attributes in one key')
168+
```
169+
170+
### Case 2. support `json.dumps` all optional parameters
171+
```python3
172+
import logging
173+
174+
from jsonformatter import JsonFormatter
175+
176+
STRING_FORMAT = '''{
177+
"Name": "name",
178+
"Levelno": "levelno",
179+
"Levelname": "levelname",
180+
"Pathname": "pathname",
181+
"Filename": "filename",
182+
"Module": "module",
183+
"Lineno": "lineno",
184+
"FuncName": "funcName",
185+
"Created": "created",
186+
"Asctime": "asctime",
187+
"Msecs": "msecs",
188+
"RelativeCreated": "relativeCreated",
189+
"Thread": "thread",
190+
"ThreadName": "threadName",
191+
"Process": "process",
192+
"Message": "message"
193+
}'''
194+
195+
root = logging.getLogger()
196+
root.setLevel(logging.INFO)
197+
198+
199+
formatter = JsonFormatter(STRING_FORMAT, indent=4, ensure_ascii=False)
200+
201+
sh = logging.StreamHandler()
202+
sh.setFormatter(formatter)
203+
204+
sh.setLevel(logging.INFO)
205+
206+
root.addHandler(sh)
207+
208+
root.info('test json optional paramter: 中文')
209+
```
210+
211+
### Case 3. add/replace `LogRecord`'s attribute value
212+
```python3
213+
import datetime
214+
import json
215+
import logging
216+
import random
217+
from collections import OrderedDict
218+
219+
from jsonformatter import JsonFormatter
220+
221+
# the key will add/replace `LogRecord`'s attribute
222+
# the value must be `callable` type and not support paramters, it returned value will as the value of LogRecord's attribute
223+
RECORD_CUSTOM_ATTRS = {
224+
# datetime.datetime type is not JSON serializable.
225+
# solve it in three ways.
226+
# 1. use `Format` %(asctme)s.
227+
# 2. use `json.dumps` optional parameter `default`.
228+
# 3. use `json.dumps` optional parameter `cls`.
229+
'asctime': lambda: datetime.datetime.today(),
230+
'user id': lambda: str(random.random())[2:10]
231+
}
232+
233+
RECORD_CUSTOM_FORMAT = OrderedDict([
234+
("User id", "user id"), # new custom attrs
235+
("Name", "name"),
236+
("Levelno", "levelno"),
237+
("Levelname", "levelname"),
238+
("Pathname", "pathname"),
239+
("Filename", "filename"),
240+
("Module", "module"),
241+
("Lineno", "lineno"),
242+
("FuncName", "funcName"),
243+
("Created", "created"),
244+
("Asctime", "%(asctime)s"), # use `Format` to convert returned value to string.
245+
("Msecs", "msecs"),
246+
("RelativeCreated", "relativeCreated"),
247+
("Thread", "thread"),
248+
("ThreadName", "threadName"),
249+
("Process", "process"),
250+
("Message", "message")
251+
])
252+
253+
254+
def DEFAULT_SOLUTION(o):
255+
if not isinstance(o,(str, int, float, bool, type(None))):
256+
return str(o)
257+
else:
258+
return o
259+
260+
class CLS_SOLUTION(json.JSONEncoder):
261+
def default(self, o):
262+
if isinstance(o, datetime.datetime):
263+
return o.isoformat()
264+
265+
return json.JSONEncoder.default(self, o)
266+
267+
root = logging.getLogger()
268+
root.setLevel(logging.INFO)
269+
270+
formatter = JsonFormatter(RECORD_CUSTOM_FORMAT, record_custom_attrs=RECORD_CUSTOM_ATTRS, default=DEFAULT_SOLUTION, cls=CLS_SOLUTION)
271+
272+
sh = logging.StreamHandler()
273+
sh.setFormatter(formatter)
274+
275+
sh.setLevel(logging.INFO)
276+
277+
root.addHandler(sh)
278+
root.info('record custom attrs')
279+
```
280+

jsonformatter/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
File: jsonformatter.py
6+
Author: Me
7+
Email: yourname@email.com
8+
Github: https://github.com/yourname
9+
Description: jsonformatter.py
10+
"""
11+
from .jsonformatter import JsonFormatter
12+
13+
__all__ = ['JsonFormatter']

0 commit comments

Comments
 (0)