index.tsx 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import {useCallback, useEffect} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import ContextDataSection from 'sentry/components/events/contexts/contextDataSection';
  4. import type {Event, EventContexts as EventContextValues} from 'sentry/types/event';
  5. import type {Group} from 'sentry/types/group';
  6. import useProjects from 'sentry/utils/useProjects';
  7. type Props = {
  8. event: Event;
  9. group?: Group;
  10. };
  11. interface UnknownContextValue {
  12. [key: string]: any;
  13. type: 'default';
  14. }
  15. /**
  16. * Catch-all for context values, known and unknown
  17. */
  18. export type ContextValue =
  19. | EventContextValues[keyof EventContextValues]
  20. | UnknownContextValue;
  21. export interface ContextItem {
  22. alias: string;
  23. type: string;
  24. value: ContextValue;
  25. }
  26. export function getOrderedContextItems(event): ContextItem[] {
  27. const {user, contexts} = event;
  28. const {data: customUserData, ...userContext} = user ?? {};
  29. const {feedback, response, ...otherContexts} = contexts ?? {};
  30. const orderedContext: [ContextItem['alias'], ContextValue][] = [
  31. ['response', response],
  32. ['feedback', feedback],
  33. ['user', {...userContext, ...customUserData}],
  34. ...Object.entries(otherContexts),
  35. ];
  36. // For these context aliases, use the alias as 'type' rather than 'value.type'
  37. const overrideTypesWithAliases = new Set([
  38. 'response',
  39. 'feedback',
  40. 'user',
  41. 'profile',
  42. 'replay',
  43. ]);
  44. const items = orderedContext
  45. .filter(([_k, ctxValue]) => {
  46. const contextKeys = Object.keys(ctxValue ?? {});
  47. const isInvalid =
  48. // Empty context
  49. contextKeys.length === 0 ||
  50. // Empty aside from 'type' key
  51. (contextKeys.length === 1 && contextKeys[0] === 'type');
  52. return !isInvalid;
  53. })
  54. .map<ContextItem>(([alias, ctx]) => ({
  55. alias: alias,
  56. type: overrideTypesWithAliases.has(alias) ? alias : ctx?.type,
  57. value: ctx,
  58. }));
  59. return items;
  60. }
  61. export function EventContexts({event, group}: Props) {
  62. const {projects} = useProjects();
  63. const project = projects.find(p => p.id === event.projectID);
  64. const {contexts, sdk} = event;
  65. const usingOtel = useCallback(() => contexts.otel !== undefined, [contexts.otel]);
  66. useEffect(() => {
  67. const span = Sentry.getActiveSpan();
  68. if (usingOtel() && span) {
  69. const rootSpan = Sentry.getRootSpan(span);
  70. rootSpan.setAttribute('otel_event', true);
  71. rootSpan.setAttribute('otel_sdk', sdk?.name);
  72. rootSpan.setAttribute('otel_sdk_version', sdk?.version);
  73. }
  74. }, [usingOtel, sdk]);
  75. return <ContextDataSection event={event} group={group} project={project} />;
  76. }