index.tsx 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  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: Event): ContextItem[] {
  27. const {user, contexts} = event;
  28. const {data: customUserData, ...userContext} = user ?? {};
  29. // hide `flags` in the contexts section since we display this
  30. // info in the feature flag section below
  31. const {feedback, response, flags: _, ...otherContexts} = contexts ?? {};
  32. const orderedContext: [ContextItem['alias'], ContextValue][] = [
  33. ['response', response],
  34. ['feedback', feedback],
  35. ['user', {...userContext, ...(customUserData as any)}],
  36. ...Object.entries(otherContexts),
  37. ];
  38. // For these context aliases, use the alias as 'type' rather than 'value.type'
  39. const overrideTypesWithAliases = new Set([
  40. 'response',
  41. 'feedback',
  42. 'user',
  43. 'profile',
  44. 'replay',
  45. ]);
  46. const items = orderedContext
  47. .filter(([_k, ctxValue]) => {
  48. const contextKeys = Object.keys(ctxValue ?? {});
  49. const isInvalid =
  50. // Empty context
  51. contextKeys.length === 0 ||
  52. // Empty aside from 'type' key
  53. (contextKeys.length === 1 && contextKeys[0] === 'type');
  54. return !isInvalid;
  55. })
  56. .map<ContextItem>(([alias, ctx]) => ({
  57. alias: alias,
  58. type: overrideTypesWithAliases.has(alias) ? alias : ctx?.type,
  59. value: ctx,
  60. }));
  61. return items;
  62. }
  63. export function EventContexts({event, group}: Props) {
  64. const {projects} = useProjects();
  65. const project = projects.find(p => p.id === event.projectID);
  66. const {contexts, sdk} = event;
  67. const usingOtel = useCallback(() => contexts.otel !== undefined, [contexts.otel]);
  68. useEffect(() => {
  69. const span = Sentry.getActiveSpan();
  70. if (usingOtel() && span) {
  71. const rootSpan = Sentry.getRootSpan(span);
  72. rootSpan.setAttribute('otel_event', true);
  73. rootSpan.setAttribute('otel_sdk', sdk?.name);
  74. rootSpan.setAttribute('otel_sdk_version', sdk?.version);
  75. }
  76. }, [usingOtel, sdk]);
  77. return <ContextDataSection event={event} group={group} project={project} />;
  78. }