DEV Community

loading...

A hassle-free, zero dependency way to handle console.log() & co. in a production Angular application

Manuel Heidrich
Full Stack JavaScript Developer in Brandenburg. Creator of Monitornator. Angular, Svelte, Vue, Ionic, Capacitor, Cordova, NestJS, Express. Making the web awesome since 2002. Also: 🎺 πŸƒπŸΌβ€β™‚οΈ πŸ“· ✈️ πŸ”
Originally published at manuel-heidrich.dev ・2 min read

console.log() and its siblings likely are the most used methods any JavaScript developer has ever used. And thatβ€˜s totally fine, because console.log() most definitely is a huge help if not even a life saver when debugging an application.

And there is the challenge right there: as helpful as output in the browserβ€˜s console is when debugging, it is absolutely not a good idea to leave this output when deploying your application to production. At least any output below the error level.

So one solution could be to not leave any console methods in the code (except maybe console.error()). Fortunately linters like tslint or eslint have built-in rules to help you with that. But letβ€˜s be honest, we developers usually are lazy af. Also often there are situations where we want to have console output permanently when developing. So it would be a huge hassle to add, remove, re-add (and so on) the console methods.

I wrote about a custom logger service a couple of years ago, which is environment agnostic, but I’m no longer using it. The main reason is that you are not able to see the file and line the output was generated. Plus I do not always want to import and inject a service first. Using console.log() is just so much more convenient.

My solution

For my current side project Monitornator I added a method called handleConsole() to my AppComponent that looks like this:

private handleConsole(): void {
  let methods: string[] = [];

  switch (environment.name) {
    case β€˜production’:
    case β€˜staging’:
      methods = [β€˜log’, β€˜debug’, β€˜info’, β€˜warn’];
      break;

    default:
      break;
  }

  if (methods.length) {
    console.warn(`🚨 Console output is disabled on ${environment.name}!`);
  }

  methods.forEach((method) => {
    console[method] = function(): void { };
  });
}
Enter fullscreen mode Exit fullscreen mode

What it does is basically overriding any console method with an empty function, depending on the current environment. So leaving console.log() in the code in this case will just print nothing while on staging or production.
Using switch case allows me to quickly add different log levels for different environments.
And before overriding the methods I print a warning to the console, because... let’s say SOMEONE more then once spent a huge amount of time wondering why console.log() suddenly stopped working while on staging πŸ€¦πŸΌβ€β™‚οΈ.

Conclusion

Of course there is a fair amount of logging libraries out there, some better than others, but I personally don’t want to add another dependency for something I can solve with just a couple lines of code. The above solution works pretty fine for me.

Discussion (3)

Collapse
devmyqi profile image
Michael Wronna • Edited

Thanks Manuel,

logging is always the first feature I implement, I mostly use a own function like:

const log = function(level,message) {
  const logdate = function() {
    const date = new Date();
    return String(date.getHours()).padStart(2,'0') + ':'
      + String(date.getMinutes()).padStart(2,'0') + ':'
      + String(date.getSeconds()).padStart(2,'0') + '.'
      + String(date.getMilliseconds()).padStart(3,'0');
    };
    const logtype = function(level) {
      const logtypes = {1:'info',2:'info',4:'info',8:'warning',
          16:'error',32:'debug'};
      return logtypes[level] ? logtypes[level] : 'undef';
   };
   if ( config.loglevel & level ) {
     console.log(`${logdate()} [${logtype(level)}] (${level}) ${message}`);
   };
};

Sometimes, especially for client code I extend this function with:

  // inside log function
  const logcmd = function(level) {
    if ( level === 8 ) { return 'warn';
    } else if ( level === 16 ) { return 'error';
    } else { return 'log'; }
  };
  // and then log with
  console[logcmd(level)]('the log string');
Collapse
jfbrennan profile image
Jordan Brennan • Edited

Love the idea and the choice to avoid adding a dependency. You could shorten it even further and go vanilla js for an even more simple solution:

handleConsole() {
  if (environment.name && ['production', 'staging'].includes(environment.name.toLowerCase())) {
    console.warn(`🚨 Console output is disabled on ${environment.name}!`);
    ['log', 'debug', 'info', 'warn'].forEach(method => console[method] = ()=>{});
  }
}
Collapse
mahnuh profile image
Manuel Heidrich Author

Sure, in the case above that would absolutely work! I chose not to shorten it for readability and the ability to add different log levels for production and staging for example.