async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: """Processes the whole request APM capturing. Args: request (Request) call_next (RequestResponseEndpoint): Next request process in Starlette. Returns: Response """ await self._request_started(request) try: response = await call_next(request) except Exception: await self.capture_exception( context={ "request": await get_data_from_request(request, self.client.config, constants.ERROR) }) zuqa.set_transaction_result("HTTP 5xx", override=False) zuqa.set_context({"status_code": 500}, "response") raise else: await self._request_finished(response) finally: self.client.end_transaction() return response
async def call(self, module, method, wrapped, instance, args, kwargs): if not hasattr(instance.application, "zuqa_client"): # If tornado was instrumented but not as the main framework # (i.e. in Flower), we should skip it. return await wrapped(*args, **kwargs) # Late import to avoid ImportErrors from zuqa.contrib.tornado.utils import get_data_from_request, get_data_from_response request = instance.request trace_parent = TraceParent.from_headers(request.headers) client = instance.application.zuqa_client client.begin_transaction("request", trace_parent=trace_parent) zuqa.set_context( lambda: get_data_from_request(instance, request, client.config, constants.TRANSACTION), "request") # TODO: Can we somehow incorporate the routing rule itself here? zuqa.set_transaction_name("{} {}".format(request.method, type(instance).__name__), override=False) ret = await wrapped(*args, **kwargs) zuqa.set_context( lambda: get_data_from_response(instance, client.config, constants. TRANSACTION), "response") result = "HTTP {}xx".format(instance.get_status() // 100) zuqa.set_transaction_result(result, override=False) client.end_transaction() return ret
def call(self, module, method, wrapped, instance, args, kwargs): if not hasattr(instance.application, "zuqa_client"): # If tornado was instrumented but not as the main framework # (i.e. in Flower), we should skip it. return wrapped(*args, **kwargs) # Late import to avoid ImportErrors from tornado.web import Finish, HTTPError from zuqa.contrib.tornado.utils import get_data_from_request e = args[0] if isinstance(e, Finish): # Not an error; Finish is an exception that ends a request without an error response return wrapped(*args, **kwargs) client = instance.application.zuqa_client request = instance.request client.capture_exception( context={ "request": get_data_from_request(instance, request, client.config, constants.ERROR) }) if isinstance(e, HTTPError): zuqa.set_transaction_result("HTTP {}xx".format( int(e.status_code / 100)), override=False) zuqa.set_context({"status_code": e.status_code}, "response") else: zuqa.set_transaction_result("HTTP 5xx", override=False) zuqa.set_context({"status_code": 500}, "response") return wrapped(*args, **kwargs)
def test_dedot_is_not_run_when_unsampled(zuqa_client): for sampled in (True, False): t = zuqa_client.begin_transaction("test") t.is_sampled = sampled zuqa.set_context(lambda: {"a.b": "b"}) zuqa_client.end_transaction("x", "OK") sampled_transaction, unsampled_transaction = zuqa_client.events[TRANSACTION] assert "a_b" in sampled_transaction["context"]["custom"] assert "context" not in unsampled_transaction
def request_started(self, app): if not self.app.debug or self.client.config.debug: trace_parent = TraceParent.from_headers(request.headers) self.client.begin_transaction("request", trace_parent=trace_parent) zuqa.set_context( lambda: get_data_from_request(request, self.client.config, constants.TRANSACTION), "request") rule = request.url_rule.rule if request.url_rule is not None else "" rule = build_name_with_http_method_prefix(rule, request) zuqa.set_transaction_name(rule, override=False)
def test_callable_context_ignored_when_not_sampled(zuqa_client): callable_data = mock.Mock() callable_data.return_value = {"a": "b"} transaction = zuqa_client.begin_transaction("test") transaction.is_sampled = False zuqa.set_context({"c": "d"}) zuqa.set_context(callable_data) zuqa_client.end_transaction("test", "OK") transaction = zuqa_client.events[TRANSACTION][0] assert callable_data.call_count == 0 assert "context" not in transaction
def request_finished(self, app, response): if not self.app.debug or self.client.config.debug: zuqa.set_context( lambda: get_data_from_response(response, self.client.config, constants.TRANSACTION), "response") if response.status_code: result = "HTTP {}xx".format(response.status_code // 100) else: result = response.status zuqa.set_transaction_result(result, override=False) # Instead of calling end_transaction here, we defer the call until the response is closed. # This ensures that we capture things that happen until the WSGI server closes the response. response.call_on_close(self.client.end_transaction)
async def handle_request(request, handler): zuqa_client = app.get(CLIENT_KEY) if zuqa_client: request[CLIENT_KEY] = zuqa_client trace_parent = AioHttpTraceParent.from_headers(request.headers) zuqa_client.begin_transaction("request", trace_parent=trace_parent) resource = request.match_info.route.resource name = request.method if resource: # canonical has been added in 3.3, and returns one of path, formatter, prefix for attr in ("canonical", "_path", "_formatter", "_prefix"): if hasattr(resource, attr): name += " " + getattr(resource, attr) break else: name += " unknown route" else: name += " unknown route" zuqa.set_transaction_name(name, override=False) zuqa.set_context( lambda: get_data_from_request(request, zuqa_client.config, constants.TRANSACTION), "request") try: response = await handler(request) zuqa.set_transaction_result("HTTP {}xx".format(response.status // 100), override=False) zuqa.set_context( lambda: get_data_from_response(response, zuqa_client.config, constants.TRANSACTION), "response") return response except Exception as exc: if zuqa_client: zuqa_client.capture_exception( context={ "request": get_data_from_request(request, zuqa_client.config, constants.ERROR) }) zuqa.set_transaction_result("HTTP 5xx", override=False) zuqa.set_context({"status_code": 500}, "response") # some exceptions are response-like, e.g. have headers and status code. Let's try and capture them if isinstance(exc, (Response, HTTPException)): zuqa.set_context( lambda: get_data_from_response(exc, zuqa_client.config, constants.ERROR), # noqa: F821 "response", ) raise finally: zuqa_client.end_transaction()
def process_response(self, request, response): if django_settings.DEBUG and not self.client.config.debug: return response try: if hasattr(response, "status_code"): transaction_name = None if self.client.config.django_transaction_name_from_route and hasattr( request.resolver_match, "route"): transaction_name = request.resolver_match.route elif getattr(request, "_zuqa_view_func", False): transaction_name = get_name_from_func( request._zuqa_view_func) if transaction_name: transaction_name = build_name_with_http_method_prefix( transaction_name, request) zuqa.set_transaction_name(transaction_name, override=False) zuqa.set_context( lambda: self.client.get_data_from_request( request, constants.TRANSACTION), "request") zuqa.set_context( lambda: self.client.get_data_from_response( response, constants.TRANSACTION), "response") zuqa.set_context(lambda: self.client.get_user_info(request), "user") zuqa.set_transaction_result("HTTP {}xx".format( response.status_code // 100), override=False) except Exception: self.client.error_logger.error( "Exception during timing of request", exc_info=True) return response
def test_dedot_context_keys(zuqa_client): zuqa_client.begin_transaction("test") zuqa.set_context({"d.o.t": "d_o_t", "s*t*a*r": "s_t_a_r", "q*u*o*t*e": "q_u_o_t_e"}) zuqa_client.end_transaction("foo", 200) transaction = zuqa_client.events[TRANSACTION][0] assert transaction["context"]["custom"] == {"s_t_a_r": "s_t_a_r", "q_u_o_t_e": "q_u_o_t_e", "d_o_t": "d_o_t"}