@sentry/node 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/cron/common.js +56 -0
- package/cjs/cron/common.js.map +1 -0
- package/cjs/cron/cron.js +112 -0
- package/cjs/cron/cron.js.map +1 -0
- package/cjs/cron/index.js +15 -0
- package/cjs/cron/index.js.map +1 -0
- package/cjs/cron/node-cron.js +72 -0
- package/cjs/cron/node-cron.js.map +1 -0
- package/cjs/cron/node-schedule.js +69 -0
- package/cjs/cron/node-schedule.js.map +1 -0
- package/cjs/debug-build.js +11 -0
- package/cjs/debug-build.js.map +1 -0
- package/cjs/index.js +85 -4018
- package/cjs/index.js.map +1 -1
- package/cjs/integrations/anr/index.js +211 -0
- package/cjs/integrations/anr/index.js.map +1 -0
- package/cjs/integrations/console.js +40 -0
- package/cjs/integrations/console.js.map +1 -0
- package/cjs/integrations/context.js +435 -0
- package/cjs/integrations/context.js.map +1 -0
- package/cjs/integrations/contextlines.js +137 -0
- package/cjs/integrations/contextlines.js.map +1 -0
- package/cjs/integrations/http.js +161 -0
- package/cjs/integrations/http.js.map +1 -0
- package/cjs/integrations/local-variables/common.js +87 -0
- package/cjs/integrations/local-variables/common.js.map +1 -0
- package/cjs/integrations/local-variables/index.js +12 -0
- package/cjs/integrations/local-variables/index.js.map +1 -0
- package/cjs/integrations/local-variables/local-variables-async.js +149 -0
- package/cjs/integrations/local-variables/local-variables-async.js.map +1 -0
- package/cjs/integrations/local-variables/local-variables-sync.js +376 -0
- package/cjs/integrations/local-variables/local-variables-sync.js.map +1 -0
- package/cjs/integrations/modules.js +99 -0
- package/cjs/integrations/modules.js.map +1 -0
- package/cjs/integrations/node-fetch.js +81 -0
- package/cjs/integrations/node-fetch.js.map +1 -0
- package/cjs/integrations/onuncaughtexception.js +135 -0
- package/cjs/integrations/onuncaughtexception.js.map +1 -0
- package/cjs/integrations/onunhandledrejection.js +90 -0
- package/cjs/integrations/onunhandledrejection.js.map +1 -0
- package/cjs/integrations/spotlight.js +109 -0
- package/cjs/integrations/spotlight.js.map +1 -0
- package/cjs/integrations/tracing/connect.js +41 -0
- package/cjs/integrations/tracing/connect.js.map +1 -0
- package/cjs/integrations/tracing/express.js +127 -0
- package/cjs/integrations/tracing/express.js.map +1 -0
- package/cjs/integrations/tracing/fastify.js +84 -0
- package/cjs/integrations/tracing/fastify.js.map +1 -0
- package/cjs/integrations/tracing/graphql.js +32 -0
- package/cjs/integrations/tracing/graphql.js.map +1 -0
- package/cjs/integrations/tracing/hapi/index.js +106 -0
- package/cjs/integrations/tracing/hapi/index.js.map +1 -0
- package/cjs/integrations/tracing/index.js +43 -0
- package/cjs/integrations/tracing/index.js.map +1 -0
- package/cjs/integrations/tracing/koa.js +60 -0
- package/cjs/integrations/tracing/koa.js.map +1 -0
- package/cjs/integrations/tracing/mongo.js +31 -0
- package/cjs/integrations/tracing/mongo.js.map +1 -0
- package/cjs/integrations/tracing/mongoose.js +31 -0
- package/cjs/integrations/tracing/mongoose.js.map +1 -0
- package/cjs/integrations/tracing/mysql.js +24 -0
- package/cjs/integrations/tracing/mysql.js.map +1 -0
- package/cjs/integrations/tracing/mysql2.js +31 -0
- package/cjs/integrations/tracing/mysql2.js.map +1 -0
- package/cjs/integrations/tracing/nest.js +69 -0
- package/cjs/integrations/tracing/nest.js.map +1 -0
- package/cjs/integrations/tracing/postgres.js +32 -0
- package/cjs/integrations/tracing/postgres.js.map +1 -0
- package/cjs/integrations/tracing/prisma.js +48 -0
- package/cjs/integrations/tracing/prisma.js.map +1 -0
- package/cjs/integrations/tracing/redis.js +29 -0
- package/cjs/integrations/tracing/redis.js.map +1 -0
- package/cjs/nodeVersion.js +10 -0
- package/cjs/nodeVersion.js.map +1 -0
- package/cjs/otel/contextManager.js +16 -0
- package/cjs/otel/contextManager.js.map +1 -0
- package/cjs/proxy/base.js +127 -0
- package/cjs/proxy/base.js.map +1 -0
- package/cjs/proxy/index.js +178 -0
- package/cjs/proxy/index.js.map +1 -0
- package/cjs/proxy/parse-proxy-response.js +104 -0
- package/cjs/proxy/parse-proxy-response.js.map +1 -0
- package/cjs/sdk/api.js +45 -0
- package/cjs/sdk/api.js.map +1 -0
- package/cjs/sdk/client.js +63 -0
- package/cjs/sdk/client.js.map +1 -0
- package/cjs/sdk/init.js +318 -0
- package/cjs/sdk/init.js.map +1 -0
- package/cjs/sdk/initOtel.js +58 -0
- package/cjs/sdk/initOtel.js.map +1 -0
- package/cjs/sdk/scope.js +18 -0
- package/cjs/sdk/scope.js.map +1 -0
- package/cjs/transports/http.js +163 -0
- package/cjs/transports/http.js.map +1 -0
- package/cjs/utils/addOriginToSpan.js +11 -0
- package/cjs/utils/addOriginToSpan.js.map +1 -0
- package/cjs/utils/errorhandling.js +44 -0
- package/cjs/utils/errorhandling.js.map +1 -0
- package/cjs/utils/getRequestUrl.js +18 -0
- package/cjs/utils/getRequestUrl.js.map +1 -0
- package/cjs/utils/module.js +62 -0
- package/cjs/utils/module.js.map +1 -0
- package/esm/cron/common.js +54 -0
- package/esm/cron/common.js.map +1 -0
- package/esm/cron/cron.js +110 -0
- package/esm/cron/cron.js.map +1 -0
- package/esm/cron/index.js +13 -0
- package/esm/cron/index.js.map +1 -0
- package/esm/cron/node-cron.js +67 -0
- package/esm/cron/node-cron.js.map +1 -0
- package/esm/cron/node-schedule.js +64 -0
- package/esm/cron/node-schedule.js.map +1 -0
- package/esm/debug-build.js +9 -0
- package/esm/debug-build.js.map +1 -0
- package/esm/index.js +35 -3973
- package/esm/index.js.map +1 -1
- package/esm/integrations/anr/index.js +204 -0
- package/esm/integrations/anr/index.js.map +1 -0
- package/esm/integrations/anr/worker-script.js +2 -2
- package/esm/integrations/console.js +38 -0
- package/esm/integrations/console.js.map +1 -0
- package/esm/integrations/context.js +427 -0
- package/esm/integrations/context.js.map +1 -0
- package/esm/integrations/contextlines.js +131 -0
- package/esm/integrations/contextlines.js.map +1 -0
- package/esm/integrations/http.js +156 -0
- package/esm/integrations/http.js.map +1 -0
- package/esm/integrations/local-variables/common.js +81 -0
- package/esm/integrations/local-variables/common.js.map +1 -0
- package/esm/integrations/local-variables/index.js +10 -0
- package/esm/integrations/local-variables/index.js.map +1 -0
- package/esm/integrations/local-variables/local-variables-async.js +143 -0
- package/esm/integrations/local-variables/local-variables-async.js.map +1 -0
- package/esm/integrations/local-variables/local-variables-sync.js +370 -0
- package/esm/integrations/local-variables/local-variables-sync.js.map +1 -0
- package/esm/integrations/local-variables/worker-script.js +1 -1
- package/esm/integrations/modules.js +97 -0
- package/esm/integrations/modules.js.map +1 -0
- package/esm/integrations/node-fetch.js +79 -0
- package/esm/integrations/node-fetch.js.map +1 -0
- package/esm/integrations/onuncaughtexception.js +132 -0
- package/esm/integrations/onuncaughtexception.js.map +1 -0
- package/esm/integrations/onunhandledrejection.js +87 -0
- package/esm/integrations/onunhandledrejection.js.map +1 -0
- package/esm/integrations/spotlight.js +106 -0
- package/esm/integrations/spotlight.js.map +1 -0
- package/esm/integrations/tracing/connect.js +38 -0
- package/esm/integrations/tracing/connect.js.map +1 -0
- package/esm/integrations/tracing/express.js +120 -0
- package/esm/integrations/tracing/express.js.map +1 -0
- package/esm/integrations/tracing/fastify.js +78 -0
- package/esm/integrations/tracing/fastify.js.map +1 -0
- package/esm/integrations/tracing/graphql.js +30 -0
- package/esm/integrations/tracing/graphql.js.map +1 -0
- package/esm/integrations/tracing/hapi/index.js +99 -0
- package/esm/integrations/tracing/hapi/index.js.map +1 -0
- package/esm/integrations/tracing/index.js +41 -0
- package/esm/integrations/tracing/index.js.map +1 -0
- package/esm/integrations/tracing/koa.js +57 -0
- package/esm/integrations/tracing/koa.js.map +1 -0
- package/esm/integrations/tracing/mongo.js +29 -0
- package/esm/integrations/tracing/mongo.js.map +1 -0
- package/esm/integrations/tracing/mongoose.js +29 -0
- package/esm/integrations/tracing/mongoose.js.map +1 -0
- package/esm/integrations/tracing/mysql.js +22 -0
- package/esm/integrations/tracing/mysql.js.map +1 -0
- package/esm/integrations/tracing/mysql2.js +29 -0
- package/esm/integrations/tracing/mysql2.js.map +1 -0
- package/esm/integrations/tracing/nest.js +63 -0
- package/esm/integrations/tracing/nest.js.map +1 -0
- package/esm/integrations/tracing/postgres.js +30 -0
- package/esm/integrations/tracing/postgres.js.map +1 -0
- package/esm/integrations/tracing/prisma.js +43 -0
- package/esm/integrations/tracing/prisma.js.map +1 -0
- package/esm/integrations/tracing/redis.js +27 -0
- package/esm/integrations/tracing/redis.js.map +1 -0
- package/esm/nodeVersion.js +7 -0
- package/esm/nodeVersion.js.map +1 -0
- package/esm/otel/contextManager.js +14 -0
- package/esm/otel/contextManager.js.map +1 -0
- package/esm/proxy/base.js +122 -0
- package/esm/proxy/base.js.map +1 -0
- package/esm/proxy/index.js +172 -0
- package/esm/proxy/index.js.map +1 -0
- package/esm/proxy/parse-proxy-response.js +102 -0
- package/esm/proxy/parse-proxy-response.js.map +1 -0
- package/esm/sdk/api.js +42 -0
- package/esm/sdk/api.js.map +1 -0
- package/esm/sdk/client.js +58 -0
- package/esm/sdk/client.js.map +1 -0
- package/esm/sdk/init.js +312 -0
- package/esm/sdk/init.js.map +1 -0
- package/esm/sdk/initOtel.js +55 -0
- package/esm/sdk/initOtel.js.map +1 -0
- package/esm/sdk/scope.js +16 -0
- package/esm/sdk/scope.js.map +1 -0
- package/esm/transports/http.js +158 -0
- package/esm/transports/http.js.map +1 -0
- package/esm/utils/addOriginToSpan.js +9 -0
- package/esm/utils/addOriginToSpan.js.map +1 -0
- package/esm/utils/errorhandling.js +42 -0
- package/esm/utils/errorhandling.js.map +1 -0
- package/esm/utils/getRequestUrl.js +16 -0
- package/esm/utils/getRequestUrl.js.map +1 -0
- package/esm/utils/module.js +60 -0
- package/esm/utils/module.js.map +1 -0
- package/package.json +16 -16
- package/types/index.d.ts +2 -2
- package/types/index.d.ts.map +1 -1
- package/types/integrations/tracing/hapi/index.d.ts.map +1 -1
- package/types/integrations/tracing/koa.d.ts.map +1 -1
- package/types/sdk/init.d.ts +4 -0
- package/types/sdk/init.d.ts.map +1 -1
- package/types-ts3.8/index.d.ts +2 -2
- package/types-ts3.8/sdk/init.d.ts +4 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { SpanKind } from '@opentelemetry/api';
|
|
2
|
+
import { defineIntegration, addBreadcrumb } from '@sentry/core';
|
|
3
|
+
import { addOpenTelemetryInstrumentation, getSpanKind, getRequestSpanData } from '@sentry/opentelemetry';
|
|
4
|
+
import { logger } from '@sentry/utils';
|
|
5
|
+
import { DEBUG_BUILD } from '../debug-build.js';
|
|
6
|
+
import { NODE_MAJOR } from '../nodeVersion.js';
|
|
7
|
+
import { addOriginToSpan } from '../utils/addOriginToSpan.js';
|
|
8
|
+
|
|
9
|
+
const _nativeNodeFetchIntegration = ((options = {}) => {
|
|
10
|
+
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
|
|
11
|
+
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
|
|
12
|
+
|
|
13
|
+
async function getInstrumentation() {
|
|
14
|
+
// Only add NodeFetch if Node >= 18, as previous versions do not support it
|
|
15
|
+
if (NODE_MAJOR < 18) {
|
|
16
|
+
DEBUG_BUILD && logger.log('NodeFetch is not supported on Node < 18, skipping instrumentation...');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const pkg = await import('opentelemetry-instrumentation-fetch-node');
|
|
22
|
+
return new pkg.FetchInstrumentation({
|
|
23
|
+
ignoreRequestHook: (request) => {
|
|
24
|
+
const url = request.origin;
|
|
25
|
+
return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);
|
|
26
|
+
},
|
|
27
|
+
onRequest: ({ span }) => {
|
|
28
|
+
_updateSpan(span);
|
|
29
|
+
|
|
30
|
+
if (_breadcrumbs) {
|
|
31
|
+
_addRequestBreadcrumb(span);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
} );
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Could not load instrumentation
|
|
38
|
+
DEBUG_BUILD && logger.log('Could not load NodeFetch instrumentation.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name: 'NodeFetch',
|
|
44
|
+
setupOnce() {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
46
|
+
getInstrumentation().then(instrumentation => {
|
|
47
|
+
if (instrumentation) {
|
|
48
|
+
addOpenTelemetryInstrumentation(instrumentation);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}) ;
|
|
54
|
+
|
|
55
|
+
const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);
|
|
56
|
+
|
|
57
|
+
/** Update the span with data we need. */
|
|
58
|
+
function _updateSpan(span) {
|
|
59
|
+
addOriginToSpan(span, 'auto.http.otel.node_fetch');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Add a breadcrumb for outgoing requests. */
|
|
63
|
+
function _addRequestBreadcrumb(span) {
|
|
64
|
+
if (getSpanKind(span) !== SpanKind.CLIENT) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const data = getRequestSpanData(span);
|
|
69
|
+
addBreadcrumb({
|
|
70
|
+
category: 'http',
|
|
71
|
+
data: {
|
|
72
|
+
...data,
|
|
73
|
+
},
|
|
74
|
+
type: 'http',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { nativeNodeFetchIntegration };
|
|
79
|
+
//# sourceMappingURL=node-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-fetch.js","sources":["../../../src/integrations/node-fetch.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport { SpanKind } from '@opentelemetry/api';\nimport { addBreadcrumb, defineIntegration } from '@sentry/core';\nimport { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';\nimport { getRequestSpanData, getSpanKind } from '@sentry/opentelemetry';\nimport type { IntegrationFn } from '@sentry/types';\nimport { logger } from '@sentry/utils';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { NODE_MAJOR } from '../nodeVersion';\n\nimport type { FetchInstrumentation } from 'opentelemetry-instrumentation-fetch-node';\n\nimport { addOriginToSpan } from '../utils/addOriginToSpan';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n\n async function getInstrumentation(): Promise<FetchInstrumentation | void> {\n // Only add NodeFetch if Node >= 18, as previous versions do not support it\n if (NODE_MAJOR < 18) {\n DEBUG_BUILD && logger.log('NodeFetch is not supported on Node < 18, skipping instrumentation...');\n return;\n }\n\n try {\n const pkg = await import('opentelemetry-instrumentation-fetch-node');\n return new pkg.FetchInstrumentation({\n ignoreRequestHook: (request: { origin?: string }) => {\n const url = request.origin;\n return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);\n },\n onRequest: ({ span }: { span: Span }) => {\n _updateSpan(span);\n\n if (_breadcrumbs) {\n _addRequestBreadcrumb(span);\n }\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any);\n } catch (error) {\n // Could not load instrumentation\n DEBUG_BUILD && logger.log('Could not load NodeFetch instrumentation.');\n }\n }\n\n return {\n name: 'NodeFetch',\n setupOnce() {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n getInstrumentation().then(instrumentation => {\n if (instrumentation) {\n addOpenTelemetryInstrumentation(instrumentation);\n }\n });\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n\n/** Update the span with data we need. */\nfunction _updateSpan(span: Span): void {\n addOriginToSpan(span, 'auto.http.otel.node_fetch');\n}\n\n/** Add a breadcrumb for outgoing requests. */\nfunction _addRequestBreadcrumb(span: Span): void {\n if (getSpanKind(span) !== SpanKind.CLIENT) {\n return;\n }\n\n const data = getRequestSpanData(span);\n addBreadcrumb({\n category: 'http',\n data: {\n ...data,\n },\n type: 'http',\n });\n}\n"],"names":[],"mappings":";;;;;;;;AA4BA,MAAM,2BAAA,IAA+B,CAAC,OAAO,GAAqB,EAAE,KAAK;AACzE,EAAE,MAAM,YAAA,GAAe,OAAO,OAAO,CAAC,WAAA,KAAgB,WAAA,GAAc,IAAA,GAAO,OAAO,CAAC,WAAW,CAAA;AAC9F,EAAE,MAAM,uBAAA,GAA0B,OAAO,CAAC,sBAAsB,CAAA;AAChE;AACA,EAAE,eAAe,kBAAkB,GAAyC;AAC5E;AACA,IAAI,IAAI,UAAW,GAAE,EAAE,EAAE;AACzB,MAAM,eAAe,MAAM,CAAC,GAAG,CAAC,sEAAsE,CAAC,CAAA;AACvG,MAAM,OAAM;AACZ,KAAI;AACJ;AACA,IAAI,IAAI;AACR,MAAM,MAAM,GAAI,GAAE,MAAM,OAAO,0CAA0C,CAAC,CAAA;AAC1E,MAAM,OAAO,IAAI,GAAG,CAAC,oBAAoB,CAAC;AAC1C,QAAQ,iBAAiB,EAAE,CAAC,OAAO,KAA0B;AAC7D,UAAU,MAAM,GAAA,GAAM,OAAO,CAAC,MAAM,CAAA;AACpC,UAAU,OAAO,2BAA2B,GAAA,IAAO,uBAAuB,CAAC,GAAG,CAAC,CAAA;AAC/E,SAAS;AACT,QAAQ,SAAS,EAAE,CAAC,EAAE,IAAK,EAAC,KAAqB;AACjD,UAAU,WAAW,CAAC,IAAI,CAAC,CAAA;AAC3B;AACA,UAAU,IAAI,YAAY,EAAE;AAC5B,YAAY,qBAAqB,CAAC,IAAI,CAAC,CAAA;AACvC,WAAU;AACV,SAAS;AACT;AACA,SAAe,CAAA;AACf,KAAM,CAAA,OAAO,KAAK,EAAE;AACpB;AACA,MAAM,eAAe,MAAM,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAA;AAC5E,KAAI;AACJ,GAAE;AACF;AACA,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,WAAW;AACrB,IAAI,SAAS,GAAG;AAChB;AACA,MAAM,kBAAkB,EAAE,CAAC,IAAI,CAAC,mBAAmB;AACnD,QAAQ,IAAI,eAAe,EAAE;AAC7B,UAAU,+BAA+B,CAAC,eAAe,CAAC,CAAA;AAC1D,SAAQ;AACR,OAAO,CAAC,CAAA;AACR,KAAK;AACL,GAAG,CAAA;AACH,CAAC,CAAE,EAAA;AACH;MACa,0BAA2B,GAAE,iBAAiB,CAAC,2BAA2B,EAAC;AACxF;AACA;AACA,SAAS,WAAW,CAAC,IAAI,EAAc;AACvC,EAAE,eAAe,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAA;AACpD,CAAA;AACA;AACA;AACA,SAAS,qBAAqB,CAAC,IAAI,EAAc;AACjD,EAAE,IAAI,WAAW,CAAC,IAAI,MAAM,QAAQ,CAAC,MAAM,EAAE;AAC7C,IAAI,OAAM;AACV,GAAE;AACF;AACA,EAAE,MAAM,IAAK,GAAE,kBAAkB,CAAC,IAAI,CAAC,CAAA;AACvC,EAAE,aAAa,CAAC;AAChB,IAAI,QAAQ,EAAE,MAAM;AACpB,IAAI,IAAI,EAAE;AACV,MAAM,GAAG,IAAI;AACb,KAAK;AACL,IAAI,IAAI,EAAE,MAAM;AAChB,GAAG,CAAC,CAAA;AACJ;;;;"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { defineIntegration, getClient, captureException } from '@sentry/core';
|
|
2
|
+
import { logger } from '@sentry/utils';
|
|
3
|
+
import { DEBUG_BUILD } from '../debug-build.js';
|
|
4
|
+
import { logAndExitProcess } from '../utils/errorhandling.js';
|
|
5
|
+
|
|
6
|
+
const INTEGRATION_NAME = 'OnUncaughtException';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Add a global exception handler.
|
|
10
|
+
*/
|
|
11
|
+
const onUncaughtExceptionIntegration = defineIntegration((options = {}) => {
|
|
12
|
+
const optionsWithDefaults = {
|
|
13
|
+
exitEvenIfOtherHandlersAreRegistered: false,
|
|
14
|
+
...options,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: INTEGRATION_NAME,
|
|
19
|
+
setup(client) {
|
|
20
|
+
global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/** Exported only for tests */
|
|
26
|
+
function makeErrorHandler(client, options) {
|
|
27
|
+
const timeout = 2000;
|
|
28
|
+
let caughtFirstError = false;
|
|
29
|
+
let caughtSecondError = false;
|
|
30
|
+
let calledFatalError = false;
|
|
31
|
+
let firstError;
|
|
32
|
+
|
|
33
|
+
const clientOptions = client.getOptions();
|
|
34
|
+
|
|
35
|
+
return Object.assign(
|
|
36
|
+
(error) => {
|
|
37
|
+
let onFatalError = logAndExitProcess;
|
|
38
|
+
|
|
39
|
+
if (options.onFatalError) {
|
|
40
|
+
onFatalError = options.onFatalError;
|
|
41
|
+
} else if (clientOptions.onFatalError) {
|
|
42
|
+
onFatalError = clientOptions.onFatalError ;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not
|
|
46
|
+
// want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust
|
|
47
|
+
// exit behaviour of the SDK accordingly:
|
|
48
|
+
// - If other listeners are attached, do not exit.
|
|
49
|
+
// - If the only listener attached is ours, exit.
|
|
50
|
+
const userProvidedListenersCount = (global.process.listeners('uncaughtException') ).filter(
|
|
51
|
+
listener => {
|
|
52
|
+
// There are 3 listeners we ignore:
|
|
53
|
+
return (
|
|
54
|
+
// as soon as we're using domains this listener is attached by node itself
|
|
55
|
+
listener.name !== 'domainUncaughtExceptionClear' &&
|
|
56
|
+
// the handler we register for tracing
|
|
57
|
+
listener.tag !== 'sentry_tracingErrorCallback' &&
|
|
58
|
+
// the handler we register in this integration
|
|
59
|
+
(listener )._errorHandler !== true
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
).length;
|
|
63
|
+
|
|
64
|
+
const processWouldExit = userProvidedListenersCount === 0;
|
|
65
|
+
const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;
|
|
66
|
+
|
|
67
|
+
if (!caughtFirstError) {
|
|
68
|
+
// this is the first uncaught error and the ultimate reason for shutting down
|
|
69
|
+
// we want to do absolutely everything possible to ensure it gets captured
|
|
70
|
+
// also we want to make sure we don't go recursion crazy if more errors happen after this one
|
|
71
|
+
firstError = error;
|
|
72
|
+
caughtFirstError = true;
|
|
73
|
+
|
|
74
|
+
if (getClient() === client) {
|
|
75
|
+
captureException(error, {
|
|
76
|
+
originalException: error,
|
|
77
|
+
captureContext: {
|
|
78
|
+
level: 'fatal',
|
|
79
|
+
},
|
|
80
|
+
mechanism: {
|
|
81
|
+
handled: false,
|
|
82
|
+
type: 'onuncaughtexception',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!calledFatalError && shouldApplyFatalHandlingLogic) {
|
|
88
|
+
calledFatalError = true;
|
|
89
|
+
onFatalError(error);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
if (shouldApplyFatalHandlingLogic) {
|
|
93
|
+
if (calledFatalError) {
|
|
94
|
+
// we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down
|
|
95
|
+
DEBUG_BUILD &&
|
|
96
|
+
logger.warn(
|
|
97
|
+
'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',
|
|
98
|
+
);
|
|
99
|
+
logAndExitProcess(error);
|
|
100
|
+
} else if (!caughtSecondError) {
|
|
101
|
+
// two cases for how we can hit this branch:
|
|
102
|
+
// - capturing of first error blew up and we just caught the exception from that
|
|
103
|
+
// - quit trying to capture, proceed with shutdown
|
|
104
|
+
// - a second independent error happened while waiting for first error to capture
|
|
105
|
+
// - want to avoid causing premature shutdown before first error capture finishes
|
|
106
|
+
// it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff
|
|
107
|
+
// so let's instead just delay a bit before we proceed with our action here
|
|
108
|
+
// in case 1, we just wait a bit unnecessarily but ultimately do the same thing
|
|
109
|
+
// in case 2, the delay hopefully made us wait long enough for the capture to finish
|
|
110
|
+
// two potential nonideal outcomes:
|
|
111
|
+
// nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError
|
|
112
|
+
// nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error
|
|
113
|
+
// note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)
|
|
114
|
+
// we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish
|
|
115
|
+
caughtSecondError = true;
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
if (!calledFatalError) {
|
|
118
|
+
// it was probably case 1, let's treat err as the sendErr and call onFatalError
|
|
119
|
+
calledFatalError = true;
|
|
120
|
+
onFatalError(firstError, error);
|
|
121
|
+
}
|
|
122
|
+
}, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{ _errorHandler: true },
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { makeErrorHandler, onUncaughtExceptionIntegration };
|
|
132
|
+
//# sourceMappingURL=onuncaughtexception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onuncaughtexception.js","sources":["../../../src/integrations/onuncaughtexception.ts"],"sourcesContent":["import { captureException, defineIntegration } from '@sentry/core';\nimport { getClient } from '@sentry/core';\nimport { logger } from '@sentry/utils';\n\nimport { DEBUG_BUILD } from '../debug-build';\nimport type { NodeClient } from '../sdk/client';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;\n\ntype TaggedListener = NodeJS.UncaughtExceptionListener & {\n tag?: string;\n};\n\ninterface OnUncaughtExceptionOptions {\n /**\n * Controls if the SDK should register a handler to exit the process on uncaught errors:\n * - `true`: The SDK will exit the process on all uncaught errors.\n * - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.\n *\n * Default: `false`\n */\n exitEvenIfOtherHandlersAreRegistered: boolean;\n\n /**\n * This is called when an uncaught error would cause the process to exit.\n *\n * @param firstError Uncaught error causing the process to exit\n * @param secondError Will be set if the handler was called multiple times. This can happen either because\n * `onFatalError` itself threw, or because an independent error happened somewhere else while `onFatalError`\n * was running.\n */\n onFatalError?(this: void, firstError: Error, secondError?: Error): void;\n}\n\nconst INTEGRATION_NAME = 'OnUncaughtException';\n\n/**\n * Add a global exception handler.\n */\nexport const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {\n const optionsWithDefaults = {\n exitEvenIfOtherHandlersAreRegistered: false,\n ...options,\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client: NodeClient) {\n global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));\n },\n };\n});\n\ntype ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);\n\n/** Exported only for tests */\nexport function makeErrorHandler(client: NodeClient, options: OnUncaughtExceptionOptions): ErrorHandler {\n const timeout = 2000;\n let caughtFirstError: boolean = false;\n let caughtSecondError: boolean = false;\n let calledFatalError: boolean = false;\n let firstError: Error;\n\n const clientOptions = client.getOptions();\n\n return Object.assign(\n (error: Error): void => {\n let onFatalError: OnFatalErrorHandler = logAndExitProcess;\n\n if (options.onFatalError) {\n onFatalError = options.onFatalError;\n } else if (clientOptions.onFatalError) {\n onFatalError = clientOptions.onFatalError as OnFatalErrorHandler;\n }\n\n // Attaching a listener to `uncaughtException` will prevent the node process from exiting. We generally do not\n // want to alter this behaviour so we check for other listeners that users may have attached themselves and adjust\n // exit behaviour of the SDK accordingly:\n // - If other listeners are attached, do not exit.\n // - If the only listener attached is ours, exit.\n const userProvidedListenersCount = (global.process.listeners('uncaughtException') as TaggedListener[]).filter(\n listener => {\n // There are 3 listeners we ignore:\n return (\n // as soon as we're using domains this listener is attached by node itself\n listener.name !== 'domainUncaughtExceptionClear' &&\n // the handler we register for tracing\n listener.tag !== 'sentry_tracingErrorCallback' &&\n // the handler we register in this integration\n (listener as ErrorHandler)._errorHandler !== true\n );\n },\n ).length;\n\n const processWouldExit = userProvidedListenersCount === 0;\n const shouldApplyFatalHandlingLogic = options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;\n\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = error;\n caughtFirstError = true;\n\n if (getClient() === client) {\n captureException(error, {\n originalException: error,\n captureContext: {\n level: 'fatal',\n },\n mechanism: {\n handled: false,\n type: 'onuncaughtexception',\n },\n });\n }\n\n if (!calledFatalError && shouldApplyFatalHandlingLogic) {\n calledFatalError = true;\n onFatalError(error);\n }\n } else {\n if (shouldApplyFatalHandlingLogic) {\n if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n DEBUG_BUILD &&\n logger.warn(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown',\n );\n logAndExitProcess(error);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(() => {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n onFatalError(firstError, error);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, timeout); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n }\n }\n },\n { _errorHandler: true },\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { defineIntegration, getClient, captureException } from '@sentry/core';
|
|
2
|
+
import { consoleSandbox } from '@sentry/utils';
|
|
3
|
+
import { logAndExitProcess } from '../utils/errorhandling.js';
|
|
4
|
+
|
|
5
|
+
const INTEGRATION_NAME = 'OnUnhandledRejection';
|
|
6
|
+
|
|
7
|
+
const _onUnhandledRejectionIntegration = ((options = {}) => {
|
|
8
|
+
const mode = options.mode || 'warn';
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
name: INTEGRATION_NAME,
|
|
12
|
+
setup(client) {
|
|
13
|
+
global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, { mode }));
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}) ;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Add a global promise rejection handler.
|
|
20
|
+
*/
|
|
21
|
+
const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send an exception with reason
|
|
25
|
+
* @param reason string
|
|
26
|
+
* @param promise promise
|
|
27
|
+
*
|
|
28
|
+
* Exported only for tests.
|
|
29
|
+
*/
|
|
30
|
+
function makeUnhandledPromiseHandler(
|
|
31
|
+
client,
|
|
32
|
+
options,
|
|
33
|
+
) {
|
|
34
|
+
return function sendUnhandledPromise(reason, promise) {
|
|
35
|
+
if (getClient() !== client) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
captureException(reason, {
|
|
40
|
+
originalException: promise,
|
|
41
|
+
captureContext: {
|
|
42
|
+
extra: { unhandledPromiseRejection: true },
|
|
43
|
+
},
|
|
44
|
+
mechanism: {
|
|
45
|
+
handled: false,
|
|
46
|
+
type: 'onunhandledrejection',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
handleRejection(reason, options);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Handler for `mode` option
|
|
56
|
+
|
|
57
|
+
*/
|
|
58
|
+
function handleRejection(
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
reason,
|
|
61
|
+
options,
|
|
62
|
+
) {
|
|
63
|
+
// https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240
|
|
64
|
+
const rejectionWarning =
|
|
65
|
+
'This error originated either by ' +
|
|
66
|
+
'throwing inside of an async function without a catch block, ' +
|
|
67
|
+
'or by rejecting a promise which was not handled with .catch().' +
|
|
68
|
+
' The promise rejected with the reason:';
|
|
69
|
+
|
|
70
|
+
/* eslint-disable no-console */
|
|
71
|
+
if (options.mode === 'warn') {
|
|
72
|
+
consoleSandbox(() => {
|
|
73
|
+
console.warn(rejectionWarning);
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
75
|
+
console.error(reason && reason.stack ? reason.stack : reason);
|
|
76
|
+
});
|
|
77
|
+
} else if (options.mode === 'strict') {
|
|
78
|
+
consoleSandbox(() => {
|
|
79
|
+
console.warn(rejectionWarning);
|
|
80
|
+
});
|
|
81
|
+
logAndExitProcess(reason);
|
|
82
|
+
}
|
|
83
|
+
/* eslint-enable no-console */
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { makeUnhandledPromiseHandler, onUnhandledRejectionIntegration };
|
|
87
|
+
//# sourceMappingURL=onunhandledrejection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onunhandledrejection.js","sources":["../../../src/integrations/onunhandledrejection.ts"],"sourcesContent":["import { captureException, defineIntegration, getClient } from '@sentry/core';\nimport type { Client, IntegrationFn } from '@sentry/types';\nimport { consoleSandbox } from '@sentry/utils';\nimport { logAndExitProcess } from '../utils/errorhandling';\n\ntype UnhandledRejectionMode = 'none' | 'warn' | 'strict';\n\ninterface OnUnhandledRejectionOptions {\n /**\n * Option deciding what to do after capturing unhandledRejection,\n * that mimicks behavior of node's --unhandled-rejection flag.\n */\n mode: UnhandledRejectionMode;\n}\n\nconst INTEGRATION_NAME = 'OnUnhandledRejection';\n\nconst _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejectionOptions> = {}) => {\n const mode = options.mode || 'warn';\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n global.process.on('unhandledRejection', makeUnhandledPromiseHandler(client, { mode }));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Add a global promise rejection handler.\n */\nexport const onUnhandledRejectionIntegration = defineIntegration(_onUnhandledRejectionIntegration);\n\n/**\n * Send an exception with reason\n * @param reason string\n * @param promise promise\n *\n * Exported only for tests.\n */\nexport function makeUnhandledPromiseHandler(\n client: Client,\n options: OnUnhandledRejectionOptions,\n): (reason: unknown, promise: unknown) => void {\n return function sendUnhandledPromise(reason: unknown, promise: unknown): void {\n if (getClient() !== client) {\n return;\n }\n\n captureException(reason, {\n originalException: promise,\n captureContext: {\n extra: { unhandledPromiseRejection: true },\n },\n mechanism: {\n handled: false,\n type: 'onunhandledrejection',\n },\n });\n\n handleRejection(reason, options);\n };\n}\n\n/**\n * Handler for `mode` option\n\n */\nfunction handleRejection(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n reason: any,\n options: OnUnhandledRejectionOptions,\n): void {\n // https://github.com/nodejs/node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L234-L240\n const rejectionWarning =\n 'This error originated either by ' +\n 'throwing inside of an async function without a catch block, ' +\n 'or by rejecting a promise which was not handled with .catch().' +\n ' The promise rejected with the reason:';\n\n /* eslint-disable no-console */\n if (options.mode === 'warn') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n console.error(reason && reason.stack ? reason.stack : reason);\n });\n } else if (options.mode === 'strict') {\n consoleSandbox(() => {\n console.warn(rejectionWarning);\n });\n logAndExitProcess(reason);\n }\n /* eslint-enable no-console */\n}\n"],"names":[],"mappings":";;;;AAeA,MAAM,gBAAA,GAAmB,sBAAsB,CAAA;AAC/C;AACA,MAAM,gCAAA,IAAoC,CAAC,OAAO,GAAyC,EAAE,KAAK;AAClG,EAAE,MAAM,IAAK,GAAE,OAAO,CAAC,IAAA,IAAQ,MAAM,CAAA;AACrC;AACA,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,KAAK,CAAC,MAAM,EAAE;AAClB,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,2BAA2B,CAAC,MAAM,EAAE,EAAE,IAAK,EAAC,CAAC,CAAC,CAAA;AAC5F,KAAK;AACL,GAAG,CAAA;AACH,CAAC,CAAE,EAAA;AACH;AACA;AACA;AACA;MACa,+BAAgC,GAAE,iBAAiB,CAAC,gCAAgC,EAAC;AAClG;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,2BAA2B;AAC3C,EAAE,MAAM;AACR,EAAE,OAAO;AACT,EAA+C;AAC/C,EAAE,OAAO,SAAS,oBAAoB,CAAC,MAAM,EAAW,OAAO,EAAiB;AAChF,IAAI,IAAI,SAAS,EAAG,KAAI,MAAM,EAAE;AAChC,MAAM,OAAM;AACZ,KAAI;AACJ;AACA,IAAI,gBAAgB,CAAC,MAAM,EAAE;AAC7B,MAAM,iBAAiB,EAAE,OAAO;AAChC,MAAM,cAAc,EAAE;AACtB,QAAQ,KAAK,EAAE,EAAE,yBAAyB,EAAE,MAAM;AAClD,OAAO;AACP,MAAM,SAAS,EAAE;AACjB,QAAQ,OAAO,EAAE,KAAK;AACtB,QAAQ,IAAI,EAAE,sBAAsB;AACpC,OAAO;AACP,KAAK,CAAC,CAAA;AACN;AACA,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACpC,GAAG,CAAA;AACH,CAAA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,eAAe;AACxB;AACA,EAAE,MAAM;AACR,EAAE,OAAO;AACT,EAAQ;AACR;AACA,EAAE,MAAM,gBAAiB;AACzB,IAAI,kCAAmC;AACvC,IAAI,8DAA+D;AACnE,IAAI,gEAAiE;AACrE,IAAI,wCAAwC,CAAA;AAC5C;AACA;AACA,EAAE,IAAI,OAAO,CAAC,IAAK,KAAI,MAAM,EAAE;AAC/B,IAAI,cAAc,CAAC,MAAM;AACzB,MAAM,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;AACpC;AACA,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,MAAM,CAAC,KAAA,GAAQ,MAAM,CAAC,KAAM,GAAE,MAAM,CAAC,CAAA;AACnE,KAAK,CAAC,CAAA;AACN,GAAE,MAAO,IAAI,OAAO,CAAC,IAAA,KAAS,QAAQ,EAAE;AACxC,IAAI,cAAc,CAAC,MAAM;AACzB,MAAM,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;AACpC,KAAK,CAAC,CAAA;AACN,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAA;AAC7B,GAAE;AACF;AACA;;;;"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import { defineIntegration } from '@sentry/core';
|
|
3
|
+
import { logger, serializeEnvelope } from '@sentry/utils';
|
|
4
|
+
|
|
5
|
+
const INTEGRATION_NAME = 'Spotlight';
|
|
6
|
+
|
|
7
|
+
const _spotlightIntegration = ((options = {}) => {
|
|
8
|
+
const _options = {
|
|
9
|
+
sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
name: INTEGRATION_NAME,
|
|
14
|
+
setup(client) {
|
|
15
|
+
if (typeof process === 'object' && process.env && process.env.NODE_ENV !== 'development') {
|
|
16
|
+
logger.warn("[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?");
|
|
17
|
+
}
|
|
18
|
+
connectToSpotlight(client, _options);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}) ;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Use this integration to send errors and transactions to Spotlight.
|
|
25
|
+
*
|
|
26
|
+
* Learn more about spotlight at https://spotlightjs.com
|
|
27
|
+
*
|
|
28
|
+
* Important: This integration only works with Node 18 or newer.
|
|
29
|
+
*/
|
|
30
|
+
const spotlightIntegration = defineIntegration(_spotlightIntegration);
|
|
31
|
+
|
|
32
|
+
function connectToSpotlight(client, options) {
|
|
33
|
+
const spotlightUrl = parseSidecarUrl(options.sidecarUrl);
|
|
34
|
+
if (!spotlightUrl) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let failedRequests = 0;
|
|
39
|
+
|
|
40
|
+
client.on('beforeEnvelope', (envelope) => {
|
|
41
|
+
if (failedRequests > 3) {
|
|
42
|
+
logger.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const serializedEnvelope = serializeEnvelope(envelope);
|
|
47
|
+
|
|
48
|
+
const request = getNativeHttpRequest();
|
|
49
|
+
const req = request(
|
|
50
|
+
{
|
|
51
|
+
method: 'POST',
|
|
52
|
+
path: spotlightUrl.pathname,
|
|
53
|
+
hostname: spotlightUrl.hostname,
|
|
54
|
+
port: spotlightUrl.port,
|
|
55
|
+
headers: {
|
|
56
|
+
'Content-Type': 'application/x-sentry-envelope',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
res => {
|
|
60
|
+
res.on('data', () => {
|
|
61
|
+
// Drain socket
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
res.on('end', () => {
|
|
65
|
+
// Drain socket
|
|
66
|
+
});
|
|
67
|
+
res.setEncoding('utf8');
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
req.on('error', () => {
|
|
72
|
+
failedRequests++;
|
|
73
|
+
logger.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');
|
|
74
|
+
});
|
|
75
|
+
req.write(serializedEnvelope);
|
|
76
|
+
req.end();
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseSidecarUrl(url) {
|
|
81
|
+
try {
|
|
82
|
+
return new URL(`${url}`);
|
|
83
|
+
} catch (e) {
|
|
84
|
+
logger.warn(`[Spotlight] Invalid sidecar URL: ${url}`);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* We want to get an unpatched http request implementation to avoid capturing our own calls.
|
|
91
|
+
*/
|
|
92
|
+
function getNativeHttpRequest() {
|
|
93
|
+
const { request } = http;
|
|
94
|
+
if (isWrapped(request)) {
|
|
95
|
+
return request.__sentry_original__;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return request;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isWrapped(impl) {
|
|
102
|
+
return '__sentry_original__' in impl;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { getNativeHttpRequest, spotlightIntegration };
|
|
106
|
+
//# sourceMappingURL=spotlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spotlight.js","sources":["../../../src/integrations/spotlight.ts"],"sourcesContent":["import * as http from 'http';\nimport { defineIntegration } from '@sentry/core';\nimport type { Client, Envelope, IntegrationFn } from '@sentry/types';\nimport { logger, serializeEnvelope } from '@sentry/utils';\n\ntype SpotlightConnectionOptions = {\n /**\n * Set this if the Spotlight Sidecar is not running on localhost:8969\n * By default, the Url is set to http://localhost:8969/stream\n */\n sidecarUrl?: string;\n};\n\nconst INTEGRATION_NAME = 'Spotlight';\n\nconst _spotlightIntegration = ((options: Partial<SpotlightConnectionOptions> = {}) => {\n const _options = {\n sidecarUrl: options.sidecarUrl || 'http://localhost:8969/stream',\n };\n\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n if (typeof process === 'object' && process.env && process.env.NODE_ENV !== 'development') {\n logger.warn(\"[Spotlight] It seems you're not in dev mode. Do you really want to have Spotlight enabled?\");\n }\n connectToSpotlight(client, _options);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Use this integration to send errors and transactions to Spotlight.\n *\n * Learn more about spotlight at https://spotlightjs.com\n *\n * Important: This integration only works with Node 18 or newer.\n */\nexport const spotlightIntegration = defineIntegration(_spotlightIntegration);\n\nfunction connectToSpotlight(client: Client, options: Required<SpotlightConnectionOptions>): void {\n const spotlightUrl = parseSidecarUrl(options.sidecarUrl);\n if (!spotlightUrl) {\n return;\n }\n\n let failedRequests = 0;\n\n client.on('beforeEnvelope', (envelope: Envelope) => {\n if (failedRequests > 3) {\n logger.warn('[Spotlight] Disabled Sentry -> Spotlight integration due to too many failed requests');\n return;\n }\n\n const serializedEnvelope = serializeEnvelope(envelope);\n\n const request = getNativeHttpRequest();\n const req = request(\n {\n method: 'POST',\n path: spotlightUrl.pathname,\n hostname: spotlightUrl.hostname,\n port: spotlightUrl.port,\n headers: {\n 'Content-Type': 'application/x-sentry-envelope',\n },\n },\n res => {\n res.on('data', () => {\n // Drain socket\n });\n\n res.on('end', () => {\n // Drain socket\n });\n res.setEncoding('utf8');\n },\n );\n\n req.on('error', () => {\n failedRequests++;\n logger.warn('[Spotlight] Failed to send envelope to Spotlight Sidecar');\n });\n req.write(serializedEnvelope);\n req.end();\n });\n}\n\nfunction parseSidecarUrl(url: string): URL | undefined {\n try {\n return new URL(`${url}`);\n } catch {\n logger.warn(`[Spotlight] Invalid sidecar URL: ${url}`);\n return undefined;\n }\n}\n\ntype HttpRequestImpl = typeof http.request;\ntype WrappedHttpRequest = HttpRequestImpl & { __sentry_original__: HttpRequestImpl };\n\n/**\n * We want to get an unpatched http request implementation to avoid capturing our own calls.\n */\nexport function getNativeHttpRequest(): HttpRequestImpl {\n const { request } = http;\n if (isWrapped(request)) {\n return request.__sentry_original__;\n }\n\n return request;\n}\n\nfunction isWrapped(impl: HttpRequestImpl): impl is WrappedHttpRequest {\n return '__sentry_original__' in impl;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isWrapped } from '@opentelemetry/core';
|
|
2
|
+
import { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';
|
|
3
|
+
import { defineIntegration, isEnabled, captureException } from '@sentry/core';
|
|
4
|
+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
|
|
5
|
+
import { consoleSandbox } from '@sentry/utils';
|
|
6
|
+
|
|
7
|
+
const _connectIntegration = (() => {
|
|
8
|
+
return {
|
|
9
|
+
name: 'Connect',
|
|
10
|
+
setupOnce() {
|
|
11
|
+
addOpenTelemetryInstrumentation(new ConnectInstrumentation({}));
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}) ;
|
|
15
|
+
|
|
16
|
+
const connectIntegration = defineIntegration(_connectIntegration);
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
function connectErrorMiddleware(err, req, res, next) {
|
|
20
|
+
captureException(err);
|
|
21
|
+
next(err);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const setupConnectErrorHandler = (app) => {
|
|
25
|
+
app.use(connectErrorMiddleware);
|
|
26
|
+
|
|
27
|
+
if (!isWrapped(app.use) && isEnabled()) {
|
|
28
|
+
consoleSandbox(() => {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.warn(
|
|
31
|
+
'[Sentry] Connect is not instrumented. This is likely because you required/imported connect before calling `Sentry.init()`.',
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { connectIntegration, setupConnectErrorHandler };
|
|
38
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sources":["../../../../src/integrations/tracing/connect.ts"],"sourcesContent":["import { isWrapped } from '@opentelemetry/core';\nimport { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';\nimport { captureException, defineIntegration, isEnabled } from '@sentry/core';\nimport { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';\nimport type { IntegrationFn } from '@sentry/types';\nimport { consoleSandbox } from '@sentry/utils';\n\ntype ConnectApp = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n use: (middleware: any) => void;\n};\n\nconst _connectIntegration = (() => {\n return {\n name: 'Connect',\n setupOnce() {\n addOpenTelemetryInstrumentation(new ConnectInstrumentation({}));\n },\n };\n}) satisfies IntegrationFn;\n\nexport const connectIntegration = defineIntegration(_connectIntegration);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction connectErrorMiddleware(err: any, req: any, res: any, next: any): void {\n captureException(err);\n next(err);\n}\n\nexport const setupConnectErrorHandler = (app: ConnectApp): void => {\n app.use(connectErrorMiddleware);\n\n if (!isWrapped(app.use) && isEnabled()) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[Sentry] Connect is not instrumented. This is likely because you required/imported connect before calling `Sentry.init()`.',\n );\n });\n }\n};\n"],"names":[],"mappings":";;;;;;AAYA,MAAM,mBAAoB,IAAG,MAAM;AACnC,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,SAAS;AACnB,IAAI,SAAS,GAAG;AAChB,MAAM,+BAA+B,CAAC,IAAI,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAA;AACrE,KAAK;AACL,GAAG,CAAA;AACH,CAAC,CAAE,EAAA;AACH;MACa,kBAAmB,GAAE,iBAAiB,CAAC,mBAAmB,EAAC;AACxE;AACA;AACA,SAAS,sBAAsB,CAAC,GAAG,EAAO,GAAG,EAAO,GAAG,EAAO,IAAI,EAAa;AAC/E,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;AACvB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;AACX,CAAA;AACA;AACa,MAAA,wBAAA,GAA2B,CAAC,GAAG,KAAuB;AACnE,EAAE,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;AACjC;AACA,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,IAAG,SAAS,EAAE,EAAE;AAC1C,IAAI,cAAc,CAAC,MAAM;AACzB;AACA,MAAM,OAAO,CAAC,IAAI;AAClB,QAAQ,4HAA4H;AACpI,OAAO,CAAA;AACP,KAAK,CAAC,CAAA;AACN,GAAE;AACF;;;;"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { _optionalChain } from '@sentry/utils';
|
|
2
|
+
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
|
|
3
|
+
import { defineIntegration, getIsolationScope, getDefaultIsolationScope, getClient, captureException, isEnabled } from '@sentry/core';
|
|
4
|
+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
|
|
5
|
+
import { isWrapped } from '@opentelemetry/core';
|
|
6
|
+
import { logger, consoleSandbox } from '@sentry/utils';
|
|
7
|
+
import { DEBUG_BUILD } from '../../debug-build.js';
|
|
8
|
+
import { addOriginToSpan } from '../../utils/addOriginToSpan.js';
|
|
9
|
+
|
|
10
|
+
const _expressIntegration = (() => {
|
|
11
|
+
return {
|
|
12
|
+
name: 'Express',
|
|
13
|
+
setupOnce() {
|
|
14
|
+
addOpenTelemetryInstrumentation(
|
|
15
|
+
new ExpressInstrumentation({
|
|
16
|
+
requestHook(span) {
|
|
17
|
+
addOriginToSpan(span, 'auto.http.otel.express');
|
|
18
|
+
},
|
|
19
|
+
spanNameHook(info, defaultName) {
|
|
20
|
+
if (getIsolationScope() === getDefaultIsolationScope()) {
|
|
21
|
+
DEBUG_BUILD &&
|
|
22
|
+
logger.warn('Isolation scope is still default isolation scope - skipping setting transactionName');
|
|
23
|
+
return defaultName;
|
|
24
|
+
}
|
|
25
|
+
if (info.layerType === 'request_handler') {
|
|
26
|
+
// type cast b/c Otel unfortunately types info.request as any :(
|
|
27
|
+
const req = info.request ;
|
|
28
|
+
const method = req.method ? req.method.toUpperCase() : 'GET';
|
|
29
|
+
getIsolationScope().setTransactionName(`${method} ${info.route}`);
|
|
30
|
+
}
|
|
31
|
+
return defaultName;
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}) ;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Express integration
|
|
41
|
+
*
|
|
42
|
+
* Capture tracing data for express.
|
|
43
|
+
* In order to capture exceptions, you have to call `setupExpressErrorHandler(app)` before any other middleware and after all controllers.
|
|
44
|
+
*/
|
|
45
|
+
const expressIntegration = defineIntegration(_expressIntegration);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An Express-compatible error handler.
|
|
49
|
+
*/
|
|
50
|
+
function expressErrorHandler(options
|
|
51
|
+
|
|
52
|
+
) {
|
|
53
|
+
return function sentryErrorMiddleware(
|
|
54
|
+
error,
|
|
55
|
+
_req,
|
|
56
|
+
res,
|
|
57
|
+
next,
|
|
58
|
+
) {
|
|
59
|
+
const shouldHandleError = _optionalChain([options, 'optionalAccess', _ => _.shouldHandleError]) || defaultShouldHandleError;
|
|
60
|
+
|
|
61
|
+
if (shouldHandleError(error)) {
|
|
62
|
+
const client = getClient();
|
|
63
|
+
if (client && client.getOptions().autoSessionTracking) {
|
|
64
|
+
// Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the
|
|
65
|
+
// `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only
|
|
66
|
+
// instantiated when the the`requestHandler` middleware is initialised, which indicates that we should be
|
|
67
|
+
// running in SessionAggregates mode
|
|
68
|
+
const isSessionAggregatesMode = client['_sessionFlusher'] !== undefined;
|
|
69
|
+
if (isSessionAggregatesMode) {
|
|
70
|
+
const requestSession = getIsolationScope().getRequestSession();
|
|
71
|
+
// If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a
|
|
72
|
+
// Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within
|
|
73
|
+
// the bounds of a request, and if so the status is updated
|
|
74
|
+
if (requestSession && requestSession.status !== undefined) {
|
|
75
|
+
requestSession.status = 'crashed';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const eventId = captureException(error, { mechanism: { type: 'middleware', handled: false } });
|
|
81
|
+
(res ).sentry = eventId;
|
|
82
|
+
next(error);
|
|
83
|
+
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
next(error);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Setup an error handler for Express.
|
|
93
|
+
* The error handler must be before any other middleware and after all controllers.
|
|
94
|
+
*/
|
|
95
|
+
function setupExpressErrorHandler(app) {
|
|
96
|
+
app.use(expressErrorHandler());
|
|
97
|
+
|
|
98
|
+
if (!isWrapped(app.use) && isEnabled()) {
|
|
99
|
+
consoleSandbox(() => {
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.warn(
|
|
102
|
+
'[Sentry] Express is not instrumented. This is likely because you required/imported express before calling `Sentry.init()`.',
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getStatusCodeFromResponse(error) {
|
|
109
|
+
const statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
|
|
110
|
+
return statusCode ? parseInt(statusCode , 10) : 500;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Returns true if response code is internal server error */
|
|
114
|
+
function defaultShouldHandleError(error) {
|
|
115
|
+
const status = getStatusCodeFromResponse(error);
|
|
116
|
+
return status >= 500;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export { expressErrorHandler, expressIntegration, setupExpressErrorHandler };
|
|
120
|
+
//# sourceMappingURL=express.js.map
|