Skip to content

Commit c44ac4b

Browse files
arturovtLms24
authored andcommitted
fix(angular): remove afterSendEvent listener once root injector is destroyed
In this commit, we added cleanup logic to handle the removal of `afterSendEvent`, which is set up within the Angular error handler. This fixes memory leaks that occur when the event is still being handled after the root view is removed.
1 parent d629991 commit c44ac4b

File tree

1 file changed

+15
-11
lines changed

1 file changed

+15
-11
lines changed

packages/angular/src/errorhandler.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HttpErrorResponse } from '@angular/common/http';
2-
import type { ErrorHandler as AngularErrorHandler } from '@angular/core';
2+
import type { ErrorHandler as AngularErrorHandler, OnDestroy } from '@angular/core';
33
import { Inject, Injectable } from '@angular/core';
44
import * as Sentry from '@sentry/browser';
55
import type { ReportDialogOptions } from '@sentry/browser';
@@ -81,21 +81,28 @@ function isErrorOrErrorLikeObject(value: unknown): value is Error {
8181
* Implementation of Angular's ErrorHandler provider that can be used as a drop-in replacement for the stock one.
8282
*/
8383
@Injectable({ providedIn: 'root' })
84-
class SentryErrorHandler implements AngularErrorHandler {
84+
class SentryErrorHandler implements AngularErrorHandler, OnDestroy {
8585
protected readonly _options: ErrorHandlerOptions;
8686

87-
/* indicates if we already registered our the afterSendEvent handler */
88-
private _registeredAfterSendEventHandler;
87+
/** The cleanup function is executed when the injector is destroyed. */
88+
private _removeAfterSendEventListener?: VoidFunction;
8989

9090
public constructor(@Inject('errorHandlerOptions') options?: ErrorHandlerOptions) {
91-
this._registeredAfterSendEventHandler = false;
92-
9391
this._options = {
9492
logErrors: true,
9593
...options,
9694
};
9795
}
9896

97+
/**
98+
* Method executed when the injector is destroyed.
99+
*/
100+
public ngOnDestroy(): void {
101+
if (this._removeAfterSendEventListener) {
102+
this._removeAfterSendEventListener();
103+
}
104+
}
105+
99106
/**
100107
* Method called for every value captured through the ErrorHandler
101108
*/
@@ -119,17 +126,14 @@ class SentryErrorHandler implements AngularErrorHandler {
119126
if (this._options.showDialog) {
120127
const client = Sentry.getClient();
121128

122-
if (client && !this._registeredAfterSendEventHandler) {
123-
client.on('afterSendEvent', (event: Event) => {
129+
if (client && !this._removeAfterSendEventListener) {
130+
this._removeAfterSendEventListener = client.on('afterSendEvent', (event: Event) => {
124131
if (!event.type && event.event_id) {
125132
runOutsideAngular(() => {
126133
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id! });
127134
});
128135
}
129136
});
130-
131-
// We only want to register this hook once in the lifetime of the error handler
132-
this._registeredAfterSendEventHandler = true;
133137
} else if (!client) {
134138
runOutsideAngular(() => {
135139
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId });

0 commit comments

Comments
 (0)