我们可以通过利用process.stdout.write
来捕获Stdout输出,例如
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;
这就是实现output-interceptor
和类似模块的方式。
但是,这种方法在异步/并发环境中不起作用,例如
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');
正如预期的,这将产生任意结果:
"bar" domain captured output: "bar 1n"
"baz" domain captured output: "baz 1nbar 2n"
"foo" domain captured output: "foo 1n"
有没有在特定回调/承诺执行的上下文中覆盖process.stdout
的方法?
我想知道是否有类似于可用于实现这种结果的域类似的魔法。
事实证明,我通过建议使用域来回答自己的问题。
诀窍是覆盖process.stdout.write
并检查process.domain
。如果可以将process.domain
识别为我们为捕获Stdout而创建的域,则将stdout
块附加到该域,例如。
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;
};
这是一个完全有效的示例:
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 0n');
await delay(Math.random() * 100);
process.stdout.write(domainName + ' domain 1n');
await delay(Math.random() * 100);
process.stdout.write(domainName + ' domain 2n');
};
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();
上面的示例将产生输出:
<><><> 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 0nfoo domain 1nfoo domain 2n' } Promise { 'bar domain 0nbar domain 1nbar domain 2n' } Promise { 'bar domain 0nbar domain 1nbar domain 2n' }
我已经更新了output-interceptor
模块以使用上述逻辑的变体。