DEV Community

Cover image for Writing a wrapper for console.log for better control in JavaScript, Part I

Posted on • Originally published at

Writing a wrapper for console.log for better control in JavaScript, Part I

This is not about the different flavors of console logging, this is an attempt to create an organized way to deal with it, in multiple environments in a browser JavaScript app, be it in Angular, Vue, React, or BinCurse.

Why console?

JavaScript is getting old, yet the best way to know what's going on, is a simple console.log in the right place. Read on Chrome Dev site everything you need to know about console. Disclaimer, I find no real value of most of them.

Working with complex frameworks that abstract vanilla JavaScript, we need to log everything in console during development, and that is the main "why".

What to console?

Here are some examples of things that need to be logged:

  • Request and response of an HTTP call

  • State update

  • Initialization sequence

  • Configuration ready

  • Special third party logging events.

  • Temporary attention messages

We also need a way to distinguish general logs, warnings and errors.

Where to console?

We want to disable logs upon environment change, particularly production. Thus the console.log should not be called directly, rather wrapped in another function, that distinguishes environment.

The script

Working backwards, we need a way to do this:


We also want to call a message, but the message is optional:

ourFunction(object, message);

And we want to distinguish the different types of logging:

ourFunction.log(object, message);, message);

But we do not want to couple it too tightly, so we'll just pass the type as an optional parameter.

ourFunction(object, message, type);

So let's write ourFunction

function ourFunction(o, message, type) {
  // switch type to have different flavors
  console.log(message, o);
Enter fullscreen mode Exit fullscreen mode

The first enhancement is obviously the function name. I shall call it _debug. After years of using different names, and clashing with third party, and getting confused with the log, this name withstood the test of times.

Of course, you might argue, why not place it in its own module? You can. I refuse. I want something as dumb as console.log, even dumber.

The second enhancement is adding format to the logs, so they look like this:

colorful log

console.log("%c " + message, style);

As for types, the basic types we want to log are general info (we can add new styles per request), errors and failures, traces, API call results, and warnings.

Third enhancement is to detect a variable before logging, a variable that is set to false in production. And no, this variable is not fed directly from environment, I'll tell you why later. This global constant could be:


Putting it together:

function _debug(o, message, type) {
  if (window && window._indebug) {
    switch (type) {
      case 'e': // error
        console.log('%c ' + message, 'background: red; color: #fff', o);
      case 't': // trace error
        console.trace('%c ' + message, 'background: red; color: #fff', o);
      case 'p': // http response'%c ' + message, 'background: #222; color: #bada55', o);
      case 'w': // warning
        console.warn('%c ' + message, 'background: #4f560f; color: #e6ff07', o);
      case 'gtm': // gtm events, special'%cGTM: ' + message, 'background: #03dbfc; color: #000', o);
      default:'%c ' + message, 'background: #d9d9d9; color: #a82868; font-weight: bold;', o);

// set _indebug to true in your environment file, could be anywhere
window._inebug = !process.env.production
Enter fullscreen mode Exit fullscreen mode

Example of environment setting in Angular, is in /environment.ts file directly, and since it is in typescript and may run on SSR, check for window, and access it indirectly.

window && (window['_indebug'] = true);

The last enhancement is to add a seperate function, for "attention", that is needed during development. This looks different, and can easily be found and removed from code. Those are not supposed to exist, they are there to make us ditch the habit of ever "console.logging".

function _attn(o, message) {
  if (window && window._indebug) {
    // use debug to filter in console window
    console.debug('%c ' + message, 'background: orange; font-weight: bold; color: black;', o);
Enter fullscreen mode Exit fullscreen mode


To make it available in typescript, we need to declare it in a typing file, could be typings.d.ts, that is included in the tsconfig. The script is still written in JavaScript, and added to files included in build, because, as you already know by know, it must stay dumb!

// in typing.d.ts
declare function _debug(o: any, message?: string, type?: string): void;
declare function _attn(o: any, message?: string): void;
Enter fullscreen mode Exit fullscreen mode

Using it

The most popular use of custom console logging is intercepting fetch requests. One vanilla way to intercept all fetch calls, is to overwrite fetch. Let's do that:

const oFetch = window.fetch;

window.fetch = async (...args) => {
  const response = await oFetch(...args);
  // let's console here, something like resource url and response
  _debug(response, 'GET ' + args[0], 'p');

  if (!response.ok) {
    // log errors
    _debug(response.statusText, 'Error', 'e');
    return Promise.reject(response);
  return response;

// when used in client
  .then((response) => response.json())
  .then((json) => {
    // use _attn for temporary logging
    _attn(json, 'after json call');
Enter fullscreen mode Exit fullscreen mode

Running in StackBlitz, it looks like this

Console output

Another example, is a call for GTM register event:

function registerEvent(data){
  // ... call dataLayer push, then log
  _debug(data, 'register event', 'gtm');
Enter fullscreen mode Exit fullscreen mode

A backdoor

Why did we set a global variable? In an SPA, after first page load, we can set the global variable to true in devTools console, and gain access to these messages on production. It is safe, because it only logs already available information.

For other than SPA, or to make it set to true on load, we can gain access to the backdoor with a URL parameter, again, it's safe, and benign:

const _insearch ='debug=true') > -1;
if (_insearch) {
    _indebug = true;
Enter fullscreen mode Exit fullscreen mode

So now you can share a link with ?debug=true in URL with your team, to gain access to the logged messages, for better insights.


We can make great use of this function in Angular, logging state changes, HTTP interception, error handling, and more. Come back next week, or may be earlier. 😴

Did I make you look? There is no such thing as BinCurse.


Discussion (0)