import {Injectable} from 'angular2/di'; import {isPresent, isBlank, print, BaseException} from 'angular2/src/facade/lang'; import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection'; class _ArrayLogger { res: any[] = []; log(s: any): void { this.res.push(s); } logGroup(s: any): void { this.res.push(s); } logGroupEnd(){}; } /** * Provides a hook for centralized exception handling. * * The default implementation of `ExceptionHandler` prints error messages to the `Console`. To * intercept error handling, * write a custom exception handler that replaces this default as appropriate for your app. * * # Example * * ```javascript * * class MyExceptionHandler implements ExceptionHandler { * call(error, stackTrace = null, reason = null) { * // do something with the exception * } * } * * bootstrap(MyApp, [bind(ExceptionHandler).toClass(MyExceptionHandler)]) * * ``` */ @Injectable() export class ExceptionHandler { constructor(private logger: any, private rethrowException: boolean = true) {} static exceptionToString(exception: any, stackTrace: any = null, reason: string = null): string { var l = new _ArrayLogger(); var e = new ExceptionHandler(l, false); e.call(exception, stackTrace, reason); return l.res.join("\n"); } call(exception: any, stackTrace: any = null, reason: string = null): void { var originalException = this._findOriginalException(exception); var originalStack = this._findOriginalStack(exception); var context = this._findContext(exception); this.logger.logGroup(`EXCEPTION: ${exception}`); if (isPresent(stackTrace) && isBlank(originalStack)) { this.logger.log("STACKTRACE:"); this.logger.log(this._longStackTrace(stackTrace)); } if (isPresent(reason)) { this.logger.log(`REASON: ${reason}`); } if (isPresent(originalException)) { this.logger.log(`ORIGINAL EXCEPTION: ${originalException}`); } if (isPresent(originalStack)) { this.logger.log("ORIGINAL STACKTRACE:"); this.logger.log(this._longStackTrace(originalStack)); } if (isPresent(context)) { this.logger.log("ERROR CONTEXT:"); this.logger.log(context); } this.logger.logGroupEnd(); // We rethrow exceptions, so operations like 'bootstrap' will result in an error // when an exception happens. If we do not rethrow, bootstrap will always succeed. if (this.rethrowException) throw exception; } _longStackTrace(stackTrace: any): any { return isListLikeIterable(stackTrace) ? (stackTrace).join("\n\n-----async gap-----\n") : stackTrace; } _findContext(exception: any): any { try { if (!(exception instanceof BaseException)) return null; return isPresent(exception.context) ? exception.context : this._findContext(exception.originalException); } catch (e) { // exception.context can throw an exception. if it happens, we ignore the context. return null; } } _findOriginalException(exception: any): any { if (!(exception instanceof BaseException)) return null; var e = exception.originalException; while (e instanceof BaseException && isPresent(e.originalException)) { e = e.originalException; } return e; } _findOriginalStack(exception: any): any { if (!(exception instanceof BaseException)) return null; var e = exception; var stack = exception.originalStack; while (e instanceof BaseException && isPresent(e.originalException)) { e = e.originalException; if (e instanceof BaseException && isPresent(e.originalException)) { stack = e.originalStack; } } return stack; } }