mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
docs: overhaul crash reporting documentation (#54280)
* docs: overhaul crash reporting documentation Add a Crash Reporting tutorial covering how Crashpad collects minidumps, setting up crashReporter, attaching context with globalExtra and per-process parameters, reacting to process-gone events, receiving uploads, symbolicating with Electron's published symbols, and hosted services. Correct the crashReporter API reference: value limit is 20320 bytes (not 127) and long keys emit a warning; rateLimit works on all platforms; ignoreSystemCrashHandler has no effect on Windows; compress: false is deprecated; getParameters() excludes globalExtra; getUploadedReports() also lists reports that were not uploaded; Node child processes have no process.crashReporter.start(); document ptype and _companyName in the payload; note MAS no-op behaviour; drop the archived mini-breakpad-server and Mozilla-only Socorro and move hosted services to the tutorial. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: trim implementation details from crash reporting docs Document behaviour rather than internals so the docs don't become an accidental API contract: drop the crash dumps directory layout and file naming, handler process details, Chromium-internal upload fields and query parameters, exact release asset names and the symbol server path format, and notes about behaviour from old Electron versions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: remove privacy section from crash reporting tutorial Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: recommend electron-minidump for symbolication Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: move default values to end of crashReporter option descriptions Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: address crash reporting review feedback Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i * docs: link crashReporter methods in MAS note Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFzbSEnmLgZoxGXgEKSH4i --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
1c36d90659
commit
718c8a6425
4 files changed
+458
-57
No files matched your search
@@ -58,6 +58,7 @@ an issue:
|
||||
* [DevTools Extension](tutorial/devtools-extension.md)
|
||||
* [Automated Testing](tutorial/automated-testing.md)
|
||||
* [REPL](tutorial/repl.md)
|
||||
* [Crash Reporting](tutorial/crash-reporting.md)
|
||||
* [Distribution](tutorial/application-distribution.md)
|
||||
* [Code Signing](tutorial/code-signing.md)
|
||||
* [Mac App Store](tutorial/mac-app-store-submission-guide.md)
|
||||
|
||||
+2
-1
@@ -656,7 +656,8 @@ Returns `string` - The current application directory.
|
||||
* `videos` Directory for a user's videos.
|
||||
* `recent` Directory for the user's recent files (Windows only).
|
||||
* `logs` Directory for your app's log folder.
|
||||
* `crashDumps` Directory where crash dumps are stored.
|
||||
* `crashDumps` Directory where crash dumps are stored. See
|
||||
[Crash Reporting](../tutorial/crash-reporting.md#what-happens-when-your-app-crashes).
|
||||
|
||||
Returns `string` - A path to a special directory or file associated with `name`. On
|
||||
failure, an `Error` is thrown.
|
||||
|
||||
+82
-56
@@ -19,30 +19,27 @@ const { crashReporter } = require('electron')
|
||||
crashReporter.start({ submitURL: 'https://your-domain.com/url-to-submit' })
|
||||
```
|
||||
|
||||
For setting up a server to accept and process crash reports, you can use
|
||||
following projects:
|
||||
For a guide to collecting, receiving and symbolicating crash reports, including
|
||||
how to run your own crash server or use a hosted service, see the
|
||||
[Crash Reporting](../tutorial/crash-reporting.md) tutorial.
|
||||
|
||||
* [socorro](https://github.com/mozilla-services/socorro)
|
||||
* [mini-breakpad-server](https://github.com/electron/mini-breakpad-server)
|
||||
Electron uses [Crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/refs/heads/main/README.md)
|
||||
to monitor and report crashes. Crashpad uses the same
|
||||
[upload protocol](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#Upload-to-collection-server)
|
||||
as Breakpad, so servers that accept Breakpad minidumps can receive Electron's
|
||||
crash reports.
|
||||
|
||||
> [!NOTE]
|
||||
> Electron uses Crashpad, not Breakpad, to collect and upload
|
||||
> crashes, but for the time being, the [upload protocol is the same](https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/doc/overview_design.md#Upload-to-collection-server).
|
||||
Crash reports are stored under the directory returned by
|
||||
[`app.getPath('crashDumps')`](app.md#appgetpathname). You can override it by calling
|
||||
[`app.setPath('crashDumps', '/path/to/crashes')`](app.md#appsetpathname-path) before starting the crash
|
||||
reporter. The layout of files inside this directory is an implementation detail
|
||||
and may change between versions of Electron.
|
||||
|
||||
Or use a 3rd party hosted solution:
|
||||
|
||||
* [Backtrace](https://backtrace.io/electron/)
|
||||
* [Sentry](https://docs.sentry.io/clients/electron)
|
||||
* [BugSplat](https://www.bugsplat.com/docs/platforms/electron)
|
||||
* [Bugsnag](https://docs.bugsnag.com/platforms/electron/)
|
||||
|
||||
Crash reports are stored temporarily before being uploaded in a directory
|
||||
underneath the app's user data directory, called 'Crashpad'. You can override
|
||||
this directory by calling `app.setPath('crashDumps', '/path/to/crashes')`
|
||||
before starting the crash reporter.
|
||||
|
||||
Electron uses [crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/refs/heads/main/README.md)
|
||||
to monitor and report crashes.
|
||||
The `crashReporter` module is disabled in Mac App Store builds. Its methods can
|
||||
be called, but they do nothing: no crash reports are collected or uploaded,
|
||||
[`getUploadedReports()`](#crashreportergetuploadedreports) returns an empty array and
|
||||
[`getUploadToServer()`](#crashreportergetuploadtoserver) returns
|
||||
`false`.
|
||||
|
||||
On Windows, some crashes never reach an in-process crash handler, most notably
|
||||
`__fastfail` terminations (`STATUS_STACK_BUFFER_OVERRUN`, raised by
|
||||
@@ -90,13 +87,16 @@ changes:
|
||||
* `uploadToServer` boolean (optional) - Whether crash reports should be sent
|
||||
to the server. If false, crash reports will be collected and stored in the
|
||||
crashes directory, but not uploaded. Default is `true`.
|
||||
* `ignoreSystemCrashHandler` boolean (optional) - If true, crashes generated
|
||||
in the main process will not be forwarded to the system crash handler.
|
||||
Default is `false`.
|
||||
* `rateLimit` boolean (optional) _macOS_ _Windows_ - If true, limit the
|
||||
number of crashes uploaded to 1/hour. Default is `false`.
|
||||
* `ignoreSystemCrashHandler` boolean (optional) _macOS_ _Linux_ - If true,
|
||||
crashes generated in the main process will not be forwarded to the system
|
||||
crash handler. This option has no effect on Windows. Default is `false`.
|
||||
* `rateLimit` boolean (optional) - If true, limit the number of crashes
|
||||
uploaded to 1/hour. Crash reports over the limit are not uploaded, but are
|
||||
still stored on disk. Default is `false`.
|
||||
* `compress` boolean (optional) - If true, crash reports will be compressed
|
||||
and uploaded with `Content-Encoding: gzip`. Default is `true`.
|
||||
and uploaded with `Content-Encoding: gzip`. Setting this
|
||||
to `false` while `uploadToServer` is `true` is deprecated and logs a
|
||||
deprecation warning. Default is `true`.
|
||||
* `extra` Record\<string, string\> (optional) - Extra string key/value
|
||||
annotations that will be sent along with crash reports that are generated
|
||||
in the main process. Only string values are supported. Crashes generated in
|
||||
@@ -110,7 +110,8 @@ changes:
|
||||
been started. If a key is present in both the global extra parameters and
|
||||
the process-specific extra parameters, then the global one will take
|
||||
precedence. By default, `productName` and the app version are included, as
|
||||
well as the Electron version.
|
||||
well as the Electron version. Global extra parameters are not returned by
|
||||
[`getParameters()`](#crashreportergetparameters).
|
||||
|
||||
This method must be called before using any other `crashReporter` APIs. Once
|
||||
initialized this way, the crashpad handler collects crashes from all
|
||||
@@ -132,10 +133,10 @@ by the crash reporter.
|
||||
|
||||
> [!NOTE]
|
||||
> Parameters passed in `extra`, `globalExtra` or set with
|
||||
> `addExtraParameter` have limits on the length of the keys and values. Key names
|
||||
> must be at most 39 bytes long, and values must be no longer than 127 bytes.
|
||||
> Keys with names longer than the maximum will be silently ignored. Key values
|
||||
> longer than the maximum length will be truncated.
|
||||
> `addExtraParameter` have limits on the length of the keys and values. Key
|
||||
> names must be at most 39 bytes long, and values must be no longer than 20320
|
||||
> bytes. Keys with names longer than the maximum are ignored, and a warning is
|
||||
> emitted. Values longer than the maximum length are truncated.
|
||||
|
||||
> [!NOTE]
|
||||
> This method is only available in the main process.
|
||||
@@ -152,9 +153,13 @@ changes:
|
||||
-->
|
||||
|
||||
Returns [`CrashReport | null`](structures/crash-report.md) - The date and ID of the
|
||||
last crash report. Only crash reports that have been uploaded will be returned;
|
||||
even if a crash report is present on disk it will not be returned until it is
|
||||
uploaded. In the case that there are no uploaded reports, `null` is returned.
|
||||
crash report with the most recent upload time, from the list returned by
|
||||
[`getUploadedReports()`](#crashreportergetuploadedreports). If there are no crash
|
||||
reports at all, `null` is returned.
|
||||
|
||||
If no report has been uploaded yet but some are stored on disk, a report that has
|
||||
not been uploaded may be returned. Check that its `id` is not empty before
|
||||
treating it as uploaded.
|
||||
|
||||
> [!NOTE]
|
||||
> This method is only available in the main process.
|
||||
@@ -172,8 +177,14 @@ changes:
|
||||
|
||||
Returns [`CrashReport[]`](structures/crash-report.md):
|
||||
|
||||
Returns all uploaded crash reports. Each report contains the date and uploaded
|
||||
ID.
|
||||
Returns the crash reports stored on disk. Each report contains the date it was
|
||||
uploaded and the ID that the crash server returned for it.
|
||||
|
||||
Despite the method's name, reports that have not been uploaded (for example
|
||||
because `uploadToServer` is `false`, the upload failed, or the report was rate
|
||||
limited) are included too. For those reports, `id` is an empty string and `date`
|
||||
is not meaningful. To list only uploaded reports, filter out reports with an
|
||||
empty `id`.
|
||||
|
||||
> [!NOTE]
|
||||
> This method is only available in the main process.
|
||||
@@ -217,11 +228,13 @@ called before `start` is called.
|
||||
### `crashReporter.addExtraParameter(key, value)`
|
||||
|
||||
* `key` string - Parameter key, must be no longer than 39 bytes.
|
||||
* `value` string - Parameter value, must be no longer than 127 bytes.
|
||||
* `value` string - Parameter value, must be no longer than 20320 bytes.
|
||||
|
||||
Set an extra parameter to be sent with the crash report. The values specified
|
||||
here will be sent in addition to any values set via the `extra` option when
|
||||
`start` was called.
|
||||
`start` was called. Calling this again with the same key replaces the value.
|
||||
The value is read when a crash happens, so you can update it as your app's
|
||||
state changes.
|
||||
|
||||
Parameters added in this fashion (or via the `extra` parameter to
|
||||
`crashReporter.start`) are specific to the calling process. Adding extra
|
||||
@@ -229,12 +242,15 @@ parameters in the main process will not cause those parameters to be sent along
|
||||
with crashes from renderer or other child processes. Similarly, adding extra
|
||||
parameters in a renderer process will not result in those parameters being sent
|
||||
with crashes that occur in other renderer processes or in the main process.
|
||||
Processes created with [`utilityProcess`](utility-process.md) have no API for
|
||||
setting extra parameters, so only `globalExtra` values are sent with their
|
||||
crashes.
|
||||
|
||||
> [!NOTE]
|
||||
> Parameters have limits on the length of the keys and values. Key
|
||||
> names must be no longer than 39 bytes, and values must be no longer than 20320
|
||||
> bytes. Keys with names longer than the maximum will be silently ignored. Key
|
||||
> values longer than the maximum length will be truncated.
|
||||
> bytes. Keys with names longer than the maximum are ignored, and a warning is
|
||||
> emitted. Values longer than the maximum length are truncated.
|
||||
|
||||
### `crashReporter.removeExtraParameter(key)`
|
||||
|
||||
@@ -245,21 +261,21 @@ will not include this parameter.
|
||||
|
||||
### `crashReporter.getParameters()`
|
||||
|
||||
Returns `Record<string, string>` - The current 'extra' parameters of the crash reporter.
|
||||
Returns `Record<string, string>` - The current 'extra' parameters of the crash
|
||||
reporter in the calling process, as set with the `extra` option and
|
||||
`addExtraParameter`. Parameters set with the `globalExtra` option are not
|
||||
included.
|
||||
|
||||
## In Node child processes
|
||||
|
||||
Since `require('electron')` is not available in Node child processes, the
|
||||
following APIs are available on the `process` object in Node child processes.
|
||||
Since `require('electron')` is not available in Node child processes (processes
|
||||
run with `ELECTRON_RUN_AS_NODE`, such as those created with
|
||||
`child_process.fork()`), the following APIs are available on the `process`
|
||||
object in Node child processes.
|
||||
|
||||
#### `process.crashReporter.start(options)`
|
||||
|
||||
See [`crashReporter.start()`](#crashreporterstartoptions).
|
||||
|
||||
Note that if the crash reporter is started in the main process, it will
|
||||
automatically monitor child processes, so it should not be started in the child
|
||||
process. Only use this method if the main process does not initialize the crash
|
||||
reporter.
|
||||
If the crash reporter is started in the main process, Node child processes are
|
||||
monitored automatically. There is no way to start the crash reporter from a
|
||||
Node child process.
|
||||
|
||||
#### `process.crashReporter.getParameters()`
|
||||
|
||||
@@ -276,18 +292,28 @@ See [`crashReporter.removeExtraParameter(key)`](#crashreporterremoveextraparamet
|
||||
## Crash Report Payload
|
||||
|
||||
The crash reporter will send the following data to the `submitURL` as
|
||||
a `multipart/form-data` `POST`:
|
||||
a `multipart/form-data` `POST`. Unless `compress` is `false`, the request body
|
||||
is gzip-compressed and sent with `Content-Encoding: gzip`.
|
||||
|
||||
* `ver` string - The version of Electron.
|
||||
* `platform` string - e.g. 'win32'.
|
||||
* `process_type` string - e.g. 'renderer'.
|
||||
* `process_type` string - e.g. 'renderer', or 'browser' for the main process.
|
||||
* `guid` string - e.g. '5e1286fc-da97-479e-918b-6bfb0c3d1c72'.
|
||||
* `_version` string - The version in `package.json`.
|
||||
* `_productName` string - The product name in the `crashReporter` `options`
|
||||
object.
|
||||
* `prod` string - Name of the underlying product. In this case Electron.
|
||||
* `_companyName` string - The company name in the `crashReporter` `options`
|
||||
object.
|
||||
object. Only sent if the deprecated `companyName` option is set.
|
||||
* `upload_file_minidump` File - The crash report in the format of `minidump`.
|
||||
* All level one properties of the `extra` object in the `crashReporter`
|
||||
* All level one properties of the `globalExtra` object in the `crashReporter`
|
||||
`options` object.
|
||||
* All extra parameters of the process that crashed, set with the `extra`
|
||||
option (main process only) or `addExtraParameter`.
|
||||
|
||||
Crashpad and Chromium may add other fields to the upload. These are not part
|
||||
of Electron's API and can change without notice, so don't rely on them.
|
||||
|
||||
The body of the server's response is stored as the crash report's ID, and is
|
||||
returned in the `id` field by
|
||||
[`getUploadedReports()`](#crashreportergetuploadedreports).
|
||||
@@ -0,0 +1,373 @@
|
||||
# Crash Reporting
|
||||
|
||||
## Overview
|
||||
|
||||
When a native crash happens in your app (e.g. a segfault in Chromium, a fatal V8 error, an
|
||||
out-of-memory renderer, or a bug in a native Node.js module), there is no JavaScript
|
||||
exception to catch. Electron's [`crashReporter`](../api/crash-reporter.md) module
|
||||
captures these crashes as minidumps that you can collect, upload and turn back into
|
||||
readable stack traces.
|
||||
|
||||
This guide covers how crash reporting works in Electron, how to attach useful context to
|
||||
reports, how to receive them, and how to symbolicate them.
|
||||
|
||||
## What happens when your app crashes
|
||||
|
||||
Electron uses [Crashpad](https://chromium.googlesource.com/crashpad/crashpad/+/refs/heads/main/README.md),
|
||||
the same crash reporting system as Chromium. When you call `crashReporter.start()` in the
|
||||
main process:
|
||||
|
||||
1. Electron starts a separate crash handler process.
|
||||
2. Child processes created after `start()` is called (renderers, the GPU process, utility
|
||||
processes and Node.js child processes) are monitored automatically.
|
||||
3. When a monitored process crashes, the handler writes a minidump: a snapshot of the
|
||||
crashed process's threads, stacks and loaded modules. This works even when the crashed
|
||||
process is too broken to run any code of its own.
|
||||
4. If uploads are enabled, the handler sends the minidump to your `submitURL`, along with
|
||||
the annotations (key/value metadata) that were set when the process crashed.
|
||||
|
||||
Crash reports are stored under the directory returned by `app.getPath('crashDumps')`. To
|
||||
store them somewhere else, call `app.setPath('crashDumps', path)` before calling
|
||||
`crashReporter.start()`. The layout of files inside this directory is an implementation
|
||||
detail and may change between versions of Electron, so don't depend on it.
|
||||
|
||||
## Setting up the crash reporter
|
||||
|
||||
Call `crashReporter.start()` in the main process as early as possible, ideally before
|
||||
`app.whenReady()`. Renderer and child processes that start before the crash reporter
|
||||
won't be monitored.
|
||||
|
||||
```js title='main.js'
|
||||
const { app, crashReporter } = require('electron')
|
||||
|
||||
crashReporter.start({
|
||||
submitURL: 'https://crashes.example.com/submit',
|
||||
uploadToServer: true,
|
||||
globalExtra: {
|
||||
releaseChannel: 'beta'
|
||||
}
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// create windows...
|
||||
})
|
||||
```
|
||||
|
||||
The options that matter most are:
|
||||
|
||||
* `submitURL` - Where crash reports are sent, as a `multipart/form-data` `POST`.
|
||||
Required unless `uploadToServer` is `false`.
|
||||
* `uploadToServer` - Whether to upload reports. If you ask users for consent to send
|
||||
crash reports, start with `uploadToServer: false` and call
|
||||
`crashReporter.setUploadToServer(true)` when they opt in. Reports are still written
|
||||
to disk when uploads are disabled.
|
||||
* `productName` - Sent as `_productName`. Defaults to `app.name`.
|
||||
* `globalExtra` - Annotations that are sent with crashes from every process. See
|
||||
[Attaching context to crash reports](#attaching-context-to-crash-reports).
|
||||
* `extra` - Annotations for crashes in the main process only.
|
||||
* `rateLimit` - Limits uploads to one per hour. Reports over the limit are not uploaded
|
||||
but stay on disk.
|
||||
* `compress` - Uploads are gzip-compressed (`Content-Encoding: gzip`) by default. Most
|
||||
crash servers expect this. Setting `compress: false` is deprecated and logs a
|
||||
warning.
|
||||
* `ignoreSystemCrashHandler` - Stops main process crashes from also being passed to the
|
||||
operating system's crash handler. This has no effect on Windows.
|
||||
|
||||
Calling `start()` a second time does nothing, and the crash reporter can't be stopped
|
||||
once it has started.
|
||||
|
||||
To test your setup, call `process.crash()` from the process you want to crash.
|
||||
|
||||
### Mac App Store builds
|
||||
|
||||
The crash reporter is disabled in [Mac App Store](./mac-app-store-submission-guide.md)
|
||||
builds of Electron. All `crashReporter` methods can still be called, but they do
|
||||
nothing: no minidumps are written and `getUploadedReports()` always returns an empty
|
||||
array. Crashes in MAS builds are reported only through Apple's own crash reporting.
|
||||
|
||||
### Sandboxed renderers and preload scripts
|
||||
|
||||
`crashReporter` is one of the modules available to [sandboxed](./sandbox.md) preload
|
||||
scripts. In renderer processes it only provides `addExtraParameter()`,
|
||||
`removeExtraParameter()` and `getParameters()`. The crash reporter itself is started in
|
||||
the main process.
|
||||
|
||||
With [context isolation](./context-isolation.md) enabled, web content can't reach
|
||||
`crashReporter` directly. Call it from your preload script, or expose a narrow function
|
||||
with the [`contextBridge`](../api/context-bridge.md), as shown in the next section.
|
||||
|
||||
## Attaching context to crash reports
|
||||
|
||||
A stack trace tells you where a crash happened. Annotations tell you what the app was
|
||||
doing at the time: which window was open, which feature was in use, which account type
|
||||
the user has. Annotation values are captured at the moment of the crash, so what you get
|
||||
is whatever the value was when the process died.
|
||||
|
||||
### Where annotations apply
|
||||
|
||||
There are two kinds of annotation, and it's important to put each value in the right
|
||||
place:
|
||||
|
||||
* **`globalExtra`** is set once, in `crashReporter.start()`, and is sent with crashes
|
||||
from every process. It can't be changed later. Use it for values that don't change
|
||||
while the app is running, such as a release channel or build ID.
|
||||
* **`extra` and `addExtraParameter()`** are per-process. A value set in the main
|
||||
process is only sent with main process crashes. A value set in one renderer is only
|
||||
sent when that renderer crashes. Each process has to set its own values.
|
||||
|
||||
If the same key is set in both `globalExtra` and a process's own parameters, the
|
||||
`globalExtra` value is used.
|
||||
|
||||
Where you can set per-process values:
|
||||
|
||||
| Process | How to set values |
|
||||
| --- | --- |
|
||||
| Main | `extra` in `crashReporter.start()`, or `crashReporter.addExtraParameter()` |
|
||||
| Renderer | `crashReporter.addExtraParameter()` in the preload script |
|
||||
| Node.js child process (`child_process.fork()`) | `process.crashReporter.addExtraParameter()` |
|
||||
| Utility process (`utilityProcess.fork()`) | No API. Only `globalExtra` values are sent. |
|
||||
|
||||
`crashReporter.getParameters()` returns the current process's own parameters. It does not
|
||||
include `globalExtra`.
|
||||
|
||||
### Limits
|
||||
|
||||
* Key names must be at most 39 bytes long. Longer keys are ignored, and Electron emits a
|
||||
process warning when you try to set one.
|
||||
* Values are strings of at most 20320 bytes. Longer values are truncated.
|
||||
* Limits are in bytes, not characters, so non-ASCII text uses up the limit faster.
|
||||
|
||||
### Keep annotations current
|
||||
|
||||
Because values are read at crash time, update them as your app's state changes. For
|
||||
example, you can record how many windows are open and which feature the user is using
|
||||
in the main process:
|
||||
|
||||
```js title='main.js'
|
||||
const { app, crashReporter } = require('electron')
|
||||
|
||||
let windowCount = 0
|
||||
|
||||
app.on('browser-window-created', (event, win) => {
|
||||
windowCount++
|
||||
crashReporter.addExtraParameter('windowCount', String(windowCount))
|
||||
win.on('closed', () => {
|
||||
windowCount--
|
||||
crashReporter.addExtraParameter('windowCount', String(windowCount))
|
||||
})
|
||||
})
|
||||
|
||||
async function exportProject() {
|
||||
crashReporter.addExtraParameter('feature', 'export')
|
||||
try {
|
||||
// ...run the export
|
||||
} finally {
|
||||
crashReporter.removeExtraParameter('feature')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For renderer crashes, set values in the renderer. This preload script records the current
|
||||
route of a single-page app and lets the page mark which feature is active. It only
|
||||
accepts known keys, so the page can't fill reports with arbitrary data:
|
||||
|
||||
```js title='preload.js'
|
||||
const { contextBridge, crashReporter } = require('electron')
|
||||
|
||||
const allowedKeys = new Set(['feature', 'route'])
|
||||
|
||||
contextBridge.exposeInMainWorld('crashContext', {
|
||||
set: (key, value) => {
|
||||
if (allowedKeys.has(key)) {
|
||||
crashReporter.addExtraParameter(key, String(value))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
crashReporter.addExtraParameter('route', location.pathname)
|
||||
})
|
||||
```
|
||||
|
||||
```js title='renderer.js' @ts-nocheck
|
||||
window.crashContext.set('route', '/settings')
|
||||
window.crashContext.set('feature', 'image-editor')
|
||||
```
|
||||
|
||||
## Reacting to crashes at runtime
|
||||
|
||||
Minidumps are for diagnosing a crash later. To react when a process dies, for example to
|
||||
log it or recover, listen for these events in the main process:
|
||||
|
||||
* [`render-process-gone`](../api/app.md#event-render-process-gone) on `app`, or on a
|
||||
single [`webContents`](../api/web-contents.md#event-render-process-gone), for renderer
|
||||
processes.
|
||||
* [`child-process-gone`](../api/app.md#event-child-process-gone) on `app` for all other
|
||||
child processes, such as the GPU and utility processes.
|
||||
|
||||
Both events provide a `reason` (such as `crashed`, `oom`, `killed` or `clean-exit`) and
|
||||
an `exitCode`.
|
||||
|
||||
```js title='main.js'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
app.on('child-process-gone', (event, details) => {
|
||||
console.error(`${details.type} process gone: ${details.reason} (exit code ${details.exitCode})`)
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = new BrowserWindow()
|
||||
let recentCrashes = 0
|
||||
|
||||
win.webContents.on('render-process-gone', (event, details) => {
|
||||
console.error(`Renderer gone: ${details.reason} (exit code ${details.exitCode})`)
|
||||
if (details.reason === 'clean-exit') return
|
||||
|
||||
// Reload the page in a new renderer process, but don't retry forever.
|
||||
recentCrashes++
|
||||
if (recentCrashes <= 3) {
|
||||
win.reload()
|
||||
setTimeout(() => {
|
||||
recentCrashes--
|
||||
}, 60 * 1000)
|
||||
}
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
})
|
||||
```
|
||||
|
||||
## Receiving crash reports
|
||||
|
||||
### On your own server
|
||||
|
||||
Crash reports are sent to `submitURL` as a `multipart/form-data` `POST`, gzip-compressed
|
||||
unless you set `compress: false`. The form contains:
|
||||
|
||||
* `upload_file_minidump` - The minidump file.
|
||||
* `process_type` - The type of process that crashed, such as `renderer`, or `browser`
|
||||
for the main process.
|
||||
* `prod` - Always `Electron`.
|
||||
* `ver` - The Electron version.
|
||||
* `_productName` - The `productName` option, which defaults to `app.name`.
|
||||
* `_version` - Your app's version, from `app.getVersion()`.
|
||||
* `guid` - An ID for this installation.
|
||||
* `platform` - `win32`, `darwin` or `linux`.
|
||||
* Your `globalExtra` values and the crashed process's own parameters.
|
||||
|
||||
Crashpad and Chromium may add other fields. These aren't part of Electron's API and can
|
||||
change without notice, so don't rely on them. See
|
||||
[Crash Report Payload](../api/crash-reporter.md#crash-report-payload) for the full list
|
||||
of documented fields.
|
||||
|
||||
Respond with a `200` status. The body of the response is stored as the report's ID, which
|
||||
`crashReporter.getUploadedReports()` returns, so you can use it to link a user's report
|
||||
to your server's record.
|
||||
|
||||
Crashpad uses the Breakpad upload protocol, so any server that accepts Breakpad or
|
||||
Crashpad minidumps can receive Electron's reports.
|
||||
|
||||
### Collecting reports locally
|
||||
|
||||
If you set `uploadToServer: false`, crash reports are still written under
|
||||
`app.getPath('crashDumps')`, but they are not sent anywhere. This is useful while
|
||||
developing, or if you want to ask the user before sending anything: once they agree, call
|
||||
`crashReporter.setUploadToServer(true)`. Only crashes that happen after that are
|
||||
uploaded; reports written while uploads were disabled are not sent later.
|
||||
|
||||
Electron doesn't provide an API for reading minidump files from disk, and the layout of
|
||||
the crash dumps directory can change between versions. If you need the minidumps
|
||||
themselves, receive them with your own `submitURL` (which can be a server running on
|
||||
the same machine) instead of reading files from the directory.
|
||||
|
||||
## Symbolicating crash reports
|
||||
|
||||
A minidump contains raw memory addresses, not function names. Electron's release builds
|
||||
are stripped of debug information, so to turn those addresses into a readable stack
|
||||
trace you need the symbol files for the exact Electron version, platform and
|
||||
architecture that crashed. This is called symbolication.
|
||||
|
||||
### Getting Electron's symbols
|
||||
|
||||
Each [Electron release](https://github.com/electron/electron/releases) on GitHub ships
|
||||
symbol archives for every platform it supports:
|
||||
|
||||
* [Breakpad](https://chromium.googlesource.com/breakpad/breakpad/) symbols for all
|
||||
platforms. These are what most minidump tools use.
|
||||
* dSYMs for macOS, for use with Apple's tools such as `atos` and `lldb`.
|
||||
* PDBs for Windows, for use with WinDbg and Visual Studio.
|
||||
* Debug info for Linux, for use with `gdb`.
|
||||
|
||||
Electron also runs a symbol server at `https://symbols.electronjs.org`. Tools that
|
||||
support symbol servers can download the symbols they need from it, so you don't have to
|
||||
download the archives for each version. For debuggers on Windows, see
|
||||
[Setting Up Symbol Server in Debugger](../development/debugging-with-symbol-server.md).
|
||||
|
||||
### Example: symbolicating a minidump
|
||||
|
||||
The [`electron-minidump`](https://www.npmjs.com/package/electron-minidump) package
|
||||
symbolicates an Electron minidump in one step. It works out which Electron version the
|
||||
dump came from, downloads the matching symbols, and prints
|
||||
the stack of every thread:
|
||||
|
||||
```sh
|
||||
npx electron-minidump /path/to/crash.dmp
|
||||
```
|
||||
|
||||
It runs on macOS and Linux, and can symbolicate dumps from any platform.
|
||||
|
||||
If you also have symbols for your own native code, pass a directory containing all the
|
||||
Breakpad symbols to Breakpad's `minidump_stackwalk` tool instead.
|
||||
|
||||
### Symbols for your own code
|
||||
|
||||
Electron's symbols only cover Electron's own binaries. If your app includes native Node.js
|
||||
modules or other native libraries, frames in those will stay unsymbolicated unless you
|
||||
also keep symbols for them. Generate Breakpad symbols for each binary you ship with
|
||||
Breakpad's `dump_syms` tool, and store them where your symbolication tool can find them. Keep them for every
|
||||
version you release, since symbols only match the exact build they came from.
|
||||
|
||||
## macOS system crash reports
|
||||
|
||||
When an app crashes on macOS, the system may also write a crash report (an `.ips` file,
|
||||
shown in the Console app) to `~/Library/Logs/DiagnosticReports`. For Electron's release
|
||||
builds these reports are misleading: because the binaries are stripped, macOS labels each
|
||||
frame with the nearest exported symbol plus a large offset, such as
|
||||
`v8::internal::SetupIsolateDelegate::SetupHeap(v8::internal::Heap*) + 2919558`. These
|
||||
function names are almost always wrong.
|
||||
|
||||
To get a correct stack from one of these reports, use
|
||||
[`@electron/symbolicate-mac`](https://github.com/electron/symbolicate-mac). It reads text
|
||||
crash reports, spindumps and `sample` output, works out the Electron version from the
|
||||
report's "Binary Images" section, and downloads the right symbols:
|
||||
|
||||
```sh
|
||||
npx @electron/symbolicate-mac /path/to/crash.txt
|
||||
```
|
||||
|
||||
For an `.ips` file, open it in the Console app and save the full text report, including
|
||||
the "Binary Images" section, first.
|
||||
|
||||
You can also symbolicate individual addresses yourself with `atos` and the dSYM from the
|
||||
matching release. Take the load address of `Electron Framework` from the "Binary Images"
|
||||
section and the frame's address from the stack:
|
||||
|
||||
```sh
|
||||
atos -arch arm64 -o "Electron Framework.dSYM/Contents/Resources/DWARF/Electron Framework" \
|
||||
-l 0x109b49000 0x10ae32a06
|
||||
```
|
||||
|
||||
## Using a hosted crash reporting service
|
||||
|
||||
Instead of running your own server, you can send crash reports to a hosted service. These
|
||||
services receive Electron's minidumps, symbolicate them (usually with Electron's public
|
||||
symbols) and group similar crashes. Many also offer SDKs that report JavaScript errors
|
||||
alongside native crashes. Services with Electron support include:
|
||||
|
||||
* [Sentry](https://docs.sentry.io/platforms/javascript/guides/electron/)
|
||||
* [BugSplat](https://docs.bugsplat.com/integrations/desktop/electron)
|
||||
* [Backtrace](https://github.com/backtrace-labs/backtrace-javascript/tree/main/packages/electron) (part of Sauce Labs)
|
||||
* [Bugsnag](https://docs.bugsnag.com/platforms/electron/)
|
||||
|
||||
This list is provided for convenience. The Electron project doesn't endorse or support
|
||||
any of these services; check each one's documentation for how it works with Electron.
|
||||
Reference in new issue
Block a user