eventProcessingErrors.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import {useMemo} from 'react';
  2. import startCase from 'lodash/startCase';
  3. import moment from 'moment-timezone';
  4. import type {ErrorMessage} from 'sentry/components/events/interfaces/crashContent/exception/actionableItems';
  5. import {getErrorMessage} from 'sentry/components/events/interfaces/crashContent/exception/actionableItems';
  6. import {
  7. shouldErrorBeShown,
  8. useFetchProguardMappingFiles,
  9. } from 'sentry/components/events/interfaces/crashContent/exception/actionableItemsUtils';
  10. import {useActionableItems} from 'sentry/components/events/interfaces/crashContent/exception/useActionableItems';
  11. import KeyValueData from 'sentry/components/keyValueData';
  12. import {t} from 'sentry/locale';
  13. import type {Event} from 'sentry/types/event';
  14. import type {Project} from 'sentry/types/project';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
  17. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  18. type Props = {
  19. event: Event;
  20. isShare: boolean;
  21. project: Project;
  22. };
  23. const keyMapping = {
  24. image_uuid: 'Debug ID',
  25. image_name: 'File Name',
  26. image_path: 'File Path',
  27. };
  28. export default function EventErrorCard({
  29. title,
  30. data,
  31. }: {
  32. data: {key: string; subject: any; value: any}[];
  33. title: string;
  34. }) {
  35. const contentItems = data.map(datum => {
  36. return {item: datum};
  37. });
  38. return <KeyValueData.Card contentItems={contentItems} title={<div>{title}</div>} />;
  39. }
  40. function EventErrorDescription({error}: {error: ErrorMessage}) {
  41. const {title, data: errorData} = error;
  42. const cleanedData = useMemo(() => {
  43. const data = errorData || {};
  44. if (data.message === 'None') {
  45. // Python ensures a message string, but "None" doesn't make sense here
  46. delete data.message;
  47. }
  48. if (typeof data.image_path === 'string') {
  49. // Separate the image name for readability
  50. const separator = /^([a-z]:\\|\\\\)/i.test(data.image_path) ? '\\' : '/';
  51. const path = data.image_path.split(separator);
  52. data.image_name = path.splice(-1, 1)[0];
  53. data.image_path = path.length ? path.join(separator) + separator : '';
  54. }
  55. if (typeof data.server_time === 'string' && typeof data.sdk_time === 'string') {
  56. data.message = t(
  57. 'Adjusted timestamps by %s',
  58. moment
  59. .duration(moment.utc(data.server_time).diff(moment.utc(data.sdk_time)))
  60. .humanize()
  61. );
  62. }
  63. return Object.entries(data)
  64. .map(([key, value]) => ({
  65. key,
  66. value,
  67. subject: keyMapping[key] || startCase(key),
  68. }))
  69. .filter(d => {
  70. if (!d.value) {
  71. return true;
  72. }
  73. return !!d.value;
  74. });
  75. }, [errorData]);
  76. return <EventErrorCard title={title} data={cleanedData} />;
  77. }
  78. export function EventProcessingErrors({event, project, isShare}: Props) {
  79. const organization = useOrganization();
  80. const {data: actionableItems} = useActionableItems({
  81. eventId: event.id,
  82. orgSlug: organization.slug,
  83. projectSlug: project.slug,
  84. });
  85. const {proguardErrors} = useFetchProguardMappingFiles({
  86. event,
  87. project,
  88. isShare,
  89. });
  90. if (!actionableItems || actionableItems.errors.length === 0 || !proguardErrors) {
  91. return null;
  92. }
  93. const {_meta} = event;
  94. const errors = [...actionableItems.errors, ...proguardErrors]
  95. .filter(error => shouldErrorBeShown(error, event))
  96. .flatMap((error, errorIdx) =>
  97. getErrorMessage(error, _meta?.errors?.[errorIdx]).map(message => ({
  98. ...message,
  99. type: error.type,
  100. }))
  101. );
  102. if (!errors.length) {
  103. return null;
  104. }
  105. return (
  106. <InterimSection
  107. title={t('Event Processing Errors')}
  108. type={SectionKey.PROCESSING_ERROR}
  109. >
  110. <KeyValueData.Container>
  111. {errors.map((error, idx) => {
  112. return <EventErrorDescription key={idx} error={error} />;
  113. })}
  114. </KeyValueData.Container>
  115. </InterimSection>
  116. );
  117. }