test_organization_events_span_indexed.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import uuid
  2. from tests.snuba.api.endpoints.test_organization_events import OrganizationEventsEndpointTestBase
  3. class OrganizationEventsSpanIndexedEndpointTest(OrganizationEventsEndpointTestBase):
  4. """Test the indexed spans dataset.
  5. To run this locally you may need to set the ENABLE_SPANS_CONSUMER flag to True in Snuba.
  6. A way to do this is
  7. 1. run: `sentry devservices down snuba`
  8. 2. clone snuba locally
  9. 3. run: `export ENABLE_SPANS_CONSUMER=True`
  10. 4. run snuba
  11. At this point tests should work locally
  12. Once span ingestion is on by default this will no longer need to be done
  13. """
  14. def setUp(self):
  15. super().setUp()
  16. self.features = {
  17. "organizations:starfish-view": True,
  18. }
  19. def test_simple(self):
  20. self.store_spans(
  21. [
  22. self.create_span({"description": "foo"}, start_ts=self.ten_mins_ago),
  23. self.create_span({"description": "bar"}, start_ts=self.ten_mins_ago),
  24. ]
  25. )
  26. response = self.do_request(
  27. {
  28. "field": ["description", "count()"],
  29. "query": "",
  30. "orderby": "description",
  31. "project": self.project.id,
  32. "dataset": "spansIndexed",
  33. }
  34. )
  35. assert response.status_code == 200, response.content
  36. data = response.data["data"]
  37. meta = response.data["meta"]
  38. assert len(data) == 2
  39. assert data[0]["description"] == "bar"
  40. assert data[1]["description"] == "foo"
  41. assert meta["dataset"] == "spansIndexed"
  42. def test_sentry_tags_vs_tags(self):
  43. self.store_spans(
  44. [
  45. self.create_span(
  46. {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
  47. ),
  48. ]
  49. )
  50. response = self.do_request(
  51. {
  52. "field": ["transaction.method", "count()"],
  53. "query": "",
  54. "orderby": "count()",
  55. "project": self.project.id,
  56. "dataset": "spansIndexed",
  57. }
  58. )
  59. assert response.status_code == 200, response.content
  60. data = response.data["data"]
  61. meta = response.data["meta"]
  62. assert len(data) == 1
  63. assert data[0]["transaction.method"] == "foo"
  64. assert meta["dataset"] == "spansIndexed"
  65. def test_sentry_tags_syntax(self):
  66. self.store_spans(
  67. [
  68. self.create_span(
  69. {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
  70. ),
  71. ]
  72. )
  73. response = self.do_request(
  74. {
  75. "field": ["sentry_tags[transaction.method]", "count()"],
  76. "query": "",
  77. "orderby": "count()",
  78. "project": self.project.id,
  79. "dataset": "spansIndexed",
  80. }
  81. )
  82. assert response.status_code == 200, response.content
  83. data = response.data["data"]
  84. meta = response.data["meta"]
  85. assert len(data) == 1
  86. assert data[0]["sentry_tags[transaction.method]"] == "foo"
  87. assert meta["dataset"] == "spansIndexed"
  88. def test_module_alias(self):
  89. # Delegates `span.module` to `sentry_tags[category]`. Maps `"db.redis"` spans to the `"cache"` module
  90. self.store_spans(
  91. [
  92. self.create_span(
  93. {
  94. "op": "db.redis",
  95. "description": "EXEC *",
  96. "sentry_tags": {
  97. "description": "EXEC *",
  98. "category": "db",
  99. "op": "db.redis",
  100. "transaction": "/app/index",
  101. },
  102. },
  103. start_ts=self.ten_mins_ago,
  104. ),
  105. ]
  106. )
  107. response = self.do_request(
  108. {
  109. "field": ["span.module", "span.description"],
  110. "query": "span.module:cache",
  111. "project": self.project.id,
  112. "dataset": "spansIndexed",
  113. }
  114. )
  115. assert response.status_code == 200, response.content
  116. data = response.data["data"]
  117. meta = response.data["meta"]
  118. assert len(data) == 1
  119. assert data[0]["span.module"] == "cache"
  120. assert data[0]["span.description"] == "EXEC *"
  121. assert meta["dataset"] == "spansIndexed"
  122. def test_device_class_filter_unknown(self):
  123. self.store_spans(
  124. [
  125. self.create_span({"sentry_tags": {"device.class": ""}}, start_ts=self.ten_mins_ago),
  126. ]
  127. )
  128. response = self.do_request(
  129. {
  130. "field": ["device.class", "count()"],
  131. "query": "device.class:Unknown",
  132. "orderby": "count()",
  133. "project": self.project.id,
  134. "dataset": "spansIndexed",
  135. }
  136. )
  137. assert response.status_code == 200, response.content
  138. data = response.data["data"]
  139. meta = response.data["meta"]
  140. assert len(data) == 1
  141. assert data[0]["device.class"] == "Unknown"
  142. assert meta["dataset"] == "spansIndexed"
  143. def test_network_span(self):
  144. self.store_spans(
  145. [
  146. self.create_span(
  147. {
  148. "sentry_tags": {
  149. "action": "GET",
  150. "category": "http",
  151. "description": "GET https://*.resource.com",
  152. "domain": "*.resource.com",
  153. "op": "http.client",
  154. "status_code": "200",
  155. "transaction": "/api/0/data/",
  156. "transaction.method": "GET",
  157. "transaction.op": "http.server",
  158. }
  159. },
  160. start_ts=self.ten_mins_ago,
  161. ),
  162. ]
  163. )
  164. response = self.do_request(
  165. {
  166. "field": ["span.op", "span.status_code"],
  167. "query": "span.module:http span.status_code:200",
  168. "project": self.project.id,
  169. "dataset": "spansIndexed",
  170. }
  171. )
  172. assert response.status_code == 200, response.content
  173. data = response.data["data"]
  174. meta = response.data["meta"]
  175. assert len(data) == 1
  176. assert data[0]["span.op"] == "http.client"
  177. assert data[0]["span.status_code"] == "200"
  178. assert meta["dataset"] == "spansIndexed"
  179. def test_inp_span(self):
  180. replay_id = uuid.uuid4().hex
  181. self.store_spans(
  182. [
  183. self.create_span(
  184. {
  185. "sentry_tags": {
  186. "replay_id": replay_id,
  187. "browser.name": "Chrome",
  188. "transaction": "/pageloads/",
  189. }
  190. },
  191. start_ts=self.ten_mins_ago,
  192. ),
  193. ]
  194. )
  195. response = self.do_request(
  196. {
  197. "field": ["replay.id", "browser.name", "origin.transaction", "count()"],
  198. "query": f"replay.id:{replay_id} AND browser.name:Chrome AND origin.transaction:/pageloads/",
  199. "orderby": "count()",
  200. "project": self.project.id,
  201. "dataset": "spansIndexed",
  202. }
  203. )
  204. assert response.status_code == 200, response.content
  205. data = response.data["data"]
  206. meta = response.data["meta"]
  207. assert len(data) == 1
  208. assert data[0]["replay.id"] == replay_id
  209. assert data[0]["browser.name"] == "Chrome"
  210. assert data[0]["origin.transaction"] == "/pageloads/"
  211. assert meta["dataset"] == "spansIndexed"
  212. def test_id_filtering(self):
  213. span = self.create_span({"description": "foo"}, start_ts=self.ten_mins_ago)
  214. self.store_span(span)
  215. response = self.do_request(
  216. {
  217. "field": ["description", "count()"],
  218. "query": f"id:{span['span_id']}",
  219. "orderby": "description",
  220. "project": self.project.id,
  221. "dataset": "spansIndexed",
  222. }
  223. )
  224. assert response.status_code == 200, response.content
  225. data = response.data["data"]
  226. meta = response.data["meta"]
  227. assert len(data) == 1
  228. assert data[0]["description"] == "foo"
  229. assert meta["dataset"] == "spansIndexed"
  230. response = self.do_request(
  231. {
  232. "field": ["description", "count()"],
  233. "query": f"transaction.id:{span['event_id']}",
  234. "orderby": "description",
  235. "project": self.project.id,
  236. "dataset": "spansIndexed",
  237. }
  238. )
  239. assert response.status_code == 200, response.content
  240. data = response.data["data"]
  241. meta = response.data["meta"]
  242. assert len(data) == 1
  243. assert data[0]["description"] == "foo"
  244. assert meta["dataset"] == "spansIndexed"
  245. def test_span_op_casing(self):
  246. self.store_spans(
  247. [
  248. self.create_span(
  249. {
  250. "sentry_tags": {
  251. "replay_id": "abc123",
  252. "browser.name": "Chrome",
  253. "transaction": "/pageloads/",
  254. "op": "this is a transaction",
  255. }
  256. },
  257. start_ts=self.ten_mins_ago,
  258. ),
  259. ]
  260. )
  261. response = self.do_request(
  262. {
  263. "field": ["span.op", "count()"],
  264. "query": 'span.op:"ThIs Is a TraNSActiON"',
  265. "orderby": "count()",
  266. "project": self.project.id,
  267. "dataset": "spansIndexed",
  268. }
  269. )
  270. assert response.status_code == 200, response.content
  271. data = response.data["data"]
  272. meta = response.data["meta"]
  273. assert len(data) == 1
  274. assert data[0]["span.op"] == "this is a transaction"
  275. assert meta["dataset"] == "spansIndexed"