8

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:

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")();

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

Check Stack trace

// useAppVersion.ts

export enum ColorStatus {
  info = "#40a7e3",
  ServerInfo = "#3e618a",
  warning = "#f28021",
  danger = "#b41e4a",
  dark = "#222222",
}
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
  );

export const colorizedLog2 = (...args: any) => colorizedLog(...args)();
// log.ts
console.log("console.log:", "Text");
colorizedLog("colorizedLog:", "dark", "Text")();
colorizedLog2("colorizedLog2:", "dark", "Text");

Chrome Console

As You can see the last one's stack trace is not correct or what we want. How to make it work? enter image description here

SeyyedKhandon
  • 5,197
  • 8
  • 37
  • 68
  • 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

1

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,775
  • 11
  • 36
  • 81
  • 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
  • 583
  • 6
  • 15