예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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)
예제 #4
0
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)
예제 #6
0
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()
예제 #9
0
    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
예제 #10
0
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"}