async def server_call_wrapper(wrapped, _instance, args, kwargs): """ Wraps the main fastapi server request entrypoint - which is the ServerErrorMiddleware __call_ function. :param wrapped: wrapt's wrapped :param instance: wrapt's instance :param args: wrapt's args :param kwargs: wrapt's kwargs """ # Skip on Lambda environment since it's not relevant and might be duplicate if is_lambda_env() or not args or len(args) != 3: return await wrapped(*args, **kwargs) scope = args[0] if not scope or scope.get('type', '') != 'http': return await wrapped(*args, **kwargs) trace = None try: epsagon.trace.trace_factory.switch_to_multiple_traces() unique_id = str(uuid.uuid4()) trace = epsagon.trace.trace_factory.get_or_create_trace( unique_id=unique_id) trace.prepare() scope[EPSAGON_MARKER] = { SCOPE_UNIQUE_ID: unique_id, } except Exception: # pylint: disable=broad-except _clean_trace(trace) return await wrapped(*args, **kwargs) response = None raised_error = None sent_trace = False epsagon_scope = scope[EPSAGON_MARKER] try: response = await wrapped(*args, **kwargs) except Exception as exception: # pylint: disable=broad-except raised_error = exception if trace.runner and not epsagon_scope.get(SCOPE_IGNORE_REQUEST): try: if raised_error: traceback_data = get_traceback_data_from_exception( raised_error) trace.runner.set_exception(raised_error, traceback_data) trace.runner.update_status_code(DEFAULT_ERROR_STATUS_CODE, override=False) await run_in_threadpool(epsagon.trace.trace_factory.send_traces, trace=trace) sent_trace = True except Exception as exception: # pylint: disable=broad-except print_debug('Failed to send traces: {}'.format(exception)) scope.pop(EPSAGON_MARKER, None) if not sent_trace: _clean_trace(trace) if raised_error: raise raised_error from None return response
async def custom_route_handler(request: Request) -> Response: """ Traces given request and its response. :param request: to trace """ should_ignore_request = True try: epsagon.trace.trace_factory.switch_to_async_tracer() if not ignore_request('', request.url.path.lower()): should_ignore_request = False trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() except Exception as exception: # pylint: disable=W0703 return await original_route_handler(request) if should_ignore_request: return await original_route_handler(request) runner = None response = None try: body = await request.json() except json.decoder.JSONDecodeError: body = '' try: runner = FastapiRunner(time.time(), request, json.dumps(body)) trace.set_runner(runner) collect_container_metadata(runner.resource['metadata']) except Exception as exception: # pylint: disable=W0703 warnings.warn('Could not extract request', EpsagonWarning) raised_err = None try: response: Response = await original_route_handler(request) except Exception as exception: # pylint: disable=W0703 raised_err = exception traceback_data = get_traceback_data_from_exception(exception) trace.runner.set_exception(exception, traceback_data) try: if not raised_err and response is not None and runner: if ignore_request( response.headers.get('Content-Type', '').lower(), '' ): return response runner.update_response(response) if runner: epsagon.trace.trace_factory.send_traces() except Exception as exception: # pylint: disable=W0703 print_debug('Failed to send traces: {}'.format(exception)) if raised_err: raise raised_err return response
async def AiohttpMiddleware(request, handler): """ aiohttp middleware to create a runner with event details :param request: incoming request data :param handler: original handler :return: response data from the handler """ print_debug('[aiohttp] started middleware') epsagon.trace.trace_factory.switch_to_async_tracer() if (ignore_request('', request.path.lower()) or is_ignored_endpoint(request.path.lower())): print_debug('[aiohttp] ignoring request') return await handler(request) trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() runner = None response = None try: body = await request.text() print_debug('[aiohttp] got body') runner = AiohttpRunner(time.time(), request, body, handler) trace.set_runner(runner) collect_container_metadata(runner.resource['metadata']) print_debug('[aiohttp] initialized runner') except Exception as exception: # pylint: disable=W0703 warnings.warn('Could not extract request', EpsagonWarning) raised_err = None try: response = await handler(request) print_debug('[aiohttp] got response') except HTTPNotFound: # Ignoring 404s epsagon.trace.trace_factory.pop_trace(trace) raise except Exception as exception: # pylint: disable=W0703 raised_err = exception traceback_data = get_traceback_data_from_exception(exception) trace.runner.set_exception(exception, traceback_data) if response is not None and runner: if ignore_request(response.content_type.lower(), ''): return response runner.update_response(response) if runner: print_debug('[aiohttp] sending trace') epsagon.trace.trace_factory.send_traces() if raised_err: print_debug('[aiohttp] raising error') raise raised_err print_debug('[aiohttp] middleware done') return response
def process_exception(self, request, process_exception): """ Processes and appends a given exception to the current trace """ if not process_exception: return if (not hasattr(request, 'epsagon_trace') or not request.epsagon_trace.runner): return traceback_data = get_traceback_data_from_exception(process_exception) request.epsagon_trace.runner.set_exception(process_exception, traceback_data, False)
def _teardown_request(self, exception): """ Runs at the end of the request. Exception will be passed if happens. If no flask url rule exists for a request, then the request trace will be passed. :param exception: Exception (or None). :return: None. """ if self.ignored_request: return trace = epsagon.trace.trace_factory.get_or_create_trace() if exception and trace.runner: traceback_data = get_traceback_data_from_exception(exception) trace.runner.set_exception(exception, traceback_data) # Ignoring endpoint, only if no error happened. if (not exception and request.url_rule and request.url_rule.rule in self.ignored_endpoints): return epsagon.trace.trace_factory.send_traces()
async def AiohttpMiddleware(request, handler): """ aiohttp middleware to create a runner with event details :param request: incoming request data :param handler: original handler :return: response data from the handler """ epsagon.trace.trace_factory.switch_to_async_tracer() if ignore_request('', request.path.lower()): return await handler(request) trace = epsagon.trace.trace_factory.get_or_create_trace() trace.prepare() runner = None response = None try: body = await request.text() runner = AiohttpRunner(time.time(), request, body, handler) trace.set_runner(runner) collect_container_metadata(runner.resource['metadata']) except Exception as exception: # pylint: disable=W0703 warnings.warn('Could not extract request', EpsagonWarning) try: response = await handler(request) except Exception as exception: # pylint: disable=W0703 traceback_data = get_traceback_data_from_exception(exception) trace.runner.set_exception(exception, traceback_data) if response is not None and runner: if ignore_request(response.content_type.lower(), ''): return response runner.update_response(response) if runner: epsagon.trace.trace_factory.send_traces() return response
def process_exception(self, _, process_exception): if process_exception: traceback_data = get_traceback_data_from_exception( process_exception) self.runner.set_exception(process_exception, traceback_data, False)