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)
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"ÿ"
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
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"
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))
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))
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"ÿ"
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
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()
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()
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)
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"
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"
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'}"
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"ÿ"
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"ÿ"
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()
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"ÿ"
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"
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()
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()