__init__.py (4256B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\beeline\middleware\flask\__init__.py 2 import datetime 3 import threading 4 import beeline 5 import flask 6 from beeline.propagation import Request 7 from flask import current_app, signals 8 from werkzeug.wrappers import Request 9 from beeline.middleware.wsgi import WSGIRequest 10 11 class HoneyMiddleware(object): 12 13 def __init__(self, app, db_events = True): 14 self.app = app 15 self.app.before_request(self._before_request) 16 if signals.signals_available: 17 self.app.teardown_request(self._teardown_request) 18 app.wsgi_app = HoneyWSGIMiddleware(app.wsgi_app) 19 if db_events: 20 app = HoneyDBMiddleware(app) 21 22 def _before_request(self): 23 beeline.add_field('request.route', flask.request.endpoint) 24 25 def _teardown_request(self, exception): 26 if exception: 27 beeline.add_field('request.error_detail', beeline.internal.stringify_exception(exception)) 28 beeline.add_field('request.error', str(type(exception))) 29 beeline.internal.send_event() 30 31 32 class HoneyWSGIMiddleware(object): 33 34 def __init__(self, app): 35 self.app = app 36 37 def __call__(self, environ, start_response): 38 req = Request(environ, shallow=True) 39 wr = WSGIRequest('flask', environ) 40 root_span = beeline.propagate_and_start_trace(wr.request_context(), wr) 41 42 def _start_response(status, headers, *args): 43 status_code = int(status[0:4]) 44 beeline.add_context_field('response.status_code', status_code) 45 if status_code != 500: 46 beeline.finish_trace(root_span) 47 elif status_code == 500 and not signals.signals_available: 48 beeline.finish_trace(root_span) 49 return start_response(status, headers, *args) 50 51 return self.app(environ, _start_response) 52 53 54 class HoneyDBMiddleware(object): 55 56 def __init__(self, app = None): 57 self.app = app 58 if app is not None: 59 self.init_app(app) 60 self.state = threading.local() 61 self.state.span = None 62 63 def init_app(self, app): 64 try: 65 from sqlalchemy.engine import Engine 66 from sqlalchemy.event import listen 67 listen(Engine, 'before_cursor_execute', self.before_cursor_execute) 68 listen(Engine, 'after_cursor_execute', self.after_cursor_execute) 69 listen(Engine, 'handle_error', self.handle_error) 70 except ImportError: 71 pass 72 73 def before_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): 74 if not current_app: 75 return 76 params = [] 77 if type(parameters) == tuple or type(parameters) == list: 78 for param in parameters: 79 if type(param) == datetime.datetime: 80 param = param.isoformat() 81 params.append(param) 82 83 elif type(parameters) == dict: 84 for k, v in parameters.items(): 85 param = '%s=' % k 86 if type(v) == datetime.datetime: 87 v = v.isoformat() 88 param += '%s' % v 89 params.append(param) 90 91 self.state.span = beeline.start_span(context={'name': 'flask_db_query', 92 'type': 'db', 93 'db.query': statement, 94 'db.query_args': params}) 95 self.query_start_time = datetime.datetime.now() 96 97 def after_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): 98 if not current_app: 99 return 100 query_duration = datetime.datetime.now() - self.query_start_time 101 beeline.add_context({'db.duration': query_duration.total_seconds() * 1000, 102 'db.last_insert_id': getattr(cursor, 'lastrowid', None), 103 'db.rows_affected': cursor.rowcount}) 104 if self.state.span: 105 beeline.finish_span(self.state.span) 106 self.state.span = None 107 108 def handle_error(self, context): 109 if not current_app: 110 return 111 beeline.add_context_field('db.error', beeline.internal.stringify_exception(context.original_exception)) 112 if self.state.span: 113 beeline.finish_span(self.state.span) 114 self.state.span = None