In my last post, I looked at how you can use HTTP Interceptors to attach and refresh authorization tokens. In this post, we are going to use the same HTTP Interceptor to catch HTTP errors – 500, 401, 400 etc. – and logging them remotely. It is important to analyze how your angular application is interacting with your APIs and other external API, so that you can improve the product itself. Collecting HTTP Errors at an angular app level could provide huge insight to you and your development team.

So, without further ado:

Creating a HTTP Interceptor

Let’s first start by creating a HTTP Interceptor class – HttpLogInterceptor, then adding it to our module (app.module.ts).

ng generate class http-log-interceptor

Then, open the http interceptor and make the following modifications to it:

@Injectable()
export class HttpLogInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {}
}

First, we made the class injectable. Then, we implemented the HTTP Interceptor Interface by adding implements HttpInterceptor Interface. And by adding the intercept method – the method that will intercept and return the modified requests. Most of the action will take place inside this method. Next, we need to add the newly created HTTP interceptor class to the list of providers, in our module:

@NgModule({
  imports: [CommonModule, MatDialogModule],
  declarations: [],
  providers: [
    AuthService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpLogInterceptor,
      multi: true
    }
  ]
})

Catching and Logging HTTP Errors

Inside our intercept method of our HttpLogInterceptor class, we need to take the HTTP request, then pipe it and catchErrors, then return the errors to the originator using throwError operator of RXJS.

return next.handle(req).pipe(
  catchError(httpError => {
    // log your error here
    loggerService.logger(httpError).subscribe(); // re throw the error so it can be caught by the subscriber
    return throwError(httpError);
  })
);

Next, we need to import our logger service into our interceptor. To avoid the Cyclic Dependency Injection error, we will inject Injector into our class. Then, we will use to assign our logger service to a variable and then we can use it as normal.

const loggerService = this.inject.get(LoggerService);

In our service, we need a logger method, which will call and pass the HttpErrorResponse error to our logger. Here, will subscribe to the logging method, inside our logger service. Since, the results of logging action don’t really matter to the end user, we will proceed and rethrow the original error.

loggerService.logger(httpError).subscribe();

// re throw the error so it can be caught by the subscriber
return throwError(httpError);

What this allows you to do, is to initiate a logging action on the background without affecting the user experience while waiting for logging to be completed. This is because the logging request will be carried asynchronously. This won’t prevent the execution of the next line of code after being called.

Logging Error Method

Now, let’s create a logger method in our logger service. We will imaginatively call it “logger”.

logger(error: HttpErrorResponse): Observable<boolean> {}

Here, we will send the error details to the logging server:

logger(error: HttpErrorResponse): Observable<boolean> {
    const url = 'URL TO Loggger';

    return this.http
          .get(url, {
            params: {
              url: error.url,
              content: error.error,
              status: error.status.toString()
            }
          })
          .pipe(
            map(_ => {
              return true;
            })
          );
}

In this method, we take the HTTP response error object and convert the to the URL parameters of our get http request. You could also use HTTP Post method, to send the data to the logging server, with the error data as part of the body.