Sentry.io 在异步 Node.js 服务器上管理错误 scopes/contexts
Sentry.io Managing error scopes/contexts on asynchronous Node.js Server
基本上您在 Node.js 中有并发请求。
您可能希望使用特定于每个请求的数据来丰富可能的错误。
这种特定于请求的数据可以通过
在应用程序的不同部分收集
Sentry.configureScope(scope => scope.setSomeUsefulData(...))
Sentry.addBreadcrumb({ ... })
稍后在深层嵌套的异步函数调用中的某个地方抛出错误。
Sentry 如何知道先前收集的哪些数据实际上与此特定错误相关,考虑到请求是同时处理的,并且在发生错误的地方无法访问某些哨兵 "scope" 来获取与此特定请求相关的数据这导致了错误。
或者我是否必须通过我所有的函数调用传递 sentry 作用域?喜欢
server.on('request', (requestContext) => {
// Create new Sentry scope
Sentry.configureScope(sentryScope => {
Products.getProductById(id, sentryScope); // And pass it on
});
});
// all the way down until...
function parseNumber(input, sentryScope) {
// ...
}
或者哨兵是否使用某种魔法将特定数据映射到相关事件?还是我遗漏了什么?
他们似乎使用 Node.js 域 (https://nodejs.org/api/domain.html) 为每个请求创建一个单独的 "context"。
根据 Sentry 文档,您需要使用 requestHandler 才能正常工作。
例如对于快递 (https://docs.sentry.io/platforms/node/express/):
const express = require('express');
const app = express();
const Sentry = require('@sentry/node');
Sentry.init({ dsn: 'https://963bad1904fe48b18c1866f10e69eff8@sentry.io/1240240' });
// The request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());
// All controllers should live here
app.get('/', function rootHandler(req, res) {
res.end('Hello world!');
});
处理程序看起来像这样:
(来自:@sentry/node/dist/handlers.js)
function requestHandler(options) {
return function sentryRequestMiddleware(req, res, next) {
if (options && options.flushTimeout && options.flushTimeout > 0) {
// tslint:disable-next-line: no-unbound-method
var _end_1 = res.end;
res.end = function (chunk, encoding, cb) {
var _this = this;
sdk_1.flush(options.flushTimeout)
.then(function () {
_end_1.call(_this, chunk, encoding, cb);
})
.then(null, function (e) {
utils_1.logger.error(e);
});
};
}
var local = domain.create();
local.add(req);
local.add(res);
local.on('error', next);
local.run(function () {
core_1.getCurrentHub().configureScope(function (scope) {
return scope.addEventProcessor(function (event) { return parseRequest(event, req, options); });
});
next();
});
};
}
exports.requestHandler = requestHandler;
因此,您可以看到根据新请求创建的新域。
我发现这个信息主要是通过发现这个问题:
https://github.com/getsentry/sentry-javascript/issues/1939
如果您想将 "context" 添加到您的 Node 后端的其他异步部分(例如,如果您有工作人员/线程/某些工作不是从 HTTP 请求启动的..),上面有示例显示如何完成的问题(通过手动创建集线器/域..)。
如果您有兴趣,现在也可以使用 async_hooks 来通过各种异步函数/回调/setTimeouts 在 Node.js 中保持一定的上下文感:
https://nodejs.org/api/async_hooks.html
这是一个很好的介绍:
https://itnext.io/request-id-tracing-in-node-js-applications-c517c7dab62d?
一些库实现了这个:
https://github.com/Jeff-Lewis/cls-hooked
https://github.com/vicanso/async-local-storage
例如,像这样的东西应该可以工作:
const ALS = require("async-local-storage");
function withScope(fn, requestId) {
ALS.scope();
ALS.set("requestId", requestId, false);
return await fn();
}
async function work() {
try {
await part1();
await part2();
} catch(e) {
Sentry.withScope((scope) => {
scope.setTag("requestId", ALS.get("requestId"));
Sentry.captureException(new Error("..."))
})
}
}
withScope(work, "1234-5678");
不过,您必须自己跟踪面包屑,例如添加面包屑:
ALS.set("breadcrumbs", [...ALS.get("breadcrumbs"), new_breadcrumb]);
并且您需要在 Sentry 的 beforeSend() 回调中手动设置这些:
beforeSend: function(data) {
data.breadcrumbs = ALS.get("breadcrumbs");
return data;
}
基本上您在 Node.js 中有并发请求。 您可能希望使用特定于每个请求的数据来丰富可能的错误。 这种特定于请求的数据可以通过
在应用程序的不同部分收集Sentry.configureScope(scope => scope.setSomeUsefulData(...))
Sentry.addBreadcrumb({ ... })
稍后在深层嵌套的异步函数调用中的某个地方抛出错误。 Sentry 如何知道先前收集的哪些数据实际上与此特定错误相关,考虑到请求是同时处理的,并且在发生错误的地方无法访问某些哨兵 "scope" 来获取与此特定请求相关的数据这导致了错误。
或者我是否必须通过我所有的函数调用传递 sentry 作用域?喜欢
server.on('request', (requestContext) => {
// Create new Sentry scope
Sentry.configureScope(sentryScope => {
Products.getProductById(id, sentryScope); // And pass it on
});
});
// all the way down until...
function parseNumber(input, sentryScope) {
// ...
}
或者哨兵是否使用某种魔法将特定数据映射到相关事件?还是我遗漏了什么?
他们似乎使用 Node.js 域 (https://nodejs.org/api/domain.html) 为每个请求创建一个单独的 "context"。
根据 Sentry 文档,您需要使用 requestHandler 才能正常工作。
例如对于快递 (https://docs.sentry.io/platforms/node/express/):
const express = require('express');
const app = express();
const Sentry = require('@sentry/node');
Sentry.init({ dsn: 'https://963bad1904fe48b18c1866f10e69eff8@sentry.io/1240240' });
// The request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());
// All controllers should live here
app.get('/', function rootHandler(req, res) {
res.end('Hello world!');
});
处理程序看起来像这样:
(来自:@sentry/node/dist/handlers.js)
function requestHandler(options) {
return function sentryRequestMiddleware(req, res, next) {
if (options && options.flushTimeout && options.flushTimeout > 0) {
// tslint:disable-next-line: no-unbound-method
var _end_1 = res.end;
res.end = function (chunk, encoding, cb) {
var _this = this;
sdk_1.flush(options.flushTimeout)
.then(function () {
_end_1.call(_this, chunk, encoding, cb);
})
.then(null, function (e) {
utils_1.logger.error(e);
});
};
}
var local = domain.create();
local.add(req);
local.add(res);
local.on('error', next);
local.run(function () {
core_1.getCurrentHub().configureScope(function (scope) {
return scope.addEventProcessor(function (event) { return parseRequest(event, req, options); });
});
next();
});
};
}
exports.requestHandler = requestHandler;
因此,您可以看到根据新请求创建的新域。
我发现这个信息主要是通过发现这个问题: https://github.com/getsentry/sentry-javascript/issues/1939
如果您想将 "context" 添加到您的 Node 后端的其他异步部分(例如,如果您有工作人员/线程/某些工作不是从 HTTP 请求启动的..),上面有示例显示如何完成的问题(通过手动创建集线器/域..)。
如果您有兴趣,现在也可以使用 async_hooks 来通过各种异步函数/回调/setTimeouts 在 Node.js 中保持一定的上下文感: https://nodejs.org/api/async_hooks.html
这是一个很好的介绍:
https://itnext.io/request-id-tracing-in-node-js-applications-c517c7dab62d?
一些库实现了这个:
https://github.com/Jeff-Lewis/cls-hooked
https://github.com/vicanso/async-local-storage
例如,像这样的东西应该可以工作:
const ALS = require("async-local-storage");
function withScope(fn, requestId) {
ALS.scope();
ALS.set("requestId", requestId, false);
return await fn();
}
async function work() {
try {
await part1();
await part2();
} catch(e) {
Sentry.withScope((scope) => {
scope.setTag("requestId", ALS.get("requestId"));
Sentry.captureException(new Error("..."))
})
}
}
withScope(work, "1234-5678");
不过,您必须自己跟踪面包屑,例如添加面包屑:
ALS.set("breadcrumbs", [...ALS.get("breadcrumbs"), new_breadcrumb]);
并且您需要在 Sentry 的 beforeSend() 回调中手动设置这些:
beforeSend: function(data) {
data.breadcrumbs = ALS.get("breadcrumbs");
return data;
}