Improve the way of Logging in Angular Application
By: Ashish Gopal Hattimare
Tags: Javascript Tips, Angular, Angular Services, Debugging, Console

How can we make Angular help us achieve this goal?

Angular provides us the functionality to add Services into our application so that we can reuse them via injecting them into our components.

We can utilize the benefit of Services and manage our browser console with it so that we do not need to maintain our debug logs everywhere in our application.

Approach 1: Isolate your console logging to a Service

import { Injectable } from '@angular/core';

@Injectable({

providedIn: 'root'

})

export class LogService {



constructor() {



}

trace(...data: any[]): void {

console.trace(data);

}

log(...data: any[]): void {

console.log(data);

}

}

Fig 1.1 logService.log() executed from AppComponent

logService.log('console executed from AppComponent');

The above code is easy to comprehend and code, but it does not solve our problem identifying where this logging was initiated. From Fig 1.1, we can see the log was initiated from AppComponent, but it shows log.service.ts:xx in the console window. Hence, we have lost the source of logging 😲

Approach 1.1: Use logService.trace()

This can be used to console the entire trace along with the source of logging 😈. This might be the best approach to debug and possibility a Hack, but it adds redundant or unnecessary console logs as well which are not always required 🤦

Approach 2: Enhance the above Approach to include a parameter in helper functions

import { Injectable } from '@angular/core';

@Injectable({

providedIn: 'root'

})

export class LogService {



constructor() {



}

trace(source: string, ...data: any[]): void {

console.trace(data);

}

log(source: string, ...data: any[]): void {

console.log(data);

}

}

Fig 1.2 logService.log() executed from AppComponent with additional parameter

logService.log('AppComponent','console executed from AppComponent');

Well Done!!! 🥳 Now, we have improved logging by adding a parameter to our helper function, but have also introduced an additional overhead 😟 on the developer side to pass a valid source string as a parameter.

“If you are making something and is difficult for other to adapt to it, then it will not be used by anyone”

Besides this, it is still showing log.service.ts:xx in the console window 😕

Approach 3: Utilise the Javascript ‘Function as first-class citizen’ theory into practice 👨🏼‍💻

We utilize the above property of Javascript and pass console.log function as a value 😎

import { Injectable } from '@angular/core';



interface IConsole {

log: any;

}



@Injectable({

providedIn: 'root'

})

export class LogService {

console: IConsole;



constructor() {



this.console = LogService.isActiveLogging() ?

LogService.ActiveLogging() :

LogService.NoLogging();

}



private static isActiveLogging(): boolean {

...

}

private static ActiveLogging(): IConsole {

return {

log: console.log

};

}

private static NoLogging(): IConsole {

const NO_OPERATION = () => {};

return {

log: NO_OPERATION

};

}

}

In the above approach, we are passing the console.log function as a value if we want to enable logging. Otherwise, we are passing an empty function as a value to disable logging. Besides this, we are only exposing our IConsole interface as a helper contract to LogService.console functionality.

Fig 1.3 logService.console.log() executed from AppComponent

logService.console.log('console executed from AppComponent');

Now, we can see in Fig 1.3 that we are getting app.component.ts:xx on the console window and we are not passing any additional parameter as overhead to identify the source. Moreover, we are still utilizing the default console to debug logs.