Customizing error reports

Add diagnostic data to every error report, or conditionally adjust error reports.

This documentation is for version 4 of the BugSnag Android notifier. We recommend upgrading to the latest release using our Upgrade guide. Documentation for the current release can be found here.

Adding callbacks

If you’d like to add diagnostic data to reports, or adjust error reports conditionally, you can use a before notify callback, which will be run immediately after an error is captured or reported:

Bugsnag.beforeNotify(new BeforeNotify() {
    @Override
    public boolean run(Error error) {
        // Attach customer information to every error report
        error.addToTab("account", "name", "Acme Co.");
        error.addToTab("account", "paying_customer", true);

        // Return `false` if you'd like to stop this error being reported
        return true;
    }
});
Bugsnag.beforeNotify {
    it.addToTab("account", "name", "Acme Co.")
    it.addToTab("account", "paying_customer", true)

    // Return `false` if you'd like to stop this error being reported
    true
}

The callback gives you access to the error object, so you can inspect and modify the error report which is about to be sent to BugSnag.

Note: Before notify callbacks do not run when capturing crashes from native C/C++ code, as the system terminates immediately. To include additional information in native crash reports, see Pre-delivery callbacks.

Pre-delivery callbacks

Use BeforeSend callbacks to modify or cancel error reports immediately prior to report delivery. Report delivery may not be during the same app launch as the error occurred, as the app may be offline when the error occurs or terminate immediately in the case of crashes from native C/C++ code. Avoid attaching additional information to a report that does not relate to the crashed session.

Configuration config = new Configuration("your-api-key-here");
config.beforeSend(new BeforeSend() {
    @Override
    public boolean run(Report report) {
        // Attach customer information to every error report
        Error error = report.getError();
        error.addToTab("account", "name", "Acme Co.");
        error.addToTab("account", "paying_customer", true);

        // Return `false` if you'd like to stop this error being reported
        return true;
    }
});
Bugsnag.init(this, config);
val config = Configuration("your-api-key-here")
config.beforeSend {
    // Attach customer information to every error report
    val error = it.error
    error.addToTab("account", "name", "Acme Co.")
    error.addToTab("account", "paying_customer", true)

    // Return `false` if you'd like to stop this error being reported
    true
}
Bugsnag.init(this, config)

Disabling error reporting

If you wish to disable error reporting, you can return false within config.beforeSend. This would allow for users to opt out of sending error reports, for example:

Configuration config = new Configuration("your-api-key-here");
config.beforeSend(new BeforeSend() {
    @Override
    public boolean run(Report report) {
        return false; // no error report will be sent
    }
});
Bugsnag.init(this, config);
val config = Configuration("your-api-key-here")
config.beforeSend { false } // no error report will be sent
Bugsnag.init(this, config)

The error object

The following properties and methods are available on the Error object, the representation of error information available in before notify callbacks.

addToTab

Sets a piece of information to be displayed on the BugSnag dashboard. The first argument is the tab name, the second argument is the key for the data, and the third argument is used as the value.

error.addToTab("user", "role", "Administrator");
error.addToTab("user", "role", "Administrator")

getContext

Returns the context of the error. The context is a summary what what was occurring in the application at the time of the crash, if available, such as the visible activity.

getException

Returns a BugsnagException object representing the event that caused the report.

getExceptionName

Returns the full name of the Throwable.

getMetaData

Returns the attached exception metadata, which can be modified to add or remove fields.

MetaData metadata = error.getMetaData();
metadata.clearTab("user");
val metadata = error.metaData
metadata.clearTab("user")

getStackTrace

Returns the Throwable‘s array of StackTraceElements.

setContext

Sets the context of the error. The context is a summary what what was occurring in the application at the time of the crash, if available, such as the visible activity.

error.setContext("Pause menu");
error.context = "Pause menu"

setGroupingHash

Sets the groupingHash used by BugSnag to manually override the default grouping technique. This option is not recommended, and should be used carefully when used.

Any errors that are sent to BugSnag, that have the same groupingHash will be grouped as one. As the name implies, this option accepts a hash of sorts.

// ... generate the hash
String groupingHash = "f8803769f3e293dfcabdb6dec5100b8c52c6ae6b";
error.setGroupingHash(groupingHash);
// ... generate the hash
val groupingHash = "f8803769f3e293dfcabdb6dec5100b8c52c6ae6b"
error.setGroupingHash(groupingHash)

setSeverity

Overrides the severity of the error. Valid severities are error, warning and info.

error.setSeverity("warning");
error.severity = Severity.WARNING

setUser

Sets the user associated with the error report.

error.setUser("123", "user@example.com", "Bob Johnson");
error.setUser("123", "user@example.com", "Bob Johnson")

Modifying the stack trace

It is possible to modify the stack trace that is associated with the error report in the following way:

Create a recursive function which loops copies each stack trace element and sets it back to the Throwable object. Any modifications to the stack trace can be done within this loop

private void alterStacktrace(Throwable t) {
    StackTraceElement[] trace = t.getStackTrace();
    List<StackTraceElement> newTrace = new ArrayList<>();

    for (StackTraceElement frame : trace) {
        // YOUR CODE HERE
        newTrace.add(new StackTraceElement(
                frame.getClassName(),
                frame.getMethodName(),
                frame.getFileName(),
                frame.getLineNumber()));
    }

    t.setStackTrace(newTrace.toArray(new StackTraceElement[newTrace.size()]));

    if (t.getCause() != null) {
        alterStacktrace(t.getCause());
    }
}
private fun alterStacktrace(t: Throwable) {
    val trace = t.stackTrace

    val newTrace = trace.map {
        // YOUR CODE HERE
        StackTraceElement(
            it.className,
            it.methodName,
            it.fileName,
            it.lineNumber)
    }

    t.stackTrace = newTrace.toTypedArray()
    t.cause?.let { alterStacktrace(it) }
}

Then call this new function from the BeforeNotify function

alterStacktrace(error.getException());
alterStacktrace(it.exception)

The report object

The following methods are available on the Report object, the representation of a report on the BugSnag dashboard in callbacks.

getError

The Error object associated with this report

Error error = report.getError();
error.addToTab("user", "role", "Administrator");
report.error.addToTab("user", "role", "Administrator")

setApiKey

Override the API key which is used to deliver the report.

report.setApiKey("my-other-api-key");
report.apiKey = "my-other-api-key"

Reporting StrictMode violations

If you have enabled StrictMode in your development builds with the penaltyDeath option, then BugSnag will automatically detect StrictMode exceptions. Any reports for these errors will contain a human readable description of the policy violation (e.g. ActivityLeak) in their metadata.

If you have not already setup StrictMode, you can do so by entering the following code in your Application class. It is strongly recommended that you only enable StrictMode for non-release builds.

if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
               .detectDiskReads()
               .detectDiskWrites()
               .detectNetwork()
               .penaltyDeath()
               .build());

    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
               .detectLeakedSqlLiteObjects()
               .detectLeakedClosableObjects()
               .penaltyDeath()
               .build());
}
if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()
                .penaltyDeath()
                .build())

    StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .penaltyDeath()
            .build())
}