Example #1
0
def _handle_error(span, response_error, status_code):
    # response_error should be a grpc.Future and so we expect to have cancelled(),
    # exception() and traceback() methods if a computation has resulted in an
    # exception being raised
    if (
        not callable(getattr(response_error, "cancelled", None))
        and not callable(getattr(response_error, "exception", None))
        and not callable(getattr(response_error, "traceback", None))
    ):
        return

    if response_error.cancelled():
        # handle cancelled futures separately to avoid raising grpc.FutureCancelledError
        span.error = 1
        exc_val = to_unicode(response_error.details())
        span._set_str_tag(ERROR_MSG, exc_val)
        span._set_str_tag(ERROR_TYPE, status_code)
        return

    exception = response_error.exception()
    traceback = response_error.traceback()

    if exception is not None and traceback is not None:
        span.error = 1
        if isinstance(exception, grpc.RpcError):
            # handle internal gRPC exceptions separately to get status code and
            # details as tags properly
            exc_val = to_unicode(response_error.details())
            span._set_str_tag(ERROR_MSG, exc_val)
            span._set_str_tag(ERROR_TYPE, status_code)
            span._set_str_tag(ERROR_STACK, stringify(traceback))
        else:
            exc_type = type(exception)
            span.set_exc_info(exc_type, exception, traceback)
            status_code = to_unicode(response_error.code())
def _handle_server_exception(server_context, span):
    if server_context is not None and hasattr(
            server_context, "_state") and server_context._state is not None:
        code = to_unicode(server_context._state.code)
        details = to_unicode(server_context._state.details)
        span.error = 1
        span._set_str_tag(errors.ERROR_MSG, details)
        span._set_str_tag(errors.ERROR_TYPE, code)
Example #3
0
 def test_to_unicode_unicode_double_decode(self):
     # Calling `compat.to_unicode` on a unicode decoded string
     # This represents the double-decode issue, which can cause a `UnicodeEncodeError`
     #   `'\xc3\xbf'.decode('utf-8').decode('utf-8')`
     res = to_unicode(b"\xc3\xbf".decode("utf-8"))
     assert type(res) == unicode
     assert res == u"ÿ"
Example #4
0
    def test_200(self, query_string=""):
        if query_string:
            fqs = "?" + query_string
        else:
            fqs = ""

        # setup our test app
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app(self.tracer)

        # make a request
        resp = self.app.get("/hi/dougie" + fqs)
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u"hi dougie"
        # validate it's traced
        spans = self.pop_spans()
        assert len(spans) == 1
        s = spans[0]

        assert_is_measured(s)
        assert s.name == "bottle.request"
        assert s.service == "bottle-app"
        assert s.span_type == "web"
        assert s.resource == "GET /hi/<name>"
        assert_span_http_status_code(s, 200)
        assert s.get_tag("http.method") == "GET"
        assert s.get_tag(http.URL) == "http://localhost:80/hi/dougie"
        if ddtrace.config.bottle.trace_query_string:
            assert s.get_tag(http.QUERY_STRING) == query_string
        else:
            assert http.QUERY_STRING not in s.meta
Example #5
0
    def test_200_ot(self):
        ot_tracer = init_tracer("my_svc", self.tracer)

        # setup our test app
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app(self.tracer)

        # make a request
        with ot_tracer.start_active_span("ot_span"):
            resp = self.app.get("/hi/dougie")

        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u"hi dougie"
        # validate it's traced
        spans = self.pop_spans()
        assert len(spans) == 2
        ot_span, dd_span = spans

        # confirm the parenting
        assert ot_span.parent_id is None
        assert dd_span.parent_id == ot_span.span_id

        assert ot_span.resource == "ot_span"

        assert_is_measured(dd_span)
        assert dd_span.name == "bottle.request"
        assert dd_span.service == "bottle-app"
        assert dd_span.resource == "GET /hi/<name>"
        assert_span_http_status_code(dd_span, 200)
        assert dd_span.get_tag("http.method") == "GET"
        assert dd_span.get_tag(http.URL) == "http://localhost:80/hi/dougie"
Example #6
0
    def test_analytics_global_off_integration_on(self):
        """
        When making a request
            When an integration trace search is enabled and sample rate is set and globally trace search is disabled
                We expect the root span to have the appropriate tag
        """
        # setup our test app
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app(self.tracer)

        with self.override_global_config(dict(analytics_enabled=False)):
            with self.override_config(
                    "bottle",
                    dict(analytics_enabled=True, analytics_sample_rate=0.5)):
                resp = self.app.get("/hi/dougie")
                assert resp.status_int == 200
                assert compat.to_unicode(resp.body) == u"hi dougie"

        root = self.get_root_span()
        root.assert_matches(
            name="bottle.request",
            metrics={
                ANALYTICS_SAMPLE_RATE_KEY: 0.5,
            },
        )

        for span in self.spans:
            if span == root:
                continue
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
Example #7
0
    def test_analytics_global_off_integration_default(self):
        """
        When making a request
            When an integration trace search is not set and sample rate is set and globally trace search is disabled
                We expect the root span to not include tag
        """
        # setup our test app
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app(self.tracer)

        with self.override_global_config(dict(analytics_enabled=False)):
            resp = self.app.get("/hi/dougie")
            assert resp.status_int == 200
            assert compat.to_unicode(resp.body) == u"hi dougie"

        root = self.get_root_span()
        self.assertIsNone(root.get_metric(ANALYTICS_SAMPLE_RATE_KEY))

        for span in self.spans:
            if span == root:
                continue
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
Example #8
0
 def test_to_unicode_bytearray_double_decode(self):
     #  Calling `compat.to_unicode` with an already decoded `bytearray`
     # This represents the double-decode issue, which can cause a `UnicodeEncodeError`
     #   `bytearray('\xc3\xbf').decode('utf-8').decode('utf-8')`
     res = to_unicode(bytearray(b"\xc3\xbf").decode("utf-8"))
     assert type(res) == unicode
     assert res == u"ÿ"
Example #9
0
    def test_distributed_tracing_disabled_via_env_var(self):
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app_distributed(self.tracer)

        # make a request
        headers = {"x-datadog-trace-id": "123", "x-datadog-parent-id": "456"}
        resp = self.app.get("/hi/dougie", headers=headers)
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u"hi dougie"

        # validate it's traced
        spans = self.pop_spans()
        assert len(spans) == 1
        s = spans[0]
        assert s.name == "bottle.request"
        assert s.service == "bottle-app"
        assert s.resource == "GET /hi/<name>"
        assert_span_http_status_code(s, 200)
        assert s.get_tag("http.method") == "GET"
        # check distributed headers
        assert 123 != s.trace_id
        assert 456 != s.parent_id
Example #10
0
def test_redis_legacy():
    # ensure the old interface isn't broken, but doesn't trace
    tracer = DummyTracer()
    TracedRedisCache = get_traced_redis(tracer, "foo")
    r = TracedRedisCache(port=REDIS_CONFIG["port"])
    r.set("a", "b")
    got = r.get("a")
    assert compat.to_unicode(got) == "b"
    assert not tracer.pop()
Example #11
0
    def func(response):
        try:
            # pull out response code from gRPC response to use both for `grpc.status.code`
            # tag and the error type tag if the response is an exception
            response_code = response.code()
            # cast code to unicode for tags
            status_code = to_unicode(response_code)
            span._set_str_tag(constants.GRPC_STATUS_CODE_KEY, status_code)

            if response_code != grpc.StatusCode.OK:
                _handle_error(span, response, status_code)
        finally:
            span.finish()
Example #12
0
    def test_render(self):
        # render
        t = Template("Hello ${name}!")
        self.assertEqual(t.render(name="mako"), "Hello mako!")

        spans = self.pop_spans()
        self.assertEqual(len(spans), 1)

        assert_is_measured(spans[0])
        self.assertEqual(spans[0].service, "mako")
        self.assertEqual(spans[0].span_type, "template")
        self.assertEqual(spans[0].get_tag("mako.template_name"),
                         DEFAULT_TEMPLATE_NAME)
        self.assertEqual(spans[0].name, "mako.template.render")
        self.assertEqual(spans[0].resource, DEFAULT_TEMPLATE_NAME)

        # render_unicode
        t = Template("Hello ${name}!")
        self.assertEqual(t.render_unicode(name="mako"),
                         to_unicode("Hello mako!"))
        spans = self.pop_spans()
        self.assertEqual(len(spans), 1)
        assert_is_measured(spans[0])
        self.assertEqual(spans[0].service, "mako")
        self.assertEqual(spans[0].span_type, "template")
        self.assertEqual(spans[0].get_tag("mako.template_name"),
                         DEFAULT_TEMPLATE_NAME)
        self.assertEqual(spans[0].name, "mako.template.render_unicode")
        self.assertEqual(spans[0].resource, DEFAULT_TEMPLATE_NAME)

        # render_context
        t = Template("Hello ${name}!")
        buf = StringIO()
        c = Context(buf, name="mako")
        t.render_context(c)
        self.assertEqual(buf.getvalue(), "Hello mako!")
        spans = self.pop_spans()
        self.assertEqual(len(spans), 1)
        assert_is_measured(spans[0])
        self.assertEqual(spans[0].service, "mako")
        self.assertEqual(spans[0].span_type, "template")
        self.assertEqual(spans[0].get_tag("mako.template_name"),
                         DEFAULT_TEMPLATE_NAME)
        self.assertEqual(spans[0].name, "mako.template.render_context")
        self.assertEqual(spans[0].resource, DEFAULT_TEMPLATE_NAME)
Example #13
0
    def test_200(self):
        # setup our test app
        @self.app.route("/hi/<name>")
        def hi(name):
            return "hi %s" % name

        self._trace_app(self.tracer)

        # make a request
        resp = self.app.get("/hi/dougie")
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u"hi dougie"
        # validate it's traced
        spans = self.pop_spans()
        assert len(spans) == 1
        s = spans[0]
        assert s.name == "bottle.request"
        assert s.service == "bottle-app"
        assert s.resource == "GET /hi/<name>"
        assert_span_http_status_code(s, 200)
        assert s.get_tag("http.method") == "GET"
Example #14
0
    def test_json(self):
        res = self.app.get("/json", status=200)
        parsed = json.loads(compat.to_unicode(res.body))
        assert parsed == {"a": 1}

        spans = self.pop_spans()
        assert len(spans) == 2
        spans_by_name = {s.name: s for s in spans}
        s = spans_by_name["pyramid.request"]
        assert_is_measured(s)
        assert s.service == "foobar"
        assert s.resource == "GET json"
        assert s.error == 0
        assert s.span_type == "web"
        assert s.get_tag("http.method") == "GET"
        assert_span_http_status_code(s, 200)
        assert s.get_tag(http.URL) == "http://localhost/json"
        assert s.get_tag("pyramid.route.name") == "json"

        s = spans_by_name["pyramid.render"]
        assert s.service == "foobar"
        assert s.error == 0
        assert s.span_type == "template"
Example #15
0
 def test_to_unicode_non_string(self):
     #  Calling `compat.to_unicode` on non-string types
     assert to_unicode(1) == u"1"
     assert to_unicode(True) == u"True"
     assert to_unicode(None) == u"None"
     assert to_unicode(dict(key="value")) == u"{'key': 'value'}"
Example #16
0
 def test_to_unicode_unicode_string(self):
     # Calling `compat.to_unicode` on a unicode string
     res = to_unicode(u"ÿ")
     assert type(res) == unicode
     assert res == u"ÿ"
Example #17
0
 def test_to_unicode_bytearray(self):
     # Calling `compat.to_unicode` with a `bytearray` containing unicode
     res = to_unicode(bytearray(b"\xc3\xbf"))
     assert type(res) == unicode
     assert res == u"ÿ"
Example #18
0
def snapshot_context(token, ignores=None, tracer=None, async_mode=True):
    ignores = ignores or []
    if not tracer:
        tracer = ddtrace.tracer

    parsed = parse.urlparse(tracer.writer.agent_url)
    conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
    try:
        # clear queue in case traces have been generated before test case is
        # itself run
        try:
            tracer.writer.flush_queue()
        except Exception as e:
            pytest.fail("Could not flush the queue before test case: %s" %
                        str(e),
                        pytrace=True)

        if async_mode:
            # Patch the tracer writer to include the test token header for all requests.
            tracer.writer._headers["X-Datadog-Test-Token"] = token
        else:
            # Signal the start of this test case to the test agent.
            try:
                conn.request("GET", "/test/start?token=%s" % token)
            except Exception as e:
                pytest.fail("Could not connect to test agent: %s" % str(e),
                            pytrace=False)
            else:
                r = conn.getresponse()
                if r.status != 200:
                    # The test agent returns nice error messages we can forward to the user.
                    raise SnapshotFailed(r.read())

        # Return context to the caller
        try:
            yield
        finally:
            # Force a flush so all traces are submitted.
            tracer.writer.flush_queue()
            if async_mode:
                del tracer.writer._headers["X-Datadog-Test-Token"]

        # Query for the results of the test.
        conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
        conn.request(
            "GET",
            "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
        r = conn.getresponse()
        if r.status != 200:
            raise SnapshotFailed(r.read())
    except SnapshotFailed as e:
        # Fail the test if a failure has occurred and print out the
        # message we got from the test agent.
        pytest.fail(to_unicode(e.args[0]), pytrace=False)
    except Exception as e:
        # Even though it's unlikely any traces have been sent, make the
        # final request to the test agent so that the test case is finished.
        conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
        conn.request(
            "GET",
            "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
        conn.getresponse()
        pytest.fail("Unexpected test failure during snapshot test: %s" %
                    str(e),
                    pytrace=True)
    finally:
        conn.close()
Example #19
0
 def test_to_unicode_unicode_encoded(self):
     # Calling `compat.to_unicode` on a unicode encoded string
     res = to_unicode(b"\xc3\xbf")
     assert type(res) == unicode
     assert res == u"ÿ"
Example #20
0
 def test_to_unicode_string(self):
     # Calling `compat.to_unicode` on a non-unicode string
     res = to_unicode(b"test")
     assert type(res) == unicode
     assert res == "test"
Example #21
0
def snapshot_context(token, ignores=None, tracer=None, async_mode=True, variants=None):
    # Use variant that applies to update test token. One must apply. If none
    # apply, the test should have been marked as skipped.
    if variants:
        applicable_variant_ids = [k for (k, v) in variants.items() if v]
        assert len(applicable_variant_ids) == 1
        variant_id = applicable_variant_ids[0]
        token = "{}_{}".format(token, variant_id) if variant_id else token

    ignores = ignores or []
    if not tracer:
        tracer = ddtrace.tracer

    parsed = parse.urlparse(tracer.writer.agent_url)
    conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
    try:
        # clear queue in case traces have been generated before test case is
        # itself run
        try:
            tracer.writer.flush_queue()
        except Exception as e:
            pytest.fail("Could not flush the queue before test case: %s" % str(e), pytrace=True)

        if async_mode:
            # Patch the tracer writer to include the test token header for all requests.
            tracer.writer._headers["X-Datadog-Test-Token"] = token

            # Also add a header to the environment for subprocesses test cases that might use snapshotting.
            existing_headers = parse_tags_str(os.environ.get("_DD_TRACE_WRITER_ADDITIONAL_HEADERS", ""))
            existing_headers.update({"X-Datadog-Test-Token": token})
            os.environ["_DD_TRACE_WRITER_ADDITIONAL_HEADERS"] = ",".join(
                ["%s:%s" % (k, v) for k, v in existing_headers.items()]
            )
        else:
            # Signal the start of this test case to the test agent.
            try:
                conn.request("GET", "/test/start?token=%s" % token)
            except Exception as e:
                pytest.fail("Could not connect to test agent: %s" % str(e), pytrace=False)
            else:
                r = conn.getresponse()
                if r.status != 200:
                    # The test agent returns nice error messages we can forward to the user.
                    raise SnapshotFailed(r.read())

        try:
            yield SnapshotTest(
                tracer=tracer,
                token=token,
            )
        finally:
            # Force a flush so all traces are submitted.
            tracer.writer.flush_queue()
            if async_mode:
                del tracer.writer._headers["X-Datadog-Test-Token"]
                del os.environ["_DD_TRACE_WRITER_ADDITIONAL_HEADERS"]

        # Query for the results of the test.
        conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
        conn.request("GET", "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
        r = conn.getresponse()
        if r.status != 200:
            raise SnapshotFailed(r.read())
    except SnapshotFailed as e:
        # Fail the test if a failure has occurred and print out the
        # message we got from the test agent.
        pytest.fail(to_unicode(e.args[0]), pytrace=False)
    except Exception as e:
        # Even though it's unlikely any traces have been sent, make the
        # final request to the test agent so that the test case is finished.
        conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
        conn.request("GET", "/test/snapshot?ignores=%s&token=%s" % (",".join(ignores), token))
        conn.getresponse()
        pytest.fail("Unexpected test failure during snapshot test: %s" % str(e), pytrace=True)
    finally:
        conn.close()
Example #22
0
    def wrapper(wrapped, instance, args, kwargs):
        if len(args) > 1:
            self = args[0]
            clsname = self.__class__.__name__
        else:
            clsname = ""

        if include_tracer:
            tracer = Tracer()
        else:
            tracer = ddtrace.tracer

        module = inspect.getmodule(wrapped)

        # Use the fully qualified function name as a unique test token to
        # identify the snapshot.
        token = "{}{}{}.{}".format(module.__name__, "." if clsname else "",
                                   clsname, wrapped.__name__)

        # Use variant that applies to update test token. One must apply. If none
        # apply, the test should have been marked as skipped.
        if variants:
            applicable_variant_ids = [k for (k, v) in variants.items() if v]
            assert len(applicable_variant_ids) == 1
            variant_id = applicable_variant_ids[0]
            token = "{}_{}".format(token, variant_id) if variant_id else token

        parsed = parse.urlparse(tracer.writer.agent_url)
        conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
        try:
            # clear queue in case traces have been generated before test case is
            # itself run
            try:
                tracer.writer.flush_queue()
            except Exception as e:
                pytest.fail("Could not flush the queue before test case: %s" %
                            str(e),
                            pytrace=True)

            if async_mode:
                # Patch the tracer writer to include the test token header for all requests.
                tracer.writer._headers["X-Datadog-Test-Token"] = token
            else:
                # Signal the start of this test case to the test agent.
                try:
                    conn.request("GET", "/test/start?token=%s" % token)
                except Exception as e:
                    pytest.fail("Could not connect to test agent: %s" % str(e),
                                pytrace=False)
                else:
                    r = conn.getresponse()
                    if r.status != 200:
                        # The test agent returns nice error messages we can forward to the user.
                        raise SnapshotFailed(r.read())

            # Run the test.
            try:
                if include_tracer:
                    kwargs["tracer"] = tracer
                ret = wrapped(*args, **kwargs)
                # Force a flush so all traces are submitted.
                tracer.writer.flush_queue()
            finally:
                if async_mode:
                    del tracer.writer._headers["X-Datadog-Test-Token"]

            # Query for the results of the test.
            conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
            conn.request(
                "GET", "/test/snapshot?ignores=%s&token=%s" %
                (",".join(ignores), token))
            r = conn.getresponse()
            if r.status != 200:
                raise SnapshotFailed(r.read())
            return ret
        except SnapshotFailed as e:
            # Fail the test if a failure has occurred and print out the
            # message we got from the test agent.
            pytest.fail(to_unicode(e.args[0]), pytrace=False)
        except Exception as e:
            # Even though it's unlikely any traces have been sent, make the
            # final request to the test agent so that the test case is finished.
            conn = httplib.HTTPConnection(parsed.hostname, parsed.port)
            conn.request(
                "GET", "/test/snapshot?ignores=%s&token=%s" %
                (",".join(ignores), token))
            conn.getresponse()
            pytest.fail("Unexpected test failure during snapshot test: %s" %
                        str(e),
                        pytrace=True)
        finally:
            conn.close()