BlackHoleGalaxy
BlackHoleGalaxy

Reputation: 9662

Angular console log only on development environment

In our Angular app (made with Angular CLI) we use several console statements. Is there a global way to detect environment and then display console.log in our components and service only under development?

What I mean by global way - I know we can use something like:

if (!environment.production) {
  console.log(this.reviewTasksList);
}

But by using this code everytime we have to console.log (along with necessary import to get environment variable) our code will become kind of verbose.

I want to know if there is a way to maybe:

Or the better solution here is to create a logger service and do all the environment check within it?

I don't want my bundle size to be impacted by debug statements and service.

Upvotes: 44

Views: 69331

Answers (12)

Dhairya Tripathi
Dhairya Tripathi

Reputation: 312

Update: to trace message

It's relatively simple to see the trace of any logs you can just add a boolean property isTraceable and then do something like this.

if (isTraceable) {
    console.trace(message);
}

By default isTraceable can be set false and you can turn it on only when needed.

Original Answer

I have created a solution for this. As mentioned by many others I am using a service to achieve this. The difference in my solution is that you can also specify log levels. Here is my code
This is my log-level.model.ts file you can add it with your other model files.

export const LOG_LEVELS = {
   FATAL: 0,
   ERROR: 1,
   WARN: 2,
   INFO: 3,
   DEBUG: 4,
};

export class LogLevel {
    static readonly DEBUG = LOG_LEVELS.DEBUG;
    static readonly INFO = LOG_LEVELS.INFO;
    static readonly WARN = LOG_LEVELS.WARN;
    static readonly ERROR = LOG_LEVELS.ERROR;
    static readonly FATAL = LOG_LEVELS.FATAL;
}

Now coming to main service this is my log.service.ts file add this with all your other service file and you will be good to go.

import { Injectable } from "@angular/core";
import { LogLevel, LOG_LEVELS } from "../public-api";


@Injectable({
    providedIn: "root"
})
export class LogService {
    currentLog: LogLevel;

    constructor() {
        this.currentLog = LOG_LEVELS.DEBUG;
    }

    setLogLevel(level: LogLevel) {
        this.currentLog = level;
    }

    debug(message: any, param: any = "DEBUG") {
        if (this.currentLog >= LOG_LEVELS.DEBUG) {
            console.log(param, message);
        }
    }

    info(message: any, param: any = "INFO") {
        if (this.currentLog >= LOG_LEVELS.INFO) {
            console.log(param, message);
        }
    }

    warn(message: any, param: any = "WARN") {
        if (this.currentLog >= LOG_LEVELS.WARN) {
            console.warn(param, message);
        }
    }

    error(message: any, param: any = "ERROR") {
        if (this.currentLog >= LOG_LEVELS.ERROR) {
            console.error(param, message);
        }
    }
}

There is just one thing left to do now, controlling the log level. This can be done in app.component.ts file by using following piece of code.

environment.production ? this.log.setLogLevel(LOG_LEVELS.ERROR) : this.log.setLogLevel(LOG_LEVELS.DEBUG);

Make sure to import properly. Also you can change log levels as per your need.

Upvotes: 4

Pablo Chumillo
Pablo Chumillo

Reputation: 1

I am using this solution in my projects, it may not be the most elegant, but it is fast and does not require modifying the window.console object.

loggs.ts

import { isDevMode } from "@angular/core";

export const loggs = () => {
  if (isDevMode) {
    window["LOG"] = (...args: any[]) => {
      const date = new Date();
      const minutes =
        date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
      const seconds =
        date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
      const hours = `${date.getHours()}:${minutes}:${seconds}`;
      console.groupCollapsed(hours);
      console.warn.apply(console, args);
      console.groupEnd();
    };
  } else {
    window["LOG"] = () => {};
  }
};

In the root moduele (AppModule)

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
//Others imports
import { loggs } from "./loggs";

loggs();

And in the last step for any component, service, etc.

Ex: In AppComponent

import { Component, OnInit } from "@angular/core";

declare var LOG: any; //<----HERE

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})

export class AppComponent implements OnInit {

constructor(){}

ngOnInit() {
    LOG("Upsi!");
  }

}

I hope it is useful for someone, greetings!

Upvotes: 0

Bhadresh Arya
Bhadresh Arya

Reputation: 811

First of all, store console.log, error and debug and then override them with blank function () => {}. This will help if you want to check for any error in production.

if (environment.production) {
    if (window) {
        window['log'] = console.log;
        window['error'] = console.error;
        window['debug'] = console.debug;
    }

    console.log = console.debug = console.error = () => { };
}

To check console in production, write console.log = log; browser console. This will temporarily avail the console.log.

Upvotes: 4

Akostha
Akostha

Reputation: 739

I really don't know the better way to do this. In my case... I use

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

and uses:

isDevMode() && console.log('bla bla bla bla');

I don't like so much.. but I didn't find another path to be transparent without write much code. Benefits: can see line and file, from which it was called.

Upvotes: 1

Andris
Andris

Reputation: 4193

I am using LoggingService for this purpose:

import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';

export interface ILoggerService {
    log(message?: any, ...optionalParams: any[]): void;
}

@Injectable()
export class LoggerService implements ILoggerService {
    log(message?: any, ...optionalParams: any[]): void {
        if (!environment.production)
            console.log(message, ...optionalParams);
    }
}

My logging method supports everything, that basic console.log supports - multiple args and styling including.

PS: You can implement Console instead of ILoggerService and then override all base methods for your needs.

Upvotes: 1

Wolfyr
Wolfyr

Reputation: 479

Change this in your main.ts:

if (environment.production) {
    enableProdMode();
}

To this:

if (environment.production) {
    enableProdMode();
    if (window) {
        window.console.log = () => {};
    }
}

Now when you are in production, your console.logs will be gone. Hope it helps, it will save you a lot of imports ;)

Upvotes: -1

Serginho
Serginho

Reputation: 7490

You can do this:

export abstract class Logger {

    abstract info(message: string, context?: object);
  ...more methods here...   
}

@Injectable()
export class ConsoleLogger implements Logger {

    constructor( @Inject(CONSOLE) private console: any) {
    }

    info(message: string, context?: object) {
        this.console.info(message, context ? context : '');
    }
}

@Injectable()
export class ServerLoggerService implements Logger {

    constructor(private http: HttpClient) {
    }

    info(message: string, context?: object) {
        this.http.post(...).subscribe();
    }
}

Now to instanciate factory in the module:

export function loggerFactory(console, http) {
  return environment.production ?
         new ServerLoggerService(http) :
         new ConsoleLoggerService(console);
}

Upvotes: 2

Masoud
Masoud

Reputation: 63

I've created a solution that can have different settings to control console logs based on environments you are running your application. You can define a setting of your choice in development environment and a different setting for production environment. install @sedeh/smart-service and in your app component inject SmartConsoleService and pass your settings to it. the rest is all done for you. any console log will be treated as per your specification. Checkout Smart-console and let me know if there is anything more need to be included in it.

Upvotes: 0

Taranjit Kang
Taranjit Kang

Reputation: 2580

This overwrites all console logs with blank function.

if (environment.production) {
  enableProdMode();
  window.console.log = function () { };   // disable any console.log debugging statements in production mode
  // window.console.error = function () { };

}

Upvotes: 32

Milad
Milad

Reputation: 28592

what if you override console.log if it's not in dev mode ?

 if (! isDevMode()){
   console.log = (...args)=>{}
 }

Upvotes: 3

Aravind
Aravind

Reputation: 41533

Alternatively you can use a common service to achieve this

this.loggerService.log(this.reviewTasksList);

where as in your service you can use

log(text: string){
     if (!environment.production) {
       console.log(text)
     }
}

Upvotes: 21

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657058

You can use isDevMode() or your environment.production to check if the code runs in development or production mode.

I think a logger service would be a good idea and then register a different logger service in providers depending on the mode.

See also https://github.com/angular/angular/pull/14308

Upvotes: 17

Related Questions