javascript - JavaScript 变量范围为异步场景中的父请求
问题描述
我们在我们的生产性 ExpressJS 应用程序中重写console.log
以添加某些字段(例如时间戳、会话 ID、请求 ID)以及将日志发送到系统日志服务器。
我们通过向全局对象添加console.requestId
和属性然后覆盖以输出这些值以及将日志发送到 syslog 来做到这一点。这工作正常:console.sessionId
console
console.log()
let originalConsoleLog = console.log;
console.log = function() {
let args = Array.from(arguments);
args.unshift(this.sessionId);
args.unshift(this.requestId);
originalConsoleLog.log.apply(console, args);
syslog.log(arguments);
}
app.get('/some-endpoint', async (req, res) => {
console.requestId = req.header('X-Request-Id');
console.sessionId = req.session = req.cookies['SESSIONID'];
let response = await someAsyncProcess();
console.log('foo');
req.json(response);
});
当同时处理 2 个请求时,问题就来了:
- 请求 1 设置 requestId/sessionId
REQ1/SESSA
- 请求 1 等待
someAsyncProcess()
- 请求 2 设置 requestId/sessionId
REQ2/SESSB
- 请求 1 记录
REQ2/SESSB foo
何时应该记录REQ1/SESSA foo
所以全局变量(如console
)出来了。
由于我们不想将某些logger
实例传递给每个模块,因此本地人也被淘汰了。
我们需要的是一个“伪全局”变量,它的作用域是 ExpressJS 中的单个请求。类似于闭包的东西,但也适用于外部模块:
let logger = new Logger(sessionId, requestId);
function() {
// I can see logger
logger.log('hello');
// This module can't
let foo = require('foo');
}
这个想法是我们可以在这个logger
变量上设置一个请求和会话 id,并且从这里调用的所有函数/模块/方法都将console.log
使用这个会话/请求 id。变量“属于”从请求响应者调用的所有代码。
我们不想将此logger
实例注入我们使用的每个子模块,但我们想console.log
动态捕获请求并注入相关的本地sessionId
和requestId
.
解决方案
您可能需要为此使用async hooks
,例如
const asyncHooks = require('async_hooks');
const store = new Map();
const asyncHook = asyncHooks.createHook({
init: (asyncId, _, triggerAsyncId) => {
if (store.has(triggerAsyncId)) {
store.set(asyncId, store.get(triggerAsyncId))
}
},
destroy: (asyncId) => {
if (store.has(asyncId)) {
store.delete(asyncId);
}
}
});
asyncHook.enable();
const createRequestContext = (sessionId, requestId) => {
const context = { sessionId, requestId };
store.set(asyncHooks.executionAsyncId(), context);
return context;
};
const getRequestContext = () => {
return store.get(asyncHooks.executionAsyncId());
};
let originalConsoleLog = console.log;
console.log = function() {
let args = Array.from(arguments);
const { sessionId, requestId } = getRequestContext();
args.unshift(sessionId);
args.unshift(requestId);
originalConsoleLog.log.apply(console, args);
syslog.log(arguments);
}
app.get('/some-endpoint', async (req, res) => {
const requestId = req.header('X-Request-Id');
const sessionId = req.session = req.cookies['SESSIONID'];
createRequestContext(requestId, sesscionId);
let response = await someAsyncProcess();
console.log('foo');
req.json(response);
});
推荐阅读
- mysql - 如何在MYSQL中识别授予权限
- xml - 不要删除 xsd 中小数后的尾随零
- r - 在 Mac 上更改 RStudio 默认浏览器以进行降价
- reactjs - 如何将此功能放入组件中?
- objective-c - Objective-c 复制一个强属性
- angular - @Input 变量更改后如何在 Angular 中更新 TinyMCE 编辑器内容?
- javascript - 如何读出并显示用户在输入字段中输入的内容
- git - 如何制作作为映射驱动器安装在外部的 PhpStorm 监视文件(跟踪 Git)
- excel - excel vba的用户登录表单
- django - Django:在日期时间字段上按日期分组