Ship internal tools in hours, not weeks. Real auth, users, jobs, audit logs, and cohesive UI included. Early access $249 $499 → [Get it now]

How to monitor Angular apps in production

Monitoring an Angular application in production is essential for identifying runtime exceptions, performance bottlenecks, and network failures that your users encounter.
As the creator of CoreUI, with over 25 years of experience in software development, I have built and maintained dozens of enterprise-grade applications where real-time monitoring was the difference between a quick fix and a day-long outage.
From my expertise, the most efficient and modern solution is to implement a centralized ErrorHandler combined with a custom telemetry service that pushes data to your preferred logging backend.
This approach ensures that every unhandled exception is captured automatically without cluttering your component logic.

Implement a custom ErrorHandler class to catch and report all runtime exceptions across your entire Angular application.

import { ErrorHandler, Injectable, Injector } from '@angular/core'
import { TelemetryService } from './services/telemetry.service'

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  constructor(private injector: Injector) {}

  handleError(error: any): void {
    const telemetry = this.injector.get(TelemetryService)
    const message = error.message ? error.message : error.toString()
    const stack = error instanceof Error ? error.stack : ''

    // Log to console for local debugging
    console.error('Production Error:', message)

    // Send to your monitoring service
    telemetry.logError({
      message,
      stack,
      url: window.location.href,
      timestamp: new Date().toISOString()
    })
  }
}

This code defines a GlobalErrorHandler that overrides Angular’s default error handling behavior. By using the Injector to manually retrieve the TelemetryService, we avoid circular dependency issues that often occur when injecting services directly into an ErrorHandler. The handleError method extracts the error message and stack trace, then forwards them to a dedicated logging service along with the current URL and a timestamp.

1. Setting Up the Telemetry Service

To effectively monitor your application, you need a robust service that communicates with your logging infrastructure. This service should handle the actual transmission of data, whether you are using a third-party tool like Sentry or your own internal API.

import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { environment } from '../environments/environment'

@Injectable({
  providedIn: 'root'
})
export class TelemetryService {
  constructor(private http: HttpClient) {}

  logError(errorData: any) {
    if (environment.production) {
      // Only send to server in production environments
      this.http.post(`${environment.apiBase}/logs/error`, errorData).subscribe({
        error: (err) => console.warn('Failed to send telemetry:', err)
      })
    }
  }

  logMetric(name: string, value: number) {
    if (environment.production) {
      this.http.post(`${environment.apiBase}/logs/metric`, { name, value }).subscribe()
    }
  }
}

This service acts as the gateway for all monitoring data. It checks the environment.production flag to ensure that your development logs don’t flood your production database.

2. Registering the Custom Error Handler

Once your handler is created, you must tell Angular to use it instead of the default one. This is done in your AppModule or standalone configuration.

import { NgModule, ErrorHandler } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { GlobalErrorHandler } from './global-error-handler'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [
    {
      provide: ErrorHandler,
      useClass: GlobalErrorHandler
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

By providing ErrorHandler with your GlobalErrorHandler, you ensure that every try-catch block you missed will be caught by this top-level provider. This is a pattern we use extensively in the Angular Dashboard Template to ensure reliability.

3. Monitoring HTTP Request Failures

While the ErrorHandler catches logic errors, an HttpInterceptor is the best way to monitor network health and API failures.

import { Injectable } from '@angular/core'
import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http'
import { catchError, throwError } from 'rxjs'
import { TelemetryService } from './services/telemetry.service'

@Injectable()
export class MonitoringInterceptor implements HttpInterceptor {
  constructor(private telemetry: TelemetryService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    return next.handle(req).pipe(
      catchError((error: HttpErrorResponse) => {
        this.telemetry.logError({
          type: 'HTTP_ERROR',
          status: error.status,
          url: error.url,
          message: error.message
        })
        return throwError(() => error)
      })
    )
  }
}

This interceptor monitors every outgoing request. If a request fails (e.g., 404 or 500), it automatically logs the failure details. This is crucial for identifying backend outages and API regressions in production.

4. Tracking Performance Metrics

Monitoring isn’t just about errors; it’s also about speed. You can track how long certain operations take or monitor Core Web Vitals.

import { Injectable } from '@angular/core'
import { TelemetryService } from './services/telemetry.service'

@Injectable({
  providedIn: 'root'
})
export class PerformanceService {
  constructor(private telemetry: TelemetryService) {}

  measureAction(name: string, action: () => void) {
    const start = performance.now()
    action()
    const end = performance.now()
    this.telemetry.logMetric(name, end - start)
  }
}

Using the performance.now() API allows you to measure the execution time of critical functions. This data helps you identify which parts of your UI are sluggish and need optimization.

5. Visualizing Status with CoreUI Components

When a production error occurs, you might want to notify the user gracefully. Using the CoreUI Alert component is a great way to handle this.

<!-- app.component.html -->
<c-alert color="danger" *ngIf="hasError" dismissible>
  <strong>Error:</strong> Something went wrong. Our team has been notified.
</c-alert>
// app.component.ts
import { Component } from '@angular/core'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  hasError = false
  
  triggerErrorState() {
    this.hasError = true
  }
}

In production, you should never show raw error details to the user. Instead, use a clean UI component to inform them that the issue is being handled.

6. Integrating Environmental Variables

For security and flexibility, always manage your monitoring endpoints via environment variables.

// src/environments/environment.prod.ts
export const environment = {
  production: true,
  apiBase: 'https://api.yourdomain.com',
  monitoringKey: 'PROD_KEY_12345'
}

This prevents leaking sensitive development keys into your production build. Angular’s environment file system swaps environment.ts for environment.prod.ts automatically during a production build — no runtime conditionals needed beyond environment.production checks inside your services.

Best Practice Note:

Always ensure that your telemetry service is non-blocking. Use subscribe without waiting for the response to ensure that the monitoring logic doesn’t slow down the user experience. In CoreUI components, we prioritize performance by keeping background tasks like logging completely decoupled from the main UI thread. Additionally, be mindful of PII (Personally Identifiable Information); never log passwords, tokens, or sensitive user data to your monitoring backend.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author