Writing a Notifier
Bugsnag's crash reporting library automatically detects crashes, collecting diagnostic information and immediately notifying your development team, helping you to understand and resolve issues as fast as possible.
Writing a Notifier
When writing a notifier library, you should consider providing methods to allow users to configure how errors are sent to Bugsnag.
On our official libraries, we attempt to follow the following guidelines:
- There should be a
CHANGELOG.md in the root of the repository, documenting and dating each release
- There should be a
LICENSE.txt in the root of the repository
- There should be a
CONTRIBUTING.md in the root of the repository with the following sections:
- How to contribute - basic git usage, building the project, running tests, and making a PR
- How to release - instructions for shipping a new version
- There should be a
README.md in the root of the repository, explaining the bare minimum about the library:
- Getting started
- Adding the library as a dependency to an application should be possible in around 1-2 lines of commands.
- There should at least be one installation method via a (language) package manager.
- There should be installation instructions for using a source archive (which can be more than 1-2 lines).
- Basic configuration should be able to be explained in three sentences, which should put the user in a state to send notifications and capture unhandled errors.
Capturing unhandled errors
- Each client should have the ability to capture and report unhandled errors/exceptions
- Where possible, capturing and reporting unhandled errors should be enabled by default
- Unhandled error reports should have a severity of error
- Each client should provide the ability to modify reports before it is sent to Bugsnag via callbacks or builders. The interfaces for modifying reports globally and individually should be the same.
Capturing handled errors
- Each client should have the ability to report handled errors via a
- Handled error reports should have a default severity of warning
- Each client should provide the ability to modify an individual report before it is sent to Bugsnag via callbacks or builders. The interfaces for modifying reports globally and individually should be the same.
- Each client should have the ability to capture “breadcrumbs” - a snapshot of a user action or application state - scoped to the activity of a single user or transaction. Depending on the platform, the implementation may call for the use of thread-local state or session variables or neither if the platform is inherently single-user for the duration of the application lifetime.
- Each client should provide a way for ‘manual’ breadcrumbs to be created which should have a type of 'manual’ set by default
- Each client should automatically capture suitable breadcrumbs (where possible) from different sources that will each be reported with a suitable breadcrumb 'type’ (from the set of available types)
- Breadcrumbs should be discarded oldest-first when the client capacity (maximum number of breadcrumbs to be sent) is reached
- The client capacity should be configurable, including setting to zero to prevent all breadcrumbs from being reported
- There should be a configuration option to disable collection of each separate source of automatic breadcrumbs
It should be possible to manipulate breadcrumbs in
beforeNotify callbacks (or equivalent) to provide full control over their content
- For breadcrumbs generated from log messages there should be a configuration option for the minimum log level to be recorded
- Every breadcrumb should have a name
- Every breadcrumb should have a timestamp
- Every breadcrumb should have a type
- Libraries should name fields, methods, and functions in a manner consistent with the best practices of the language. Camel casing, snake casing, no casing.
- Indentation should be consistent with the target platform. Two spaces, one tab, four spaces, if there’s no consensus pick one and add it to the contribution guidelines.
- Code conventions should be linted as a test phase and violations fixed. This is where tools like rubocop, pycodestyle, and clang-tidy can help.
- When a particular piece of the library is not needed by consumers to fulfil the documented interface, make it private. Expose as little as necessary to use library, which will make generated code-level documentation more useful for consumers, and easier to change the internals when necessary without concern for breaking existing applications.
The basic responsibility of a notifier library is to provide an interface for consumers to send error reports to Bugsnag. As such, each notifier should have a few core constructs, with property names consistent with the code conventions above and the specifications below:
- A standalone interface to configuration and sending notifications.
Applications should be able to have more than one client as different transport mechanisms or global diagnostic data may be needed for different app components/teams.
- A client should have an initializer which requires only an API key to get started sending error reports to Bugsnag.
- The client should have a method of sending reports to Bugsnag and that method should be named
notify should accept an error or exception structure if available, and handle the construction of the payload from the fields of the structure. If there is no equivalent of an error structure, notify should accept a name and message to represent an error.
- Notifying should provide the option of modifying a
Report which will be used to construct the request sent to the API. This modification should be implemented via callbacks or builders as is platform-appropriate.
- The client should have a method for queuing breadcrumbs to be attached to the next error reports sent, and this method should be called
Library defaults and behaviors for the Client. The structure does not necessarily need to be separate from Client itself.
- API key: The API key sent in the request payload if no report-specific value exists
- Application type (“app type”): A specialized type of the application, such as the worker queue or web framework used, like “rails”, “mailman”, “celery”.
- Auto notify: A boolean indicating whether the client captures unhandled/fatal errors
- Before Notify/Send: An ordered collection of callbacks used to modify or cancel reports before they are sent to Bugsnag. Each callback should provide a Report as an argument and allow cancellation by returning false or using a flag on report (if the language only supports passing by value).
- Delivery: How reports are sent to Bugsnag
- Async: If true, reports are sent on a background queue. This value should default to true for non-fatal errors.
- Proxy: Optional proxy settings used when connecting to “endpoint”. The transport mechanism should support at least basic authentication proxies by specifying a representation of host, port, user, and password.
- Notify release stages: A collection of strings representing which release stages should be used to send notifications. No notifications should be sent when the configured release stage is not present in “notify release stages”.
- Release stage: The release stage of the application, such as “production”, “development”, or “beta”. If the release stage can be known from the application environment, it should be populated automatically.
A modifiable representation of the error report which will be sent to Bugsnag.
- API key: Individual reports should be able to specify a custom API key
- Breadcrumbs: An ordered collection of Breadcrumbs
- Context: A string representation of what was happening in the app at the time of the error
- Error class: The name of the class, signal, or structure of the error
- Error message: The message or description provided by the error
- Grouping hash: A string identifier used to group error reports together
- Metadata: A mapping of strings to string and value pairs, where the value can be serialized as JSON. If there is no way to allow arbitrary serializable types, metadata can instead represented by a mapping of strings to string and string pairs.
- Stacktrace: An ordered collection of Stack Frames
- Severity: The severity of the error. The values must map to the allowed API values of “error”, “warning”, and “info”. The implementation should use an enumerated type if available within the programming language. If there is no equivalent to enums, the allowed values should be provided as namespaced constants.
- User: Information about the user affected by the crash. Should at least include fields for ID, name, and email, though adding other custom fields should also be supported.
In addition to consumer-provided data in the “metadata” field, additional information about the state of the system should be reported if available.
- All integrations should provide an “app” tab which includes metadata which is constant for the duration of the application’s run. At minimum, the following should be supported:
- Application type (type): A specialized type of the application, such as the worker queue or web framework used, like “rails”, “mailman”, or “celery”. This value should be specified in a client’s Configuration.
- Release stage (releaseStage): The release stage as specified in a client’s Configuration
- Version (version): The semantic version of the application running
- All integrations should provide a “device” tab which includes metadata which is constant for the duration that the device configuration or relevant at the time of the error. As many of the following fields should be supported as are possible:
- Free memory (freeMemory): A number of unused bytes of addressable memory on the drive running the application
- Host name (hostname): The local network name of the device
- Identifier (id): A unique identifier for this device, if it exists or is provided by the consumer
- Locale (locale): The locale in use, as represented by as IETF language tag. An example would be en-US.
- Manufacturer (manufacturer): The manufacturer of the device
- Model number (modelNumber): The model number of the device
- Operating System Name (osName): The name of the operating system in use
- Operating System Version (osVersion): The version of the operating system in use
- Time (time): The time on the device’s system clock at the time of the error
- Timezone (timezone): The timezone of the device’s system clock
- Total memory (totalMemory): The number of bytes of addressable memory on the drive running the application
- Web framework integration libraries should capture a “request” metadata tab with as many of the components below as possible (metadata key name in parens):
- Client IP (clientIp): The IP address of the connected client
- Headers (headers): Headers sent with the request as a string to string mapping
- HTTP method (httpMethod): The method used, such as GET,PUT, etc
- URL (url): The URL of the request
- Parameters (params): Parameters sent with the request as a hash
- Referer (referer): The HTTP referer
A representation of a frame in a stacktrace. Not all fields are relevant to every platform, and compiled languages may have additional fields for symbolication.
- File: The file containing the line which is being executed
- Line number: The line which is being executed
- Column number: The column which is being executed
- Method: The method which the line number is within
- In project: A boolean indicating whether this line is from a third-party library as opposed to the main application
- Code: A mapping between line numbers and strings containing the text of the lines of code where the error occurred.
A representation of a user action or application state change, including relevant metadata.
- Name: The name or label of the breadcrumb
- Timestamp: The time at which the event was recorded
- Type: The type of breadcrumb, enumerated from the possible values: navigation, request, log, process, user, state, error, manual
- Metadata: A string to string mapping of additional information about the breadcrumb