-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy patherrorhandler.ts
120 lines (100 loc) · 4.25 KB
/
errorhandler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorHandler as AngularErrorHandler, Inject, Injectable } from '@angular/core';
import * as Sentry from '@sentry/browser';
import { runOutsideAngular } from './zone';
/**
* Options used to configure the behavior of the Angular ErrorHandler.
*/
export interface ErrorHandlerOptions {
logErrors?: boolean;
showDialog?: boolean;
dialogOptions?: Sentry.ReportDialogOptions;
/**
* Custom implementation of error extraction from the raw value captured by the Angular.
* @param error Value captured by Angular's ErrorHandler provider
* @param defaultExtractor Default implementation that can be used as the fallback in case of custom implementation
*/
extractor?(error: unknown, defaultExtractor: (error: unknown) => unknown): unknown;
}
/**
* Implementation of Angular's ErrorHandler provider that can be used as a drop-in replacement for the stock one.
*/
@Injectable({ providedIn: 'root' })
class SentryErrorHandler implements AngularErrorHandler {
protected readonly _options: ErrorHandlerOptions;
public constructor(@Inject('errorHandlerOptions') options?: ErrorHandlerOptions) {
this._options = {
logErrors: true,
...options,
};
}
/**
* Method called for every value captured through the ErrorHandler
*/
public handleError(error: unknown): void {
const extractedError = this._extractError(error) || 'Handled unknown error';
// Capture handled exception and send it to Sentry.
const eventId = runOutsideAngular(() => Sentry.captureException(extractedError));
// When in development mode, log the error to console for immediate feedback.
if (this._options.logErrors) {
// eslint-disable-next-line no-console
console.error(extractedError);
}
// Optionally show user dialog to provide details on what happened.
if (this._options.showDialog) {
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId });
}
}
/**
* Used to pull a desired value that will be used to capture an event out of the raw value captured by ErrorHandler.
*/
protected _extractError(error: unknown): unknown {
// Allow custom overrides of extracting function
if (this._options.extractor) {
const defaultExtractor = this._defaultExtractor.bind(this);
return this._options.extractor(error, defaultExtractor);
}
return this._defaultExtractor(error);
}
/**
* Default implementation of error extraction that handles default error wrapping, HTTP responses, ErrorEvent and few other known cases.
*/
protected _defaultExtractor(errorCandidate: unknown): unknown {
let error = errorCandidate;
// Try to unwrap zone.js error.
// https://github.com/angular/angular/blob/master/packages/core/src/util/errors.ts
if (error && (error as { ngOriginalError: Error }).ngOriginalError) {
error = (error as { ngOriginalError: Error }).ngOriginalError;
}
// We can handle messages and Error objects directly.
if (typeof error === 'string' || error instanceof Error) {
return error;
}
// If it's http module error, extract as much information from it as we can.
if (error instanceof HttpErrorResponse) {
// The `error` property of http exception can be either an `Error` object, which we can use directly...
if (error.error instanceof Error) {
return error.error;
}
// ... or an`ErrorEvent`, which can provide us with the message but no stack...
if (error.error instanceof ErrorEvent && error.error.message) {
return error.error.message;
}
// ...or the request body itself, which we can use as a message instead.
if (typeof error.error === 'string') {
return `Server returned code ${error.status} with body "${error.error}"`;
}
// If we don't have any detailed information, fallback to the request message itself.
return error.message;
}
// Nothing was extracted, fallback to default error message.
return null;
}
}
/**
* Factory function that creates an instance of a preconfigured ErrorHandler provider.
*/
function createErrorHandler(config?: ErrorHandlerOptions): SentryErrorHandler {
return new SentryErrorHandler(config);
}
export { createErrorHandler, SentryErrorHandler };