samplesTable.tsx 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. import {useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import shuffle from 'lodash/shuffle';
  4. import Badge from 'sentry/components/badge';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {PanelTableHeader} from 'sentry/components/panels/panelTable';
  7. import {space} from 'sentry/styles/space';
  8. import {MRI} from 'sentry/types';
  9. import EventView from 'sentry/utils/discover/eventView';
  10. import {useMetricsSpans} from 'sentry/utils/metrics/useMetricsCodeLocations';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {
  14. generateProfileLink,
  15. generateReplayLink,
  16. generateTraceLink,
  17. generateTransactionLink,
  18. } from 'sentry/views/performance/transactionSummary/utils';
  19. import TransactionsTable from '../../components/discover/transactionsTable';
  20. import {MetricRange} from '../../utils/metrics/index';
  21. export type SamplesTableProps = MetricRange & {
  22. mri: MRI;
  23. };
  24. // TODO(ddm): This is a placeholder component. Shown data is bogus.
  25. export function TraceTable({mri, ...range}: SamplesTableProps) {
  26. const location = useLocation();
  27. const organization = useOrganization();
  28. const eventView = EventView.fromLocation(location);
  29. const {isLoading} = useMetricsSpans(mri, range);
  30. const tableData: any = {
  31. data: [
  32. {
  33. 'profile.id': null,
  34. timestamp: '2023-10-30T07:04:57+00:00',
  35. 'spans.ui': 4817.500354,
  36. 'span_ops_breakdown.relative': '',
  37. replayId: '2a3ef28213b2f408ca2828b6a27149c2b',
  38. 'transaction.duration': 11893,
  39. 'spans.db': null,
  40. trace: '9fe2707d6a4dc45efa75439764f963188a',
  41. 'spans.http': 8814.999819,
  42. 'spans.resource': 2655.700206,
  43. id: 'ddb209c68a54846b19e3422309afb26ca3',
  44. 'user.display': 'alexandra.cota@sentry.io',
  45. 'spans.browser': 637.500286,
  46. 'project.name': 'javascript',
  47. },
  48. {
  49. 'profile.id': '2a3ef28213b2f408ca2828b6a27149c2b',
  50. timestamp: '2023-10-29T22:17:08+00:00',
  51. 'spans.ui': 19985.199929,
  52. 'span_ops_breakdown.relative': '',
  53. replayId: '',
  54. 'transaction.duration': 11888,
  55. 'spans.db': null,
  56. trace: '343267d658554f44be28a42e49f460f3',
  57. 'spans.http': 9248.399973,
  58. 'spans.resource': 1555.999995,
  59. id: 'e77387c5a7e043312aaaf7f406c6049a2',
  60. 'user.display': 'matej.minar@sentry.io',
  61. 'spans.browser': 534.999848,
  62. 'project.name': 'javascript',
  63. },
  64. {
  65. 'profile.id': null,
  66. timestamp: '2023-10-30T01:45:19+00:00',
  67. 'spans.ui': 5709.600449,
  68. 'span_ops_breakdown.relative': '',
  69. replayId: '',
  70. 'transaction.duration': 11863,
  71. 'spans.db': null,
  72. trace: '9ad958c8af21d4c0db58bb5542c41c4d13',
  73. 'spans.http': 9214.499951,
  74. 'spans.resource': 2082.90124,
  75. id: 'acc1854b13a04f84bd2f0fc5b803dd65',
  76. 'user.display': 'riccardo.busetti@sentry.io',
  77. 'spans.browser': 3389.699936,
  78. 'project.name': 'javascript',
  79. },
  80. {
  81. 'profile.id': '2a3ef28213b2f408ca2828b6a27149c2b',
  82. timestamp: '2023-10-29T16:59:16+00:00',
  83. 'spans.ui': 2638.000251,
  84. 'span_ops_breakdown.relative': '',
  85. replayId: '',
  86. 'transaction.duration': 11857,
  87. 'spans.db': null,
  88. trace: '548f0e7a4bd44a28aee52b890698440e',
  89. 'spans.http': 10524.60003,
  90. 'spans.resource': 222.597839,
  91. id: 'e491bd2be0734357ab9dcc690133a43b',
  92. 'user.display': 'ognjen.bostjancic@sentry.io',
  93. 'spans.browser': 9877.500056,
  94. 'project.name': 'javascript',
  95. },
  96. {
  97. 'profile.id': null,
  98. timestamp: '2023-10-30T11:39:40+00:00',
  99. 'spans.ui': 2214.000223,
  100. 'span_ops_breakdown.relative': '',
  101. replayId: '2a3ef28213b2f408ca2828b6a27149c2b',
  102. 'transaction.duration': 11852,
  103. 'spans.db': null,
  104. trace: 'eaad797b3c2c34b79bb705e5af2db688b',
  105. 'spans.http': 4308.000088,
  106. 'spans.resource': 16362.000228,
  107. id: 'd53b10ef8edc43023b92caf8d0a8a473d',
  108. 'user.display': 'arhur.knaus@sentry.io',
  109. 'spans.browser': 615.000009,
  110. 'project.name': 'javascript',
  111. },
  112. ],
  113. meta: {
  114. 'profile.id': 'string',
  115. timestamp: 'date',
  116. 'spans.ui': 'duration',
  117. 'span_ops_breakdown.relative': 'string',
  118. replayId: 'string',
  119. 'transaction.duration': 'duration',
  120. 'spans.db': 'duration',
  121. trace: 'string',
  122. 'spans.http': 'duration',
  123. 'spans.resource': 'duration',
  124. id: 'string',
  125. 'user.display': 'string',
  126. 'spans.browser': 'duration',
  127. 'project.name': 'string',
  128. units: {
  129. 'profile.id': null,
  130. timestamp: null,
  131. 'spans.ui': 'millisecond',
  132. 'span_ops_breakdown.relative': null,
  133. replayId: null,
  134. 'transaction.duration': 'millisecond',
  135. 'spans.db': 'millisecond',
  136. trace: null,
  137. 'spans.http': 'millisecond',
  138. 'spans.resource': 'millisecond',
  139. id: null,
  140. 'user.display': null,
  141. 'spans.browser': 'millisecond',
  142. 'project.name': null,
  143. },
  144. isMetricsData: false,
  145. tips: {
  146. query: null,
  147. columns: null,
  148. },
  149. datasetReason: 'unchanged',
  150. dataset: 'discover',
  151. },
  152. };
  153. const [rows] = useState(() => shuffle(tableData.data));
  154. const columnOrder: any = [
  155. {
  156. key: 'id',
  157. name: 'id',
  158. type: 'string',
  159. isSortable: false,
  160. column: {
  161. kind: 'field',
  162. field: 'id',
  163. },
  164. width: -1,
  165. },
  166. {
  167. key: 'user.display',
  168. name: 'user.display',
  169. type: 'string',
  170. isSortable: false,
  171. column: {
  172. kind: 'field',
  173. field: 'user.display',
  174. },
  175. width: -1,
  176. },
  177. {
  178. key: 'span_ops_breakdown.relative',
  179. name: 'span_ops_breakdown.relative',
  180. type: 'never',
  181. isSortable: false,
  182. column: {
  183. kind: 'field',
  184. field: 'span_ops_breakdown.relative',
  185. },
  186. width: -1,
  187. },
  188. {
  189. key: 'transaction.duration',
  190. name: 'transaction.duration',
  191. type: 'duration',
  192. isSortable: false,
  193. column: {
  194. kind: 'field',
  195. field: 'transaction.duration',
  196. },
  197. width: -1,
  198. },
  199. {
  200. key: 'trace',
  201. name: 'trace',
  202. type: 'string',
  203. isSortable: false,
  204. column: {
  205. kind: 'field',
  206. field: 'trace',
  207. },
  208. width: -1,
  209. },
  210. {
  211. key: 'timestamp',
  212. name: 'timestamp',
  213. type: 'date',
  214. isSortable: false,
  215. column: {
  216. kind: 'field',
  217. field: 'timestamp',
  218. },
  219. width: -1,
  220. },
  221. {
  222. key: 'replayId',
  223. name: 'replayId',
  224. type: 'string',
  225. isSortable: false,
  226. column: {
  227. kind: 'field',
  228. field: 'replayId',
  229. },
  230. width: -1,
  231. },
  232. {
  233. key: 'profile.id',
  234. name: 'profile.id',
  235. type: 'string',
  236. isSortable: false,
  237. column: {
  238. kind: 'field',
  239. field: 'profile.id',
  240. },
  241. width: -1,
  242. },
  243. {
  244. key: 'spans.browser',
  245. name: 'spans.browser',
  246. type: 'duration',
  247. isSortable: false,
  248. column: {
  249. kind: 'field',
  250. field: 'spans.browser',
  251. },
  252. width: -1,
  253. },
  254. {
  255. key: 'spans.db',
  256. name: 'spans.db',
  257. type: 'duration',
  258. isSortable: false,
  259. column: {
  260. kind: 'field',
  261. field: 'spans.db',
  262. },
  263. width: -1,
  264. },
  265. {
  266. key: 'spans.http',
  267. name: 'spans.http',
  268. type: 'duration',
  269. isSortable: false,
  270. column: {
  271. kind: 'field',
  272. field: 'spans.http',
  273. },
  274. width: -1,
  275. },
  276. {
  277. key: 'spans.resource',
  278. name: 'spans.resource',
  279. type: 'duration',
  280. isSortable: false,
  281. column: {
  282. kind: 'field',
  283. field: 'spans.resource',
  284. },
  285. width: -1,
  286. },
  287. {
  288. key: 'spans.ui',
  289. name: 'spans.ui',
  290. type: 'duration',
  291. isSortable: false,
  292. column: {
  293. kind: 'field',
  294. field: 'spans.ui',
  295. },
  296. width: -1,
  297. },
  298. ];
  299. if (isLoading) {
  300. return <LoadingIndicator />;
  301. }
  302. return (
  303. <TraceTableWrapper>
  304. <TitleOverlay>
  305. <StyledBadge type="alpha" color="white">
  306. Coming Soon
  307. </StyledBadge>
  308. <div>Sampled traces</div>
  309. </TitleOverlay>
  310. <TransactionsTable
  311. eventView={eventView}
  312. organization={organization}
  313. location={location}
  314. isLoading={false}
  315. tableData={{meta: tableData.meta, data: rows}}
  316. columnOrder={columnOrder}
  317. titles={[
  318. 'event id',
  319. 'user',
  320. 'operation duration',
  321. 'total duration',
  322. 'trace id',
  323. 'timestamp',
  324. 'replay',
  325. 'profile',
  326. ]}
  327. generateLink={{
  328. id: generateTransactionLink(''),
  329. trace: generateTraceLink(eventView.normalizeDateSelection(location)),
  330. replayId: generateReplayLink([]),
  331. 'profile.id': generateProfileLink(),
  332. }}
  333. useAggregateAlias
  334. />
  335. </TraceTableWrapper>
  336. );
  337. }
  338. const TitleOverlay = styled('span')`
  339. position: absolute;
  340. display: flex;
  341. gap: ${space(1)};
  342. align-items: center;
  343. z-index: 1;
  344. line-height: 1.1;
  345. height: 46px;
  346. max-width: 250px;
  347. padding: 0px 16px;
  348. background-color: ${p => p.theme.backgroundSecondary};
  349. color: ${p => p.theme.subText};
  350. font-size: 12px;
  351. font-weight: 600;
  352. text-transform: uppercase;
  353. user-select: none;
  354. border-top: 1px solid ${p => p.theme.border};
  355. border-bottom: 1px solid ${p => p.theme.border};
  356. border-left: 1px solid ${p => p.theme.border};
  357. border-top-left-radius: 4px;
  358. `;
  359. const StyledBadge = styled(Badge)`
  360. color: white !important;
  361. margin-top: -1px;
  362. `;
  363. const TraceTableWrapper = styled('div')`
  364. pointer-events: none;
  365. width: 100%;
  366. user-select: none;
  367. > div {
  368. width: 100%;
  369. }
  370. > div > div > div {
  371. filter: blur(3px);
  372. }
  373. > div > ${PanelTableHeader} {
  374. color: ${p => p.theme.backgroundSecondary};
  375. span {
  376. display: none;
  377. }
  378. }
  379. > span > div {
  380. pointer-events: none;
  381. }
  382. `;