Skip to content

Commit 04a5810

Browse files
author
Bob Bui
committed
exclude a specific API endpoints from logging requests fix #44
1 parent 4965145 commit 04a5810

15 files changed

+112
-38
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ 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.5 - 2020-08-04
8+
- fix #44
9+
710
## 1.2.4 - 2020-08-03
811
- fix #51
912

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ If you're using Cloud Foundry, it might worth to check out the library [SAP/cf-p
1111
2.4 [Log extra properties](#24-log-extra-properties)
1212
2.5 [Root logger](#25-root-logger)
1313
2.6 [Custom log formatter](#26-custom-log-formatter)
14+
2.7 [Exclude certain URl from request instrumentation](#27-exclude-certain-url-from-request-instrumentation)
1415
3. [Configuration](#3-configuration)
1516
4. [Python References](#4-python-references)
1617
5. [Framework support plugin development](#5-framework-support-plugin-development)
@@ -190,6 +191,13 @@ json_logging.config_root_logger()
190191
Customer JSON log formatter can be passed to init method. see example for more detail:
191192
https://github.com/thangbn/json-logging-python/blob/master/example/custom_log_format.py
192193

194+
## 2.7 Exclude certain URl from request instrumentation
195+
Certain URL can be excluded from request instrumentation by specifying a list of regex into **init_request_instrument** method like below:
196+
197+
```python
198+
json_logging.init_request_instrument(app, exclude_url_patterns=[r'/exclude_from_request_instrumentation'])
199+
```
200+
193201
# 3. Configuration
194202
logging library can be configured by setting the value in json_logging, all configuration must be placed before json_logging.init method call
195203

example/connexion-example/hello.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
#!/usr/bin/env python3
2+
import logging
3+
import sys
24

35
import connexion
46
import json_logging
57

8+
# init the logger as usual
9+
logger = logging.getLogger("test logger")
10+
logger.setLevel(logging.DEBUG)
11+
logger.addHandler(logging.StreamHandler(sys.stdout))
12+
613

714
def post_greeting(name):
15+
logger.info("test log statement")
16+
logger.info("test log statement with extra props", extra={'props': {"extra_property": 'extra_value'}})
817
return 'Hello {name}'.format(name=name)
918

1019

20+
def exclude_from_request_instrumentation(name):
21+
return 'Hello {name}. this request wont log request instrumentation information'.format(name=name)
22+
23+
1124
def create():
1225
app = connexion.FlaskApp(__name__, port=9090, specification_dir='openapi/')
1326
json_logging.init_connexion(enable_json=True)
14-
json_logging.init_request_instrument(app)
27+
json_logging.init_request_instrument(app, exclude_url_patterns=[r'/exclude_from_request_instrumentation'])
1528

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

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

+21
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,24 @@ paths:
2828
schema:
2929
type: string
3030
example: "dave"
31+
/exclude_from_request_instrumentation:
32+
get:
33+
summary: Generate greeting
34+
description: Generates a greeting message.
35+
operationId: hello.exclude_from_request_instrumentation
36+
responses:
37+
200:
38+
description: greeting response
39+
content:
40+
text/plain:
41+
schema:
42+
type: string
43+
example: "hello dave!"
44+
parameters:
45+
- name: name
46+
in: path
47+
description: Name of the person to greet.
48+
required: true
49+
schema:
50+
type: string
51+
example: "dave"

example/flask_sample_app.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
app = flask.Flask(__name__)
99
json_logging.init_flask(enable_json=True)
10-
json_logging.init_request_instrument(app)
10+
json_logging.init_request_instrument(app, exclude_url_patterns=[r'/exclude_from_request_instrumentation'])
1111

1212
# init the logger as usual
1313
logger = logging.getLogger("test logger")
@@ -34,5 +34,10 @@ def exception():
3434
return "Error occurred, check log for detail"
3535

3636

37+
@app.route('/exclude_from_request_instrumentation')
38+
def exclude_from_request_instrumentation():
39+
return "this request wont log request instrumentation information"
40+
41+
3742
if __name__ == "__main__":
3843
app.run(host='0.0.0.0', port=int(5000), use_reloader=False)

example/quart_sample_app.py

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

99
app = quart.Quart(__name__)
1010
json_logging.init_quart(enable_json=True)
11-
json_logging.init_request_instrument(app)
11+
json_logging.init_request_instrument(app, exclude_url_patterns=[r'/exclude_from_request_instrumentation'])
1212

1313
# init the logger as usual
1414
logger = logging.getLogger("test logger")
@@ -25,6 +25,11 @@ async def home():
2525
"\ncorrelation_id : " + correlation_id
2626

2727

28+
@app.route('/exclude_from_request_instrumentation')
29+
def exclude_from_request_instrumentation():
30+
return "this request wont log request instrumentation information"
31+
32+
2833
if __name__ == "__main__":
2934
loop = asyncio.get_event_loop()
3035
app.run(host='0.0.0.0', port=int(5001), use_reloader=False, loop=loop)

example/sanic_sample_app.py

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

99
app = sanic.Sanic(name="sanic-web-app")
1010
json_logging.init_sanic(enable_json=True)
11-
json_logging.init_request_instrument(app)
11+
json_logging.init_request_instrument(app, exclude_url_patterns=[r'/exclude_from_request_instrumentation'])
1212

1313
# init the logger as usual
1414
logger = logging.getLogger("sanic-integration-test-app")
@@ -31,5 +31,10 @@ def test(request):
3131
"\ncorrelation_id_without_request_obj: " + correlation_id_without_request_obj)
3232

3333

34+
@app.route('/exclude_from_request_instrumentation')
35+
def exclude_from_request_instrumentation(request):
36+
return sanic.response.text("this request wont log request instrumentation information")
37+
38+
3439
if __name__ == "__main__":
3540
app.run(host="0.0.0.0", port=8000)

json_logging/__init__.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
_request_util = None
3232
_default_formatter = None
3333

34-
3534
def get_correlation_id(request=None):
3635
"""
3736
Get current request correlation-id. If one is not present, a new one might be generated
@@ -152,7 +151,7 @@ def __init(framework_name=None, custom_formatter=None, enable_json=False):
152151
util.update_formatter_for_loggers(existing_loggers, _default_formatter)
153152

154153

155-
def init_request_instrument(app=None, custom_formatter=None):
154+
def init_request_instrument(app=None, custom_formatter=None, exclude_url_patterns=[]):
156155
"""
157156
Configure the request instrumentation logging configuration for given web app. Must be called after init method
158157
@@ -170,7 +169,7 @@ def init_request_instrument(app=None, custom_formatter=None):
170169
raise ValueError('custom_formatter is not subclass of logging.Formatter', custom_formatter)
171170

172171
configurator = _current_framework['app_request_instrumentation_configurator']()
173-
configurator.config(app)
172+
configurator.config(app, exclude_url_patterns=exclude_url_patterns)
174173

175174
formatter = custom_formatter if custom_formatter else JSONRequestLogFormatter
176175
request_logger = configurator.request_logger

json_logging/framework/connexion/__init__.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json_logging.framework
77
from json_logging import JSONLogWebFormatter
88
from json_logging.framework_base import AppRequestInstrumentationConfigurator, RequestAdapter, ResponseAdapter
9+
from json_logging.util import is_not_match_any_pattern
910

1011

1112
def is_connexion_present():
@@ -30,7 +31,7 @@ def is_connexion_present():
3031

3132

3233
class ConnexionAppRequestInstrumentationConfigurator(AppRequestInstrumentationConfigurator):
33-
def config(self, app):
34+
def config(self, app, exclude_url_patterns=[]):
3435
if not is_connexion_present():
3536
raise RuntimeError("connexion is not available in system runtime")
3637
from flask.app import Flask
@@ -45,18 +46,19 @@ def config(self, app):
4546
# noinspection PyAttributeOutsideInit
4647
self.request_logger = logging.getLogger('connexion-request-logger')
4748

48-
4949
from flask import g
5050

5151
@app.app.before_request
5252
def before_request():
53-
g.request_info = json_logging.RequestInfo(_current_request)
53+
if is_not_match_any_pattern(_current_request.path, exclude_url_patterns):
54+
g.request_info = json_logging.RequestInfo(_current_request)
5455

5556
@app.app.after_request
5657
def after_request(response):
57-
request_info = g.request_info
58-
request_info.update_response_status(response)
59-
self.request_logger.info("", extra={'request_info': request_info})
58+
if hasattr(g, 'request_info'):
59+
request_info = g.request_info
60+
request_info.update_response_status(response)
61+
self.request_logger.info("", extra={'request_info': request_info})
6062
return response
6163

6264

json_logging/framework/flask/__init__.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# coding=utf-8
22
import logging
3-
import sys
43

54
import json_logging
65
import json_logging.framework
76
from json_logging.framework_base import AppRequestInstrumentationConfigurator, RequestAdapter, ResponseAdapter
87

8+
from json_logging.util import is_not_match_any_pattern
9+
910

1011
def is_flask_present():
1112
# noinspection PyPep8,PyBroadException
@@ -25,7 +26,7 @@ def is_flask_present():
2526

2627

2728
class FlaskAppRequestInstrumentationConfigurator(AppRequestInstrumentationConfigurator):
28-
def config(self, app):
29+
def config(self, app, exclude_url_patterns=[]):
2930
if not is_flask_present():
3031
raise RuntimeError("flask is not available in system runtime")
3132
from flask.app import Flask
@@ -45,13 +46,15 @@ def config(self, app):
4546

4647
@app.before_request
4748
def before_request():
48-
g.request_info = json_logging.RequestInfo(_current_request)
49+
if is_not_match_any_pattern(_current_request.path, exclude_url_patterns):
50+
g.request_info = json_logging.RequestInfo(_current_request)
4951

5052
@app.after_request
5153
def after_request(response):
52-
request_info = g.request_info
53-
request_info.update_response_status(response)
54-
self.request_logger.info("", extra={'request_info': request_info})
54+
if hasattr(g, 'request_info'):
55+
request_info = g.request_info
56+
request_info.update_response_status(response)
57+
self.request_logger.info("", extra={'request_info': request_info})
5558
return response
5659

5760

json_logging/framework/quart/__init__.py

+14-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json_logging.framework
77
from json_logging import JSONLogWebFormatter
88
from json_logging.framework_base import AppRequestInstrumentationConfigurator, RequestAdapter, ResponseAdapter
9+
from json_logging.util import is_not_match_any_pattern
910

1011

1112
def is_quart_present():
@@ -26,38 +27,39 @@ def is_quart_present():
2627

2728

2829
class QuartAppRequestInstrumentationConfigurator(AppRequestInstrumentationConfigurator):
29-
def config(self, app):
30+
def config(self, app, exclude_url_patterns=[]):
3031
if not is_quart_present():
3132
raise RuntimeError("quart is not available in system runtime")
3233
from quart.app import Quart
3334
if not isinstance(app, Quart):
3435
raise RuntimeError("app is not a valid quart.app.Quart app instance")
3536

3637
# Remove quart logging handlers
37-
from quart.logging import default_handler, serving_handler
38+
from quart.logging import default_handler
3839
logging.getLogger('quart.app').removeHandler(default_handler)
39-
logging.getLogger('quart.serving').removeHandler(serving_handler)
40-
4140
json_logging.util.update_formatter_for_loggers([
42-
# logging.getLogger('quart.app'),
43-
# logging.getLogger('quart.serving'),
41+
logging.getLogger('quart.app'),
4442
], JSONLogWebFormatter)
4543

44+
logging.getLogger('quart.serving').disabled = True
45+
4646
# noinspection PyAttributeOutsideInit
47-
self.request_logger = logging.getLogger('quart.app')
47+
self.request_logger = logging.getLogger('quart-request-logger')
4848

4949
from quart import g
5050

5151
@app.before_request
5252
def before_request():
53-
g.request_info = json_logging.RequestInfo(_current_request)
53+
if is_not_match_any_pattern(_current_request.path, exclude_url_patterns):
54+
g.request_info = json_logging.RequestInfo(_current_request)
5455

5556
@app.after_request
5657
def after_request(response):
57-
request_info = g.request_info
58-
request_info.update_response_status(response)
59-
# TODO:handle to print out request instrumentation in non-JSON mode
60-
self.request_logger.info("", extra={'request_info': request_info})
58+
if hasattr(g, 'request_info'):
59+
request_info = g.request_info
60+
request_info.update_response_status(response)
61+
# TODO:handle to print out request instrumentation in non-JSON mode
62+
self.request_logger.info("", extra={'request_info': request_info})
6163
return response
6264

6365

json_logging/framework/sanic/__init__.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import json_logging.framework
99
from json_logging.framework_base import FrameworkConfigurator, AppRequestInstrumentationConfigurator, RequestAdapter, \
1010
ResponseAdapter
11+
from json_logging.util import is_not_match_any_pattern
1112

1213

1314
def is_sanic_present():
@@ -41,7 +42,7 @@ def config(self):
4142

4243

4344
class SanicAppRequestInstrumentationConfigurator(AppRequestInstrumentationConfigurator):
44-
def config(self, app):
45+
def config(self, app, exclude_url_patterns=[]):
4546
if not is_sanic_present():
4647
raise RuntimeError("Sanic is not available in system runtime")
4748
# noinspection PyPackageRequirements
@@ -56,13 +57,15 @@ def config(self, app):
5657

5758
@app.middleware('request')
5859
def before_request(request):
59-
request.ctx.request_info = json_logging.RequestInfo(request)
60+
if is_not_match_any_pattern(request.path, exclude_url_patterns):
61+
request.ctx.request_info = json_logging.RequestInfo(request)
6062

6163
@app.middleware('response')
6264
def after_request(request, response):
63-
request_info = request.ctx.request_info
64-
request_info.update_response_status(response)
65-
self.request_logger.info("", extra={'request_info': request_info, 'type': 'request'})
65+
if hasattr(request.ctx, 'request_info'):
66+
request_info = request.ctx.request_info
67+
request_info.update_response_status(response)
68+
self.request_logger.info("", extra={'request_info': request_info, 'type': 'request'})
6669

6770
def get_request_logger(self):
6871
return self.request_logger

json_logging/framework_base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def __new__(cls, *args, **kw):
189189
cls._instance.request_logger = None
190190
return cls._instance
191191

192-
def config(self, app):
192+
def config(self, app, exclude_url_patterns=None):
193193
"""
194194
configuration logic
195195

json_logging/util.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# coding=utf-8
22
import logging
33
import os
4+
import re
45
import sys
56
from datetime import datetime
67
from logging import Logger, StreamHandler
@@ -203,3 +204,7 @@ def _get_correlation_id_in_request_header(request_adapter, request):
203204
if value is not None:
204205
return value
205206
return None
207+
208+
209+
def is_not_match_any_pattern(path, patterns):
210+
return all(map(lambda pattern: re.search(pattern, path) is None, patterns))

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
setup(
1414
name="json-logging",
15-
version='1.2.4',
15+
version='1.2.5',
1616
packages=find_packages(exclude=['contrib', 'docs', 'tests*', 'example', 'dist', 'build']),
1717
license='Apache License 2.0',
1818
description="JSON Python Logging",

0 commit comments

Comments
 (0)