Ionic Global Error Handler
During development (when using ionic serve), Ionic replaces the default Angular error handler with its own implementation which displays the error details in a page overlay instead of just printing them out to console. However, as soon as you build the app for the mobile device, you're back to the default error handler.
By intercepting all outputs to console you could provide an easy access to these errors even on the device. But since we are talking about unhandled errors, you might want to give them even more attention, e.g. automatically report them to your analytics service. If you implement your own error handler and use it to replace the default one, you can achieve just that.
The basics are simple. You implement or extend Angular's ErrorHandler
class. I chose the latter to keep the default logging to console intact. There's only a single method to implement: handleError
.
import { ErrorHandler, Injectable } from '@angular/core';
@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
constructor() {
super();
}
handleError(error: any): void {
super.handleError(error);
}
}
We haven't added any custom functionality yet, but let's first register our handler instead of the one from Ionic. Find the following entry in the AppModule
's providers
list:
{provide: ErrorHandler, useClass: IonicErrorHandler}
And replace it with your own:
{provide: ErrorHandler, useClass: GlobalErrorHandler}
You'll lose the custom error overlay during development. I don't mind that. But if you do, you can extend IonicErrorHandler
instead.
Let's get back to implementing our error handler now. I chose Google Analytics for reporting the errors:
import { GoogleAnalytics } from '@ionic-native/google-analytics';
import { ErrorHandler, Injectable } from '@angular/core';
@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
constructor(private ga: GoogleAnalytics) {
super();
}
handleError(error: any): void {
super.handleError(error);
this.ga.trackException(JSON.serialize(error), false);
}
}
I described how to set up and initialize Google Analytics in Ionic in a previous blog post.
This simple implementation has some flaws, though:
Most of
Error
object properties aren't serialized by default. To log more information, you should pass your own replacer function toJSON.serialize()
.When the error is thrown from a promise, attempting to serialize the resulting
Error
object will likely result in an error because of a circular JSON structure. Since the thrownError
object is also just a wrapper for the original error details I'm really interested in, I decided to only report that and avoid the serialization issue:if (error.rejection) { error = error.rejection; }
Google Analytics restricts the maximum length of a tracked exception. If that length is exceeded, nothing gets logged. To deal with that issue, I decided to parse the relevant information and only report that.
export interface ParsedError { message: string; status?: number; stack?: string; } parse(error: any): ParsedError { // get best available error message let parsedError: ParsedError = { message: error.message ? error.message as string : error.toString() }; // include HTTP status code if (error.status != null) { parsedError.status = error.status; } // include stack trace if (error.stack != null) { parsedError.stack = error.stack; } return parsedError; }
With all that in place, here's the resulting error handler implementation:
handleError(error: any): void {
super.handleError(error);
// unroll errors from promises
if (error.rejection) {
error = error.rejection;
}
let parsedError = this.parse(error);
this.ga.trackException(JSON.serialize(parsedError), false);
}
And I don't need to use a custom replacer function for JSON serialization any more, because I'm now serializing my custom ParsedError
object instead of a JavaScript Error
object.