As part of this article we examine how we can setup proactive monitoring using Angular 12 and Datadog, by following two different approaches. The implementation should be similar for React and Vue and of course this can be adjusted to use Sentry or any other Datadog alternative.
Datadog is mostly popular as a cloud and infrastructure monitoring but it can be also used as a centralized logging platform. Personally I find that you get better application insights when you combine infrastructure and application logs into one platform.
Before getting started, you will need to install @datadog/browser-logs.
npm i @datadog/browser-logs
Using forwardErrorsToLogs
The easier way to get started is to initialize Datadog with forwardErrorsToLogs enabled for production, or any non-dev environment. With this approach you will be forwarding error logs to datadog, on top of outputting the errors in console.
As you can tell this approach is not Angular specific. Nevertheless here is how this can be done in any Angular project.
1import { datadogLogs as datadog } from '@datadog/browser-logs'; 2 3if (environment.production) { 4 enableProdMode(); 5 6 datadog.init({ 7 clientToken: environment.datadog.clientToken, 8 site: environment.datadog.site, 9 service: environment.datadog.service,10 forwardErrorsToLogs: true,11 sampleRate: 100,12 });13}
However, there is a main downside with this approach — there is no control over to what happens behind the scenes. For example, it is not possible to forward a warning message, or include extra details in context or even scrub sensitive data.
Also to make that work, the datadog library overwrites console.error
which might have side effects if any other package does the same.
Using Logger service
Another approach is to wrap Datadog into a Logger service which we can inject later on via Dependency Injection. There are a few benefits with the approach. Firstly, we can now forward any type of messages, not only errors. Secondly, the Datadog dependency is well hidden from the rest of the application. Last, but not least, we can provide a default implementation when Datadog configuration is not available.
1import { Injectable } from '@angular/core'; 2import { datadogLogs as datadog } from '@datadog/browser-logs'; 3import { environment } from './../environments/environment'; 4 5@Injectable({ 6 providedIn: 'root', 7}) 8export class Logger { 9 private initialized = false;10 constructor() {11 if (!environment?.datadog) {12 return;13 }14 15 datadog.init({16 clientToken: environment.datadog.clientToken,17 site: environment.datadog.site,18 service: environment.datadog.service,19 forwardErrorsToLogs: true,20 sampleRate: 100,21 });22 this.initialized = true;23 }24 25 public debug(message: string, context?: { [x: string]: any }): void {26 if (this.initialized) {27 datadog.logger.debug(message, context);28 }29 }30 31 public info(message: string, context?: { [x: string]: any }): void {32 if (this.initialized) {33 datadog.logger.info(message, context);34 }35 }36 37 public warn(message: string, context?: { [x: string]: any }): void {38 if (this.initialized) {39 datadog.logger.warn(message, context);40 }41 }42 43 public error(message: string, context?: { [x: string]: any }): void {44 if (this.initialized) {45 datadog.logger.error(message, context);46 }47 }48}
The same service can be used in a custom error handler that intercepts error handling to capture errors and forward them to Datadog. Note that the default implementation of ErrorHandler prints error messages to the console.
1import { ErrorHandler, Injectable, isDevMode } from '@angular/core'; 2import { Logger } from './logger.service'; 3 4@Injectable({ 5 providedIn: 'root', 6}) 7export class MyErrorHandler implements ErrorHandler { 8 constructor(private logger: Logger) {} 9 10 public handleError(error: any): void {11 if (isDevMode()) {12 console.log(error);13 }14 15 this.logger.error(error);16 }17}
With the above in place, you should be able to forward custom error messages to Datadog. As mentioned before, the Logger service can be extended to include environment and other context details that are helpful when troubleshooting.
Conclusion
As part of this article we covered two ways of integrating your Angular project with Datadog. Doing so it will allow you to view errors as they happen in Datadog. Combine that with infrastructure/API logs and automated notifications or metrics and you should a basic but yet robust system for proactive monitoring.
Make sure to follow me on dev.to, Medium or Twitter to read more about Angular and other dev topics.