-
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support Electron utility process (#991)
- Loading branch information
Showing
18 changed files
with
600 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ stats.json | |
/renderer | ||
/main | ||
/common | ||
/utility | ||
/index.* | ||
/integrations.* | ||
/ipc.* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,5 +6,6 @@ | |
!/main/**/* | ||
!/renderer/**/* | ||
!/common/**/* | ||
!/utility/**/* | ||
!/index.* | ||
!/integrations.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Attachment, AttachmentItem, Envelope, Event, EventItem, Profile } from '@sentry/types'; | ||
import { forEachEnvelopeItem } from '@sentry/utils'; | ||
|
||
/** Pulls an event and additional envelope items out of an envelope. Returns undefined if there was no event */ | ||
export function eventFromEnvelope(envelope: Envelope): [Event, Attachment[], Profile | undefined] | undefined { | ||
let event: Event | undefined; | ||
const attachments: Attachment[] = []; | ||
let profile: Profile | undefined; | ||
|
||
forEachEnvelopeItem(envelope, (item, type) => { | ||
if (type === 'event' || type === 'transaction' || type === 'feedback') { | ||
event = Array.isArray(item) ? (item as EventItem)[1] : undefined; | ||
} else if (type === 'attachment') { | ||
const [headers, data] = item as AttachmentItem; | ||
|
||
attachments.push({ | ||
filename: headers.filename, | ||
attachmentType: headers.attachment_type, | ||
contentType: headers.content_type, | ||
data, | ||
}); | ||
} else if (type === 'profile') { | ||
profile = item[1] as unknown as Profile; | ||
} | ||
}); | ||
|
||
return event ? [event, attachments, profile] : undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { captureEvent, getClient } from '@sentry/node'; | ||
import { Attachment, Event } from '@sentry/types'; | ||
import { logger, parseEnvelope } from '@sentry/utils'; | ||
import * as electron from 'electron'; | ||
|
||
import { eventFromEnvelope } from '../common/envelope'; | ||
import { getMagicMessage, isMagicMessage } from '../common/ipc'; | ||
import { mergeEvents } from './merge'; | ||
|
||
function log(message: string): void { | ||
logger.log(`[Utility Process] ${message}`); | ||
} | ||
|
||
/** | ||
* We wrap `electron.utilityProcess.fork` so we can pass a messageport to any SDK running in the utility process | ||
*/ | ||
export function configureUtilityProcessIPC(): void { | ||
if (!electron.utilityProcess?.fork) { | ||
return; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/unbound-method | ||
electron.utilityProcess.fork = new Proxy(electron.utilityProcess.fork, { | ||
apply: (target, thisArg, args: Parameters<typeof electron.utilityProcess.fork>) => { | ||
// Call the underlying function to get the child process | ||
const child: electron.UtilityProcess = target.apply(thisArg, args); | ||
|
||
function getProcessName(): string { | ||
const [, , options] = args; | ||
return options?.serviceName || `pid:${child.pid}`; | ||
} | ||
|
||
// We don't send any messages unless we've heard from the child SDK. At that point we know it's ready to receive | ||
// and will also filter out any messages we send so users don't see them | ||
child.on('message', (msg: unknown) => { | ||
if (isMagicMessage(msg)) { | ||
log(`SDK started in utility process '${getProcessName()}'`); | ||
|
||
const { port1, port2 } = new electron.MessageChannelMain(); | ||
|
||
port2.on('message', (msg) => { | ||
if (msg.data instanceof Uint8Array || typeof msg.data === 'string') { | ||
handleEnvelopeFromUtility(msg.data); | ||
} | ||
}); | ||
port2.start(); | ||
|
||
// Send one side of the message port to the child SDK | ||
child.postMessage(getMagicMessage(), [port1]); | ||
} | ||
}); | ||
|
||
// We proxy child.on so we can filter messages from the child SDK and ensure that users do not see them | ||
// eslint-disable-next-line @typescript-eslint/unbound-method | ||
child.on = new Proxy(child.on, { | ||
apply: (target, thisArg, [event, listener]) => { | ||
if (event === 'message') { | ||
return target.apply(thisArg, [ | ||
'message', | ||
(msg: unknown) => { | ||
if (isMagicMessage(msg)) { | ||
return; | ||
} | ||
|
||
return listener(msg); | ||
}, | ||
]); | ||
} | ||
|
||
return target.apply(thisArg, [event, listener]); | ||
}, | ||
}); | ||
|
||
return child; | ||
}, | ||
}); | ||
} | ||
|
||
function handleEnvelopeFromUtility(env: Uint8Array | string): void { | ||
const envelope = parseEnvelope(env); | ||
|
||
const eventAndAttachments = eventFromEnvelope(envelope); | ||
if (eventAndAttachments) { | ||
const [event, attachments] = eventAndAttachments; | ||
|
||
captureEventFromUtility(event, attachments); | ||
} else { | ||
// Pass other types of envelope straight to the transport | ||
void getClient()?.getTransport()?.send(envelope); | ||
} | ||
} | ||
|
||
function captureEventFromUtility(event: Event, attachments: Attachment[]): void { | ||
// Remove the environment as it defaults to 'production' and overwrites the main process environment | ||
delete event.environment; | ||
delete event.release; | ||
|
||
// Remove the SDK info as we want the Electron SDK to be the one reporting the event | ||
delete event.sdk?.name; | ||
delete event.sdk?.version; | ||
delete event.sdk?.packages; | ||
|
||
captureEvent(mergeEvents(event, { tags: { 'event.process': 'utility' } }), { attachments }); | ||
} |
Oops, something went wrong.