node.jsterminalansi-escapeconemuterminal-color

Node.js cluster; jumbled console output ONLY when using colour


Нello! I'm running a clustered node project with a number of nodes. They do a fair bit of console output. I also want to be able to do beautiful coloured output.

My problem: I'm getting jumbled, race-condition-y console output ONLY WHEN USING COLOURS.

I've been boiling things down to isolate my issue, and my current setup is for every node in the cluster to have its own unique string. This is the only string the node will output to console.

let chars = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let getMyUniqueString = () => {
  return chars[Math.floor(Math.random() * chars.length)].repeat(100);
};

I run a bunch of nodes which are using this function to determine their unique strings, and I see something like the following:

Console output without any jumbling

Isn't that beautiful! No matter how long and how furiously all those nodes output, this console output never gets jumbled.

Now, I try with unique strings which contain just a tiny bit of colour:

let chars = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let getMyUniqueString = () => {
  let redEscSeq = '\x1b[41m';
  let clearEscSeq = '\x1b[0m';
  let aRedBoxOfText = redEscSeq + '  ' + clearEscSeq;
  let repeatedChars = chars[Math.floor(Math.random() * chars.length)].repeat(100);
  return aRedBoxOfText + repeatedChars;
};

And look how sad some of my results look!

enter image description here

The ONLY way data is being sent to the terminal, across all nodes, is through the console.log function.

Why is console.log smart enough to keep the output from many nodes unjumbled when there is no colour, but not smart enough to do it when even a bit of colour is included??

Thanks for any help!

(Just for reference, the following image is the kind of unjumbled output I'd expect to see consistently in the coloured case; it's just a red box (two spaces with red background colour) prefixing each line:)

enter image description here

EDIT: While this problem exists in the native windows "cmd.exe" console, in the powershell console, and in ConEmu (a nice 3rd party windows terminal shown in the screenshots), it does NOT exist in the Cygwin terminal! In Cygwin there is never any jumbling, even with tons of colours and async output. Is there anything I can do to encourage this Cygwin behaviour in other consoles??


Solution

  • It is a race condition, and it's unlikely you can do anything about it, maybe except reporting a bug to library used by nodejs.

    While windows API itself does in fact support printing multi-colored string in a single API call, as shown here: https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput, where every character contains its color information. But it also doesn't support ANSI escape codes. And this is not what's actually used by javascript.

    Nodejs engine uses a library called libuv to write strings to terminal, which is crossplatform and internally translates ANSI escape codes to do the right thing. But if you look closely at the source of libuv for handling ANSI escape codes, you will see that it does a complete buffer flush after every escape code, meaning at some poiint it has to become multiple windows API calls to print one line of text.

    Under normal circumstances this is obviously not an issue, but if you have multiple threads doing the writing, it means that parts of those lines may become jumbled... just like what you see here.

    So the answer here is that there is no solution. Or you accept using cygwin as a solution.