|
| 1 | +import { captureEvent, getClient } from '@sentry/node'; |
| 2 | +import { Attachment, Event } from '@sentry/types'; |
| 3 | +import { logger, parseEnvelope } from '@sentry/utils'; |
| 4 | +import * as electron from 'electron'; |
| 5 | + |
| 6 | +import { eventFromEnvelope } from '../common/envelope'; |
| 7 | +import { getMagicMessage, isMagicMessage } from '../common/ipc'; |
| 8 | +import { mergeEvents } from './merge'; |
| 9 | + |
| 10 | +function log(message: string): void { |
| 11 | + logger.log(`[Utility Process] ${message}`); |
| 12 | +} |
| 13 | + |
| 14 | +/** |
| 15 | + * We wrap `electron.utilityProcess.fork` so we can pass a messageport to any SDK running in the utility process |
| 16 | + */ |
| 17 | +export function configureUtilityProcessIPC(): void { |
| 18 | + if (!electron.utilityProcess?.fork) { |
| 19 | + return; |
| 20 | + } |
| 21 | + |
| 22 | + // eslint-disable-next-line @typescript-eslint/unbound-method |
| 23 | + electron.utilityProcess.fork = new Proxy(electron.utilityProcess.fork, { |
| 24 | + apply: (target, thisArg, args: Parameters<typeof electron.utilityProcess.fork>) => { |
| 25 | + // Call the underlying function to get the child process |
| 26 | + const child: electron.UtilityProcess = target.apply(thisArg, args); |
| 27 | + |
| 28 | + function getProcessName(): string { |
| 29 | + const [, , options] = args; |
| 30 | + return options?.serviceName || `pid:${child.pid}`; |
| 31 | + } |
| 32 | + |
| 33 | + // We don't send any messages unless we've heard from the child SDK. At that point we know it's ready to receive |
| 34 | + // and will also filter out any messages we send so users don't see them |
| 35 | + child.on('message', (msg: unknown) => { |
| 36 | + if (isMagicMessage(msg)) { |
| 37 | + log(`SDK started in utility process '${getProcessName()}'`); |
| 38 | + |
| 39 | + const { port1, port2 } = new electron.MessageChannelMain(); |
| 40 | + |
| 41 | + port2.on('message', (msg) => { |
| 42 | + if (msg.data instanceof Uint8Array || typeof msg.data === 'string') { |
| 43 | + handleEnvelopeFromUtility(msg.data); |
| 44 | + } |
| 45 | + }); |
| 46 | + port2.start(); |
| 47 | + |
| 48 | + // Send one side of the message port to the child SDK |
| 49 | + child.postMessage(getMagicMessage(), [port1]); |
| 50 | + } |
| 51 | + }); |
| 52 | + |
| 53 | + // We proxy child.on so we can filter messages from the child SDK and ensure that users do not see them |
| 54 | + // eslint-disable-next-line @typescript-eslint/unbound-method |
| 55 | + child.on = new Proxy(child.on, { |
| 56 | + apply: (target, thisArg, [event, listener]) => { |
| 57 | + if (event === 'message') { |
| 58 | + return target.apply(thisArg, [ |
| 59 | + 'message', |
| 60 | + (msg: unknown) => { |
| 61 | + if (isMagicMessage(msg)) { |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + return listener(msg); |
| 66 | + }, |
| 67 | + ]); |
| 68 | + } |
| 69 | + |
| 70 | + return target.apply(thisArg, [event, listener]); |
| 71 | + }, |
| 72 | + }); |
| 73 | + |
| 74 | + return child; |
| 75 | + }, |
| 76 | + }); |
| 77 | +} |
| 78 | + |
| 79 | +function handleEnvelopeFromUtility(env: Uint8Array | string): void { |
| 80 | + const envelope = parseEnvelope(env); |
| 81 | + |
| 82 | + const eventAndAttachments = eventFromEnvelope(envelope); |
| 83 | + if (eventAndAttachments) { |
| 84 | + const [event, attachments] = eventAndAttachments; |
| 85 | + |
| 86 | + captureEventFromUtility(event, attachments); |
| 87 | + } else { |
| 88 | + // Pass other types of envelope straight to the transport |
| 89 | + void getClient()?.getTransport()?.send(envelope); |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +function captureEventFromUtility(event: Event, attachments: Attachment[]): void { |
| 94 | + // Remove the environment as it defaults to 'production' and overwrites the main process environment |
| 95 | + delete event.environment; |
| 96 | + delete event.release; |
| 97 | + |
| 98 | + // Remove the SDK info as we want the Electron SDK to be the one reporting the event |
| 99 | + delete event.sdk?.name; |
| 100 | + delete event.sdk?.version; |
| 101 | + delete event.sdk?.packages; |
| 102 | + |
| 103 | + captureEvent(mergeEvents(event, { tags: { 'event.process': 'utility' } }), { attachments }); |
| 104 | +} |
0 commit comments