|
1 | | -import type { ClientOptions, Event, EventHint } from '@sentry/types'; |
2 | | -import { dateTimestampInSeconds, normalize, resolvedSyncPromise, truncate, uuid4 } from '@sentry/utils'; |
| 1 | +import type { ClientOptions, Event, EventHint, StackParser } from '@sentry/types'; |
| 2 | +import { dateTimestampInSeconds, GLOBAL_OBJ, normalize, resolvedSyncPromise, truncate, uuid4 } from '@sentry/utils'; |
3 | 3 |
|
4 | 4 | import { Scope } from '../scope'; |
5 | 5 |
|
@@ -36,6 +36,7 @@ export function prepareEvent( |
36 | 36 |
|
37 | 37 | applyClientOptions(prepared, options); |
38 | 38 | applyIntegrationsMetadata(prepared, integrations); |
| 39 | + applyDebugMetadata(prepared, options.stackParser); |
39 | 40 |
|
40 | 41 | // If we have scope given to us, use it as the base for further modifications. |
41 | 42 | // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. |
@@ -112,6 +113,59 @@ function applyClientOptions(event: Event, options: ClientOptions): void { |
112 | 113 | } |
113 | 114 | } |
114 | 115 |
|
| 116 | +/** |
| 117 | + * Applies debug metadata images to the event in order to apply source maps by looking up their debug ID. |
| 118 | + */ |
| 119 | +export function applyDebugMetadata(event: Event, stackParser: StackParser): void { |
| 120 | + const debugIdMap = GLOBAL_OBJ._sentryDebugIds; |
| 121 | + |
| 122 | + if (!debugIdMap) { |
| 123 | + return; |
| 124 | + } |
| 125 | + |
| 126 | + // Build a map of abs_path -> debug_id |
| 127 | + const absPathDebugIdMap = Object.keys(debugIdMap).reduce<Record<string, string>>((acc, debugIdStackTrace) => { |
| 128 | + const parsedStack = stackParser(debugIdStackTrace); |
| 129 | + for (const stackFrame of parsedStack) { |
| 130 | + if (stackFrame.abs_path) { |
| 131 | + acc[stackFrame.abs_path] = debugIdMap[debugIdStackTrace]; |
| 132 | + break; |
| 133 | + } |
| 134 | + } |
| 135 | + return acc; |
| 136 | + }, {}); |
| 137 | + |
| 138 | + // Get a Set of abs_paths in the stack trace |
| 139 | + const errorAbsPaths = new Set<string>(); |
| 140 | + try { |
| 141 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
| 142 | + event!.exception!.values!.forEach(exception => { |
| 143 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
| 144 | + exception.stacktrace!.frames!.forEach(frame => { |
| 145 | + if (frame.abs_path) { |
| 146 | + errorAbsPaths.add(frame.abs_path); |
| 147 | + } |
| 148 | + }); |
| 149 | + }); |
| 150 | + } catch (e) { |
| 151 | + // To save bundle size we're just try catching here instead of checking for the existence of all the different objects. |
| 152 | + } |
| 153 | + |
| 154 | + // Fill debug_meta information |
| 155 | + event.debug_meta = event.debug_meta || {}; |
| 156 | + event.debug_meta.images = event.debug_meta.images || []; |
| 157 | + const images = event.debug_meta.images; |
| 158 | + errorAbsPaths.forEach(absPath => { |
| 159 | + if (absPathDebugIdMap[absPath]) { |
| 160 | + images.push({ |
| 161 | + type: 'sourcemap', |
| 162 | + code_file: absPath, |
| 163 | + debug_id: absPathDebugIdMap[absPath], |
| 164 | + }); |
| 165 | + } |
| 166 | + }); |
| 167 | +} |
| 168 | + |
115 | 169 | /** |
116 | 170 | * This function adds all used integrations to the SDK info in the event. |
117 | 171 | * @param event The event that will be filled with all integrations. |
|
0 commit comments