message.tsx 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import styled from '@emotion/styled';
  2. import {renderLinksInText} from 'sentry/components/events/interfaces/crashContent/exception/utils';
  3. import KeyValueList from 'sentry/components/events/interfaces/keyValueList';
  4. import {AnnotatedText} from 'sentry/components/events/meta/annotatedText';
  5. import {t} from 'sentry/locale';
  6. import type {Event} from 'sentry/types/event';
  7. import {EntryType} from 'sentry/types/event';
  8. import {isEmptyObject} from 'sentry/utils/object/isEmptyObject';
  9. import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
  10. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  11. type Props = {
  12. data: {
  13. formatted: string | null;
  14. params?: Record<string, any> | any[] | null;
  15. };
  16. event: Event;
  17. };
  18. function renderParams(params: Props['data']['params'], meta: any) {
  19. if (!params || isEmptyObject(params)) {
  20. return null;
  21. }
  22. // NB: Always render params, regardless of whether they appear in the
  23. // formatted string due to structured logging frameworks, like Serilog. They
  24. // only format some parameters into the formatted string, but we want to
  25. // display all of them.
  26. if (Array.isArray(params)) {
  27. const arrayData = params.map((value, i) => {
  28. const key = `#${i}`;
  29. return {
  30. key,
  31. value,
  32. subject: key,
  33. meta: meta?.data?.params?.[i]?.[''],
  34. };
  35. });
  36. return <KeyValueList data={arrayData} shouldSort={false} isContextData />;
  37. }
  38. const objectData = Object.entries(params).map(([key, value]) => ({
  39. key,
  40. value,
  41. subject: key,
  42. meta: meta?.data?.params?.[key]?.[''],
  43. }));
  44. return <KeyValueList data={objectData} shouldSort={false} isContextData />;
  45. }
  46. export function Message({data, event}: Props) {
  47. const entryIndex = event.entries.findIndex(entry => entry.type === EntryType.MESSAGE);
  48. const meta = event?._meta?.entries?.[entryIndex] ?? {};
  49. const messageData = data.formatted
  50. ? renderLinksInText({exceptionText: data.formatted})
  51. : null;
  52. return (
  53. <InterimSection title={t('Message')} type={SectionKey.MESSAGE}>
  54. <PlainPre>
  55. <AnnotatedText value={messageData} meta={meta?.data?.formatted?.['']} />
  56. </PlainPre>
  57. {renderParams(data.params, meta)}
  58. </InterimSection>
  59. );
  60. }
  61. const PlainPre = styled('pre')`
  62. background-color: inherit;
  63. padding: 0;
  64. border: 0;
  65. margin-bottom: 0;
  66. white-space: pre-wrap;
  67. word-break: break-all;
  68. `;