Improve the way of Logging in Angular Application

Angular is a widely popular framework and developers who have worked with the frontend are very familiar with console for debugging their code. Due to continuous development and integration, it is quite difficult and time-consuming to manage to comment out console logs from the application so that it does not go into release or production to the non-developer.

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.

We can enhance this approach further with log4j or ngx-logger, and provide their functions as value.

Conclusion

With the help of Approach 3, we can solve the problem with no additional overhead to the developer and kept the standard console logging functionality the same. Additionally, we can ensure that we are not logging console output in release or production.

Software Engineer @DeutscheTelekom