Redirect to a new page when Sentry catch error

Viewed 243

I am using Sentry and Angular.

When an error happens, and after it has been send, I would like to redirect users to an error page.

But I don't find a proper way.

  • Redirect in beforeSend is not ok as error has not been sent, and a redirection would break it.
  • implement a custom errorHandler is a good solution, but I do not want to manually recode the SentryErrorHandler I tried extending it, but since this service is injectable, doing a new
@Injectable({ providedIn: 'root' })
class SentryCustomErrorHandler extends SentryErrorHandler {
  constructor(@Inject('errorConfig') config: ErrorHandlerOptions) {
    super(config);
  }

  handleError(error: unknown): void {
    super.handleError(error);
    window.location.href = '/error';
  }
}

/**
 * Factory function that creates an instance of a preconfigured ErrorHandler provider.
 */
function createSentryCustomErrorHandler(config?: ErrorHandlerOptions): SentryErrorHandler {
  return new SentryCustomErrorHandler(config);
}

export { createSentryCustomErrorHandler, SentryCustomErrorHandler };

with another Injectable breaks angular.

I can't find a way to execute code after an error is sent...

1 Answers

sentry-error-handler.class.ts

import { ErrorHandler, Injectable, isDevMode } from '@angular/core';
import { BrowserOptions, captureException, configureScope, init } from '@sentry/browser';
import { User } from '@sentry/types';

export class SentryErrorHandlerOptions {
  private readonly _options: BrowserOptions;

  get options(): BrowserOptions {
    return this._options;
  }

  constructor(options: BrowserOptions) {
    this._options = options;
  }
}

@Injectable({ providedIn: 'root' })
export class SentryErrorHandlerService {
  setUser(sentryUser: User): void {
    configureScope(scope => {
      scope.setUser(sentryUser);
    });
  }
}

@Injectable()
export class SentryErrorHandler implements ErrorHandler {

  constructor(
    options: SentryErrorHandlerOptions
  ) {
  
    init({
      ...options.options,
      beforeSend: (event: any) => {
        return event;
      }
    });
  }

  handleError(error: any): void {
    this.captureException(error);
    console.error(error);
    window.location.href = '/error';
  }

  /**
   * Wrapper - do not send errors in DevMode (ng serve)
   * @param exception
   * @private
   */
  private captureException(exception: any): void {
    if (isDevMode()) {
      return;
    }
    captureException(exception);
  }
}

sentry-error-handler.module.ts

import { ErrorHandler, InjectionToken, ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SentryErrorHandler, SentryErrorHandlerOptions } from './sentry-error-handler.class';
import { BrowserOptions } from '@sentry/browser';

export const FOR_ROOT_SENTRY_OPTIONS_TOKEN = new InjectionToken<BrowserOptions>('forRoot() SentryErrorHandler options');

@NgModule({
  imports: [CommonModule]
})
export class SentryErrorHandlerModule {
  constructor(@Optional() @SkipSelf() parentModule: SentryErrorHandlerModule) {
    if (parentModule) {
      throw new Error(
        'SentryErrorHandlerModule is already loaded. Import it in the AppModule only using .forRoot({...})'
      );
    }
  }

  /**
   * This module have to be initialized by using forRoot()
   * @param options
   */
  static forRoot(options: BrowserOptions): ModuleWithProviders<SentryErrorHandlerModule> {
    return {
      ngModule: SentryErrorHandlerModule,
      providers: [
        // Translate raw OPTIONS to SentryErrorHandlerOptions,
        // so later we can inject it into factory function
        {
          provide: FOR_ROOT_SENTRY_OPTIONS_TOKEN,
          useValue: options
        },
        {
          provide: ErrorHandler,
          useClass: SentryErrorHandler
        }
      ]
    };
  }
}

And inside app.module.ts

SentryErrorHandlerModule.forRoot({
          dsn: 'DSN FOR YOUR ACCOUNT',
          environment: environment.envName,
          release: environment.version
        })
Related