Skip to content

Commit 2e6d0a0

Browse files
authored
Merge pull request #21 from donbowman/add-connexion
feat: add support for connexion framework
2 parents d2fec10 + e92167c commit 2e6d0a0

File tree

5 files changed

+204
-0
lines changed

5 files changed

+204
-0
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,27 @@ if __name__ == "__main__":
126126
app.run(host='0.0.0.0', port=int(5000), use_reloader=False, loop=loop)
127127
```
128128

129+
### Connexion
130+
131+
```python
132+
import datetime, logging, sys, json_logging, connexion
133+
134+
app = connexion.FlaskApp(__name__)
135+
json_logging.ENABLE_JSON_LOGGING = True
136+
json_logging.init(framework_name='connexion', specification_dir='openapi/')
137+
json_logging.init_request_instrument(app)
138+
139+
app.add_api('api.yaml')
140+
141+
# init the logger as usual
142+
logger = logging.getLogger("test-logger")
143+
logger.setLevel(logging.DEBUG)
144+
logger.addHandler(logging.StreamHandler(sys.stdout))
145+
146+
if __name__ == "__main__":
147+
app.run()
148+
```
149+
129150
## 2.3 Get current correlation-id
130151
Current request correlation-id can be retrieved and pass to downstream services call as follow:
131152

example/connexion-example/hello.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env python3
2+
3+
import connexion
4+
import json_logging
5+
6+
7+
def post_greeting(name: str) -> str:
8+
return 'Hello {name}'.format(name=name)
9+
10+
def create()
11+
app = connexion.FlaskApp(__name__, port=9090, specification_dir='openapi/')
12+
json_logging.ENABLE_JSON_LOGGING = True
13+
json_logging.init(framework_name='connexion')
14+
json_logging.init_request_instrument(app)
15+
16+
app.add_api('helloworld-api.yaml', arguments={'title': 'Hello World Example'})
17+
return app
18+
19+
if __name__ == '__main__':
20+
21+
app = create()
22+
app.run()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
openapi: "3.0.0"
2+
3+
info:
4+
title: Hello World
5+
version: "1.0"
6+
servers:
7+
- url: http://localhost:9090/v1.0
8+
9+
paths:
10+
/greeting/{name}:
11+
post:
12+
summary: Generate greeting
13+
description: Generates a greeting message.
14+
operationId: hello.post_greeting
15+
responses:
16+
200:
17+
description: greeting response
18+
content:
19+
text/plain:
20+
schema:
21+
type: string
22+
example: "hello dave!"
23+
parameters:
24+
- name: name
25+
in: path
26+
description: Name of the person to greet.
27+
required: true
28+
schema:
29+
type: string
30+
example: "dave"

json_logging/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,11 @@ def format(self, record):
344344
register_framework_support('quart', None, quart_support.QuartAppRequestInstrumentationConfigurator,
345345
quart_support.QuartRequestAdapter,
346346
quart_support.QuartResponseAdapter)
347+
348+
# register connexion support
349+
# noinspection PyPep8
350+
import json_logging.framework.connexion as connexion_support
351+
352+
register_framework_support('connexion', None, connexion_support.ConnexionAppRequestInstrumentationConfigurator,
353+
connexion_support.ConnexionRequestAdapter,
354+
connexion_support.ConnexionResponseAdapter)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# coding=utf-8
2+
import logging
3+
import sys
4+
5+
import json_logging
6+
import json_logging.framework
7+
from json_logging import JSONLogWebFormatter
8+
from json_logging.framework_base import AppRequestInstrumentationConfigurator, RequestAdapter, ResponseAdapter
9+
10+
11+
def is_connexion_present():
12+
# noinspection PyPep8,PyBroadException
13+
try:
14+
import connexion
15+
return True
16+
except:
17+
return False
18+
19+
20+
if is_connexion_present():
21+
from connexion import request as request_obj
22+
import connexion as connexion
23+
from flask import g
24+
25+
_current_request = request_obj
26+
_connexion = connexion
27+
_connexion.g = g
28+
29+
30+
class ConnexionAppRequestInstrumentationConfigurator(AppRequestInstrumentationConfigurator):
31+
def config(self, app):
32+
if not is_connexion_present():
33+
raise RuntimeError("connexion is not available in system runtime")
34+
from flask.app import Flask
35+
if not isinstance(app.app, Flask):
36+
raise RuntimeError("app is not a valid connexion.app.Connexion app instance")
37+
38+
# Disable standard logging
39+
logging.getLogger('werkzeug').disabled = True
40+
41+
json_logging.util.update_formatter_for_loggers([logging.getLogger('werkzeug')], JSONLogWebFormatter)
42+
43+
# noinspection PyAttributeOutsideInit
44+
self.request_logger = logging.getLogger('connexion-request-logger')
45+
self.request_logger.setLevel(logging.DEBUG)
46+
self.request_logger.addHandler(logging.StreamHandler(sys.stdout))
47+
48+
from flask import g
49+
50+
@app.app.before_request
51+
def before_request():
52+
g.request_info = json_logging.RequestInfo(_current_request)
53+
54+
@app.app.after_request
55+
def after_request(response):
56+
request_info = g.request_info
57+
request_info.update_response_status(response)
58+
# TODO:handle to print out request instrumentation in non-JSON mode
59+
self.request_logger.info("", extra={'request_info': request_info})
60+
return response
61+
62+
63+
class ConnexionRequestAdapter(RequestAdapter):
64+
@staticmethod
65+
def get_request_class_type():
66+
raise NotImplementedError
67+
68+
@staticmethod
69+
def support_global_request_object():
70+
return True
71+
72+
@staticmethod
73+
def get_current_request():
74+
return _current_request
75+
76+
def get_remote_user(self, request):
77+
if request.authorization is not None:
78+
return request.authorization.username
79+
else:
80+
return json_logging.EMPTY_VALUE
81+
82+
def is_in_request_context(self, request_):
83+
return _connexion.has_request_context()
84+
85+
def get_http_header(self, request, header_name, default=None):
86+
if header_name in request.headers:
87+
return request.headers.get(header_name)
88+
return default
89+
90+
def set_correlation_id(self, request_, value):
91+
_connexion.g.correlation_id = value
92+
93+
def get_correlation_id_in_request_context(self, request):
94+
return _connexion.g.get('correlation_id', None)
95+
96+
def get_protocol(self, request):
97+
return request.environ.get('SERVER_PROTOCOL')
98+
99+
def get_path(self, request):
100+
return request.path
101+
102+
def get_content_length(self, request):
103+
return request.content_length
104+
105+
def get_method(self, request):
106+
return request.method
107+
108+
def get_remote_ip(self, request):
109+
return request.remote_addr
110+
111+
def get_remote_port(self, request):
112+
return request.environ.get('REMOTE_PORT')
113+
114+
115+
class ConnexionResponseAdapter(ResponseAdapter):
116+
def get_status_code(self, response):
117+
return response.status_code
118+
119+
def get_response_size(self, response):
120+
return response.calculate_content_length()
121+
122+
def get_content_type(self, response):
123+
return response.content_type

0 commit comments

Comments
 (0)