7

I've been working on extending console.log to make a colorful one that keeps the stack trace as to where it has been called. I've tried some solutions, but finally reach this point:

export const colorizedLog = (
  text: string,
  status: keyof typeof ColorStatus = "dark",
  ...args: any
) =>
  console.log.bind(
    console,
    `%c ${text}`,
    `font-weight:bold; color: ${ColorStatus[status]}`,
    ...args
  );

With this little binding, we will be able to keep the stack trace, but the problem here is we have to use it with an annoying extra () at the end, because of returning value is the function itself which is the result of bind:

 colorizedLog(
    "Title:",
    "info",
    "This is a working example")();

The top other resources that I've read through are as below:

  1. Extending console.log
  2. Macro using babel- kent.c dodds
  3. Webpack Define plugin

const colorizedLog = (text, color= "#40a7e3", ...args) =>
  console.log.bind(
    console,
    `%c ${text}`,
    `font-weight:bold; color:${color}`,
    ...args
  );
colorizedLog("Title:", "#40a7e3", "This is a working example")();

Update: Stack trace

useAppVersion.ts enter image description here

App.vue enter image description here

Chrome Console enter image description here

SeyyedKhandon
  • 4,081
  • 4
  • 27
  • 50
  • Very interesting. What is your build setup? – Werlious Feb 13 '21 at 22:29
  • @Werlious, just simple vue(webpack based config(babel+ts)) – SeyyedKhandon Feb 14 '21 at 03:06
  • 1
    If you could, could you add a trace to your code? ```function trace() { try { throw new Error('trace'); } catch(e) { alert(e.stack); } }``` and then call `trace()` from each of those functions and post the output? – Werlious Feb 14 '21 at 10:43
  • @Werlious, thanks, I've been used it before, but the problem will be an unnecessary list of stack traces that will be print, which we really don't need them and the output will be polluted by them. – SeyyedKhandon Feb 14 '21 at 11:31
  • not a problem, but I really only mentioned it so we can have some track traces to see where the scope is being lost at, because I'm not sure if Chrome or Typescript is responsible – Werlious Feb 15 '21 at 08:20

2 Answers2

2

You can make it immediately invoked:

const colorizedLog = (text, color= "#40a7e3", ...args) =>
  console.log.bind(
    console,
    `%c ${text}`,
    `font-weight:bold; color:${color}`,
    ...args
  )();

colorizedLog("Title:", "#40a7e3", "This is a working example");
Julius Dzidzevičius
  • 10,357
  • 10
  • 33
  • 75
  • The problem with this approach is, we will lose the stack trace. – SeyyedKhandon Feb 13 '21 at 07:27
  • Since op already uses a lambda, I thought it is weird that op use a bind. This is even worse (both in functionality and readability) than my direct solution (see [comment](https://stackoverflow.com/questions/66182606/how-to-extend-console-log-which-can-accept-args#comment117007654_66182606)) – user202729 Feb 13 '21 at 07:28
1

If you don't mind adding another call to that stack trace, you can wrap it in another function that hides the extra ():

const rawColorizedLog = (
  text: string,
  status: keyof typeof ColorStatus = "dark",
  ...args: any
) =>
  console.log.bind(
    console,
    `%c ${text}`,
    `font-weight:bold; color: ${ColorStatus[status]}`,
    ...args
  );
export const colorizedLog = (...args: any) => rawColorizedLog(...args)();

Then you can just call colorizedLog(...whatever) but you will have an extra call to rawColorizedLog in the stack, but I'm sure you can think of a clever name for rawColorizedLog to cover up the extra call with something cooler ;)

Werlious
  • 549
  • 7
  • 15