We can capture stdout output by tapping into the process.stdout.write
, eg
let output = '';
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
process.stdout.write = (chunk, encoding, callback) => {
if (typeof chunk === 'string') {
output += chunk;
}
return originalStdoutWrite(chunk, encoding, callback);
};
console.log('foo');
console.log('bar');
console.log('baz');
process.stdout.write = originalStdoutWrite;
This is how the output-interceptor
and similar modules are implemented.
However, this approach does not work in a asynchronous/ concurrent environment, eg
const createOutputInterceptor = require('output-interceptor').createOutputInterceptor;
const delay = require('delay');
const interceptOutput = createOutputInterceptor();
const run = async (domain) => {
await interceptOutput(async () => {
console.log(domain, 1);
await delay(Math.random() * 1000);
console.log(domain, 2);
});
console.log('"%s" domain captured output:', domain, JSON.stringify(interceptOutput.output));
};
run('foo');
run('bar');
run('baz');
As expected, this would produce arbitrary result:
"bar" domain captured output: "bar 1\n"
"baz" domain captured output: "baz 1\nbar 2\n"
"foo" domain captured output: "foo 1\n"
Is there a way to override process.stdout
only in a context of a specific callback/ Promise execution?
I wondering if there is magic similar to Domain that could be used to achieve such result.
Turns out I have answered my own question by suggesting to use Domain .
The trick is to override process.stdout.write
and check for process.domain
. If process.domain
can be recognised as a domain that we have created with intent to capture the stdout, then we attach the stdout
chunk to that domain, eg
const createDomain = require('domain').create;
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
process.stdout.write = (chunk, encoding, callback) => {
if (
process.domain &&
process.domain.outputInterceptor !== undefined &&
typeof chunk === 'string'
) {
process.domain.outputInterceptor += chunk;
}
return originalStdoutWrite(chunk, encoding, callback);
};
const captureStdout = async (routine) => {
const domain = createDomain();
domain.outputInterceptor = '';
await domain.run(() => {
return routine();
});
const output = domain.outputInterceptor;
domain.outputInterceptor = undefined;
return output;
};
Here is a fully working example:
const createDomain = require('domain').create;
const delay = require('delay');
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
process.stdout.write = (chunk, encoding, callback) => {
if (
process.domain &&
process.domain.outputInterceptor !== undefined &&
typeof chunk === 'string'
) {
process.domain.outputInterceptor += chunk;
}
return originalStdoutWrite(chunk, encoding, callback);
};
let domainIndex = 0;
const captureStdout = async (routine) => {
const domain = createDomain();
domain.outputInterceptor = '';
await domain.run(() => {
return routine();
});
const output = domain.outputInterceptor;
domain.outputInterceptor = undefined;
return output;
};
const run = async (domainName) => {
process.stdout.write(domainName + ' domain 0\n');
await delay(Math.random() * 100);
process.stdout.write(domainName + ' domain 1\n');
await delay(Math.random() * 100);
process.stdout.write(domainName + ' domain 2\n');
};
const main = async () => {
console.log('<><><> 0');
const result1 = captureStdout(() => {
return run('foo');
});
console.log('<><><> 1');
const result2 = captureStdout(() => {
return run('bar');
});
console.log('<><><> 2');
const result3 = captureStdout(() => {
return run('bar');
});
console.log('<><><> 3');
await Promise.all([
result1,
result2,
result3,
]);
console.log(
result1,
result2,
result3
);
};
main();
The above example will produce output:
<><><> 0
foo domain 0
<><><> 1
bar domain 0
<><><> 2
bar domain 0
<><><> 3
foo domain 1
bar domain 1
bar domain 1
foo domain 2
bar domain 2
bar domain 2
Promise { 'foo domain 0\nfoo domain 1\nfoo domain 2\n' } Promise { 'bar domain 0\nbar domain 1\nbar domain 2\n' } Promise { 'bar domain 0\nbar domain 1\nbar domain 2\n' }
I have since updated output-interceptor
module to use a variation of the above logic.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.