Skip to content

Commit 45a6736

Browse files
committed
- fix #45
- fix #46 - refactoring - optimization log record
1 parent ea862d4 commit 45a6736

20 files changed

+157
-161
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
55
The format is based on [Keep a Changelog](http://keepachangelog.com/).
66

7+
## 1.2.0 - 2020-04-10
8+
- fix #45
9+
- fix #46
10+
- refactoring
11+
- optimization log record
12+
713
## 1.1.0 - 2020-04-10
814
- Add the possibility to modify the formatter for request logs.
915

README.md

+30-21
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ If you're using Cloud Foundry, it worth to check out the library [SAP/cf-python-
2121
1. Emit JSON logs ([format detail](#0-full-logging-format-references))
2222
2. Auto extract **correlation-id** for distributed tracing [\[1\]](#1-what-is-correlation-idrequest-id)
2323
3. Lightweight, no dependencies, minimal configuration needed (1 LoC to get it working)
24-
4. Fully compatible with Python **logging** module. Support both Python 2.7.x and 3.x
24+
4. Seamlessly integrate with Python native **logging** module. Support both Python 2.7.x and 3.x
2525
5. Support HTTP request instrumentation. Built in support for [Flask](https://github.com/pallets/flask/), [Sanic](https://github.com/channelcat/sanic), [Quart](https://gitlab.com/pgjones/quart), [Connexion](https://github.com/zalando/connexion). Extensible to support other web frameworks. PR welcome :smiley: .
26-
6. Support inject arbitrary extra properties to JSON log message.
26+
6. Highly customizable: support inject arbitrary extra properties to JSON log message, override logging formatter, etc.
2727

2828
# 2. Usage
2929
Install by running this command:
@@ -59,8 +59,7 @@ logger.info("test logging statement")
5959
import datetime, logging, sys, json_logging, flask
6060

6161
app = flask.Flask(__name__)
62-
json_logging.ENABLE_JSON_LOGGING = True
63-
json_logging.init_flask()
62+
json_logging.init_flask(enable_json=True)
6463
json_logging.init_request_instrument(app)
6564

6665
# init the logger as usual
@@ -71,6 +70,8 @@ logger.addHandler(logging.StreamHandler(sys.stdout))
7170
@app.route('/')
7271
def home():
7372
logger.info("test log statement")
73+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
74+
correlation_id = json_logging.get_correlation_id()
7475
return "Hello world : " + str(datetime.datetime.now())
7576

7677
if __name__ == "__main__":
@@ -81,9 +82,8 @@ if __name__ == "__main__":
8182
```python
8283
import logging, sys, json_logging, sanic
8384

84-
app = sanic.Sanic()
85-
json_logging.ENABLE_JSON_LOGGING = True
86-
json_logging.init_sanic()
85+
app = sanic.Sanic(name="sanic-web-app")
86+
json_logging.init_sanic(enable_json=True)
8787
json_logging.init_request_instrument(app)
8888

8989
# init the logger as usual
@@ -94,6 +94,12 @@ logger.addHandler(logging.StreamHandler(sys.stdout))
9494
@app.route("/")
9595
async def home(request):
9696
logger.info("test log statement")
97+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
98+
# this will be faster
99+
correlation_id = json_logging.get_correlation_id(request=request)
100+
# this will be slower, but will work in context you cant get a reference of request object
101+
correlation_id_without_request_obj = json_logging.get_correlation_id()
102+
97103
return sanic.response.text("hello world")
98104

99105
if __name__ == "__main__":
@@ -106,8 +112,7 @@ if __name__ == "__main__":
106112
import asyncio, logging, sys, json_logging, quart
107113

108114
app = quart.Quart(__name__)
109-
json_logging.ENABLE_JSON_LOGGING = True
110-
json_logging.init_quart()
115+
json_logging.init_quart(enable_json=True)
111116
json_logging.init_request_instrument(app)
112117

113118
# init the logger as usual
@@ -118,7 +123,8 @@ logger.addHandler(logging.StreamHandler(sys.stdout))
118123
@app.route('/')
119124
async def home():
120125
logger.info("test log statement")
121-
logger.info("test log statement", extra={'props': {"extra_property": 'extra_value'}})
126+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
127+
correlation_id = json_logging.get_correlation_id()
122128
return "Hello world"
123129

124130
if __name__ == "__main__":
@@ -129,11 +135,10 @@ if __name__ == "__main__":
129135
### Connexion
130136

131137
```python
132-
import datetime, logging, sys, json_logging, connexion
138+
import logging, sys, json_logging, connexion
133139

134140
app = connexion.FlaskApp(__name__)
135-
json_logging.ENABLE_JSON_LOGGING = True
136-
json_logging.init_connexion()
141+
json_logging.init_connexion(enable_json=True)
137142
json_logging.init_request_instrument(app)
138143

139144
app.add_api('api.yaml')
@@ -151,7 +156,10 @@ if __name__ == "__main__":
151156
Current request correlation-id can be retrieved and pass to downstream services call as follow:
152157

153158
```python
154-
correlation_id = json_logging.get_correlation_id()
159+
# this will be faster
160+
correlation_id = json_logging.get_correlation_id(request=request)
161+
# this will be slower, but will work in context where you couldn't get a reference of request object
162+
correlation_id_without_request_obj = json_logging.get_correlation_id()
155163
# use correlation id for downstream service calls here
156164
```
157165

@@ -178,7 +186,7 @@ logging library can be configured by setting the value in json_logging, all conf
178186

179187
Name | Description | Default value
180188
--- | --- | ---
181-
ENABLE_JSON_LOGGING | Whether to enable JSON logging mode.Can be set as an environment variable, enable when set to to either one in following list (case-insensitive) **['true', '1', 'y', 'yes']** , this have no effect on request logger | false
189+
ENABLE_JSON_LOGGING | **DEPRECATED** Whether to enable JSON logging mode.Can be set as an environment variable, enable when set to to either one in following list (case-insensitive) **['true', '1', 'y', 'yes']** , this have no effect on request logger | false
182190
ENABLE_JSON_LOGGING_DEBUG | Whether to enable debug logging for this library for development purpose. | true
183191
CORRELATION_ID_HEADERS | List of HTTP headers that will be used to look for correlation-id value. HTTP headers will be searched one by one according to list order| ['X-Correlation-ID','X-Request-ID']
184192
EMPTY_VALUE | Default value when a logging record property is None | '-'
@@ -198,7 +206,7 @@ To add support for a new web framework, you need to extend following classes in
198206

199207
Class | Description | Mandatory
200208
--- | --- | ---
201-
RequestAdapter | Helper class help to extract logging-relevant information from HTTP request object | no
209+
RequestAdapter | Helper class help to extract logging-relevant information from HTTP request object | yes
202210
ResponseAdapter | Helper class help to extract logging-relevant information from HTTP response object | yes
203211
FrameworkConfigurator | Class to perform logging configuration for given framework as needed | no
204212
AppRequestInstrumentationConfigurator | Class to perform request instrumentation logging configuration | no
@@ -236,7 +244,7 @@ e.g.:
236244
"written_ts": 1514048137280721000,
237245
"component_id": "1d930c0xd-19-s3213",
238246
"component_name": "ny-component_name",
239-
"component_instance": 0,
247+
"component_instance_idx": 0,
240248
"logger": "test logger",
241249
"thread": "MainThread",
242250
"level": "INFO",
@@ -256,7 +264,7 @@ e.g.:
256264
"written_ts": 1514048137280721000,
257265
"component_id": "-",
258266
"component_name": "-",
259-
"component_instance": 0,
267+
"component_instance_idx": 0,
260268
"correlation_id": "1975a02e-e802-11e7-8971-28b2bd90b19a",
261269
"remote_user": "user_a",
262270
"request": "/index.html",
@@ -287,7 +295,7 @@ correlation_id | The timestamp in nano-second precision when this request metric
287295
type | Type of logging. "logs" or "request" | string |
288296
component_id | Uniquely identifies the software component that has processed the current request | string | 9e6f3ecf-def0-4baf-8fac-9339e61d5645
289297
component_name | A human-friendly name representing the software component | string | my-fancy-component
290-
component_instance | Instance's index of horizontally scaled service | string | 0
298+
component_instance_idx | Instance's index of horizontally scaled service | string | 0
291299

292300
- application logs
293301

@@ -352,6 +360,7 @@ password=
352360
build
353361
```bash
354362
python setup.py bdist_wheel --universal
363+
python setup.py sdist --universal
355364
```
356365

357366
pypitest
@@ -361,6 +370,6 @@ pip3 install json_logging --index-url https://test.pypi.org/simple/
361370
```
362371
pypi
363372
```
364-
twine upload --repository-url https://upload.pypi.org/legacy/ dist/*
365-
pip3 install json_logging
373+
pip3 install json_loggingtwine upload --repository-url https://upload.pypi.org/legacy/ dist/*
374+
366375
```

example/connexion-example/hello.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
import json_logging
55

66

7-
def post_greeting(name: str) -> str:
7+
def post_greeting(name):
88
return 'Hello {name}'.format(name=name)
99

1010

1111
def create():
1212
app = connexion.FlaskApp(__name__, port=9090, specification_dir='openapi/')
13-
json_logging.ENABLE_JSON_LOGGING = True
14-
json_logging.init_connexion()
13+
json_logging.init_connexion(enable_json=True)
1514
json_logging.init_request_instrument(app)
1615

1716
app.add_api('helloworld-api.yaml', arguments={'title': 'Hello World Example'})

example/connexion-example/openapi/helloworld-api.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ servers:
88

99
paths:
1010
/greeting/{name}:
11-
post:
11+
get:
1212
summary: Generate greeting
1313
description: Generates a greeting message.
1414
operationId: hello.post_greeting

example/custom_log_format.py

+12-15
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ class CustomJSONLog(logging.Formatter):
1919
"""
2020
Customized logger
2121
"""
22-
python_log_prefix = 'python.'
22+
2323
def get_exc_fields(self, record):
2424
if record.exc_info:
2525
exc_info = self.format_exception(record.exc_info)
2626
else:
2727
exc_info = record.exc_text
28-
return {f'{self.python_log_prefix}exc_info': exc_info}
28+
return {'python.exc_info': exc_info}
2929

3030
@classmethod
3131
def format_exception(cls, exc_info):
@@ -38,13 +38,13 @@ def format(self, record):
3838
"caller": record.filename + '::' + record.funcName
3939
}
4040
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
41+
"python.logger_name": record.name,
42+
"python.module": record.module,
43+
"python.funcName": record.funcName,
44+
"python.filename": record.filename,
45+
"python.lineno": record.lineno,
46+
"python.thread": record.threadName,
47+
"python.pid": record.process
4848
}
4949
if hasattr(record, 'props'):
5050
json_log_object['data'].update(record.props)
@@ -55,20 +55,17 @@ def format(self, record):
5555
return json.dumps(json_log_object)
5656

5757

58-
def logger_init():
59-
json_logging.__init(custom_formatter=CustomJSONLog)
60-
6158
# You would normally import logger_init and setup the logger in your main module - e.g.
6259
# main.py
6360

64-
logger_init()
61+
json_logging.init_non_web(custom_formatter=CustomJSONLog)
6562

6663
logger = logging.getLogger(__name__)
6764
logger.setLevel(logging.DEBUG)
6865
logger.addHandler(logging.StreamHandler(sys.stderr))
6966

7067
logger.info('Starting')
7168
try:
72-
1/0
73-
except: # noqa pylint: disable=bare-except
69+
1 / 0
70+
except: # noqa pylint: disable=bare-except
7471
logger.exception('You can\'t divide by zero')

example/flask_sample_app.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
import json_logging
77

88
app = flask.Flask(__name__)
9-
json_logging.ENABLE_JSON_LOGGING = True
10-
json_logging.init_flask()
9+
json_logging.init_flask(enable_json=True)
1110
json_logging.init_request_instrument(app)
1211

1312
# init the logger as usual
@@ -19,8 +18,10 @@
1918
@app.route('/')
2019
def home():
2120
logger.info("test log statement")
22-
logger.info("test log statement", extra={'props': {"extra_property": 'extra_value'}})
23-
return "Hello world"
21+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
22+
correlation_id = json_logging.get_correlation_id()
23+
return "hello world" \
24+
"\ncorrelation_id : " + correlation_id
2425

2526

2627
@app.route('/exception')

example/non_web.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
logger.addHandler(logging.StreamHandler(sys.stdout))
1313

1414
logger.info("test log statement")
15-
logger.info("test log statement", extra={'props': {"extra_property": 'extra_value'}})
15+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})

example/quart_sample_app.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import json_logging
88

99
app = quart.Quart(__name__)
10-
json_logging.ENABLE_JSON_LOGGING = True
11-
json_logging.init_quart()
10+
json_logging.init_quart(enable_json=True)
1211
json_logging.init_request_instrument(app)
1312

1413
# init the logger as usual
@@ -20,8 +19,10 @@
2019
@app.route('/')
2120
async def home():
2221
logger.info("test log statement")
23-
logger.info("test log statement", extra={'props': {"extra_property": 'extra_value'}})
24-
return "Hello world"
22+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
23+
correlation_id = json_logging.get_correlation_id()
24+
return "hello world" \
25+
"\ncorrelation_id : " + correlation_id
2526

2627

2728
if __name__ == "__main__":

example/requirements.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
flask==1.0
2-
sanic==0.7.0
3-
Quart==0.6.7
2+
connexion[swagger-ui]
3+
quart
4+
sanic

example/sanic_sample_app.py

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
#!/usr/bin/env python3
2+
13
import json_logging
24
import logging
35
# noinspection PyPackageRequirements
46
import sanic
57
import sys
68

7-
app = sanic.Sanic()
8-
json_logging.ENABLE_JSON_LOGGING = True
9-
json_logging.__init(framework_name='sanic')
9+
app = sanic.Sanic(name="sanic-web-app")
10+
json_logging.init_sanic(enable_json=True)
1011
json_logging.init_request_instrument(app)
1112

1213
# init the logger as usual
@@ -15,13 +16,19 @@
1516
logger.addHandler(logging.StreamHandler(sys.stdout))
1617

1718

18-
# noinspection PyUnusedLocal
1919
@app.route("/")
20-
async def test(request):
20+
def test(request):
2121
logger.info("test log statement")
22-
logger.info("test log statement", extra={'props': {"extra_property": 'extra_value'}})
23-
# noinspection PyUnresolvedReferences
24-
return sanic.response.text("hello world")
22+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
23+
# this will be faster
24+
correlation_id = json_logging.get_correlation_id(request=request)
25+
# this will be slower, but will work in context you cant get a reference of request object
26+
correlation_id_without_request_obj = json_logging.get_correlation_id()
27+
28+
return sanic.response.text(
29+
"hello world"
30+
"\ncorrelation_id : " + correlation_id +
31+
"\ncorrelation_id_without_request_obj: " + correlation_id_without_request_obj)
2532

2633

2734
if __name__ == "__main__":

0 commit comments

Comments
 (0)