@multiplayer-app/session-recorder-common 1.3.32 → 1.3.34
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/dist/esm/sdk/capture-exception.d.ts +1 -0
- package/dist/esm/sdk/capture-exception.d.ts.map +1 -1
- package/dist/esm/sdk/capture-exception.js +60 -4
- package/dist/esm/sdk/capture-exception.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esnext/sdk/capture-exception.d.ts +1 -0
- package/dist/esnext/sdk/capture-exception.d.ts.map +1 -1
- package/dist/esnext/sdk/capture-exception.js +39 -5
- package/dist/esnext/sdk/capture-exception.js.map +1 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +1 -1
- package/dist/src/sdk/capture-exception.d.ts +1 -0
- package/dist/src/sdk/capture-exception.d.ts.map +1 -1
- package/dist/src/sdk/capture-exception.js +40 -5
- package/dist/src/sdk/capture-exception.js.map +1 -1
- package/package.json +1 -1
- package/src/sdk/capture-exception.ts +57 -19
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.captureException = void 0;
|
|
3
|
+
exports.shouldCaptureException = exports.captureException = void 0;
|
|
4
4
|
const api_1 = require("@opentelemetry/api");
|
|
5
5
|
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
|
|
6
6
|
const set_resource_attributes_1 = require("./set-resource-attributes");
|
|
@@ -10,7 +10,7 @@ const set_resource_attributes_1 = require("./set-resource-attributes");
|
|
|
10
10
|
* @returns {void}
|
|
11
11
|
*/
|
|
12
12
|
const captureException = (error, errorInfo) => {
|
|
13
|
-
if (!error) {
|
|
13
|
+
if (!error || !(0, exports.shouldCaptureException)(error)) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
const activeContext = api_1.context.active();
|
|
@@ -18,7 +18,7 @@ const captureException = (error, errorInfo) => {
|
|
|
18
18
|
let isNewSpan = false;
|
|
19
19
|
if (!span || !span.isRecording()) {
|
|
20
20
|
span = api_1.trace.getTracer('exception').startSpan(error.name || 'Error', {
|
|
21
|
-
attributes: Object.assign({ [semantic_conventions_1.ATTR_EXCEPTION_MESSAGE]: error.message, [semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE]: error.stack, [semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name }, (0, set_resource_attributes_1.getResourceAttributes)())
|
|
21
|
+
attributes: Object.assign({ [semantic_conventions_1.ATTR_EXCEPTION_MESSAGE]: error.message, [semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE]: error.stack, [semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name }, (0, set_resource_attributes_1.getResourceAttributes)())
|
|
22
22
|
});
|
|
23
23
|
api_1.trace.setSpan(activeContext, span);
|
|
24
24
|
isNewSpan = true;
|
|
@@ -27,7 +27,7 @@ const captureException = (error, errorInfo) => {
|
|
|
27
27
|
span.setAttributes({
|
|
28
28
|
[semantic_conventions_1.ATTR_EXCEPTION_MESSAGE]: error.message,
|
|
29
29
|
[semantic_conventions_1.ATTR_EXCEPTION_STACKTRACE]: error.stack,
|
|
30
|
-
[semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name
|
|
30
|
+
[semantic_conventions_1.ATTR_EXCEPTION_TYPE]: error.name
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
if (errorInfo) {
|
|
@@ -38,11 +38,46 @@ const captureException = (error, errorInfo) => {
|
|
|
38
38
|
span.recordException(error);
|
|
39
39
|
span.setStatus({
|
|
40
40
|
code: api_1.SpanStatusCode.ERROR,
|
|
41
|
-
message: error.message
|
|
41
|
+
message: error.message
|
|
42
42
|
});
|
|
43
43
|
if (isNewSpan) {
|
|
44
44
|
span.end();
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
exports.captureException = captureException;
|
|
48
|
+
/**
|
|
49
|
+
* Best-effort deduplication of exceptions that fire multiple times
|
|
50
|
+
* (e.g. framework handler + global handlers) within a short time window.
|
|
51
|
+
*/
|
|
52
|
+
const exceptionDedupeWindowMs = 2000;
|
|
53
|
+
const recentExceptionFingerprints = new Map();
|
|
54
|
+
const shouldCaptureException = (error, _errorInfo) => {
|
|
55
|
+
if (!error)
|
|
56
|
+
return false;
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
// Build a fingerprint that is stable enough across repeated emissions
|
|
59
|
+
// but not so broad that different errors collapse into one.
|
|
60
|
+
const keyParts = [];
|
|
61
|
+
keyParts.push(error.name || 'Error');
|
|
62
|
+
keyParts.push(error.message || '');
|
|
63
|
+
// First stack line tends to include file/line where it originated.
|
|
64
|
+
if (typeof error.stack === 'string') {
|
|
65
|
+
const firstFrame = error.stack.split('\n')[1] || '';
|
|
66
|
+
keyParts.push(firstFrame.trim());
|
|
67
|
+
}
|
|
68
|
+
const fingerprint = keyParts.join('|').slice(0, 500);
|
|
69
|
+
const lastSeen = recentExceptionFingerprints.get(fingerprint);
|
|
70
|
+
if (lastSeen && now - lastSeen < exceptionDedupeWindowMs) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
recentExceptionFingerprints.set(fingerprint, now);
|
|
74
|
+
// Cheap cleanup of old entries to avoid unbounded growth.
|
|
75
|
+
for (const [key, ts] of recentExceptionFingerprints) {
|
|
76
|
+
if (now - ts > exceptionDedupeWindowMs * 5) {
|
|
77
|
+
recentExceptionFingerprints.delete(key);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
};
|
|
82
|
+
exports.shouldCaptureException = shouldCaptureException;
|
|
48
83
|
//# sourceMappingURL=capture-exception.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture-exception.js","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":";;;AAAA,4CAAmE;AACnE,8EAI4C;AAC5C,uEAAiE;AAEjE;;;;GAIG;AACI,MAAM,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"capture-exception.js","sourceRoot":"","sources":["../../../src/sdk/capture-exception.ts"],"names":[],"mappings":"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","sourcesContent":["import { context, trace, SpanStatusCode } from '@opentelemetry/api'\nimport {\n ATTR_EXCEPTION_MESSAGE,\n ATTR_EXCEPTION_STACKTRACE,\n ATTR_EXCEPTION_TYPE\n} from '@opentelemetry/semantic-conventions'\nimport { getResourceAttributes } from './set-resource-attributes'\n\n/**\n * @description Add error to current span\n * @param {Error} error\n * @returns {void}\n */\nexport const captureException = (error: Error, errorInfo?: Record<string, any>) => {\n if (!error || !shouldCaptureException(error)) {\n return\n }\n\n const activeContext = context.active()\n\n let span = trace.getSpan(activeContext)\n let isNewSpan = false\n\n if (!span || !span.isRecording()) {\n span = trace.getTracer('exception').startSpan(error.name || 'Error', {\n attributes: {\n [ATTR_EXCEPTION_MESSAGE]: error.message,\n [ATTR_EXCEPTION_STACKTRACE]: error.stack,\n [ATTR_EXCEPTION_TYPE]: error.name,\n ...getResourceAttributes()\n }\n })\n trace.setSpan(activeContext, span)\n isNewSpan = true\n } else {\n span.setAttributes({\n [ATTR_EXCEPTION_MESSAGE]: error.message,\n [ATTR_EXCEPTION_STACKTRACE]: error.stack,\n [ATTR_EXCEPTION_TYPE]: error.name\n })\n }\n\n if (errorInfo) {\n Object.entries(errorInfo).forEach(([key, value]) => {\n span.setAttribute(`error_info.${key}`, value)\n })\n }\n\n span.recordException(error)\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error.message\n })\n\n if (isNewSpan) {\n span.end()\n }\n}\n\n/**\n * Best-effort deduplication of exceptions that fire multiple times\n * (e.g. framework handler + global handlers) within a short time window.\n */\n\nconst exceptionDedupeWindowMs = 2000\nconst recentExceptionFingerprints = new Map<string, number>()\n\nexport const shouldCaptureException = (error: Error, _errorInfo?: Record<string, any>): boolean => {\n if (!error) return false\n\n const now = Date.now()\n\n // Build a fingerprint that is stable enough across repeated emissions\n // but not so broad that different errors collapse into one.\n const keyParts: string[] = []\n keyParts.push(error.name || 'Error')\n keyParts.push(error.message || '')\n\n // First stack line tends to include file/line where it originated.\n if (typeof error.stack === 'string') {\n const firstFrame = error.stack.split('\\n')[1] || ''\n keyParts.push(firstFrame.trim())\n }\n\n const fingerprint = keyParts.join('|').slice(0, 500)\n\n const lastSeen = recentExceptionFingerprints.get(fingerprint)\n if (lastSeen && now - lastSeen < exceptionDedupeWindowMs) {\n return false\n }\n\n recentExceptionFingerprints.set(fingerprint, now)\n\n // Cheap cleanup of old entries to avoid unbounded growth.\n for (const [key, ts] of recentExceptionFingerprints) {\n if (now - ts > exceptionDedupeWindowMs * 5) {\n recentExceptionFingerprints.delete(key)\n }\n }\n\n return true\n}\n"]}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { context, trace, SpanStatusCode } from '@opentelemetry/api'
|
|
|
2
2
|
import {
|
|
3
3
|
ATTR_EXCEPTION_MESSAGE,
|
|
4
4
|
ATTR_EXCEPTION_STACKTRACE,
|
|
5
|
-
ATTR_EXCEPTION_TYPE
|
|
5
|
+
ATTR_EXCEPTION_TYPE
|
|
6
6
|
} from '@opentelemetry/semantic-conventions'
|
|
7
7
|
import { getResourceAttributes } from './set-resource-attributes'
|
|
8
8
|
|
|
@@ -11,11 +11,8 @@ import { getResourceAttributes } from './set-resource-attributes'
|
|
|
11
11
|
* @param {Error} error
|
|
12
12
|
* @returns {void}
|
|
13
13
|
*/
|
|
14
|
-
export const captureException = (
|
|
15
|
-
error
|
|
16
|
-
errorInfo?: Record<string, any>,
|
|
17
|
-
) => {
|
|
18
|
-
if (!error) {
|
|
14
|
+
export const captureException = (error: Error, errorInfo?: Record<string, any>) => {
|
|
15
|
+
if (!error || !shouldCaptureException(error)) {
|
|
19
16
|
return
|
|
20
17
|
}
|
|
21
18
|
|
|
@@ -25,24 +22,21 @@ export const captureException = (
|
|
|
25
22
|
let isNewSpan = false
|
|
26
23
|
|
|
27
24
|
if (!span || !span.isRecording()) {
|
|
28
|
-
span = trace.getTracer('exception').startSpan(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
)
|
|
25
|
+
span = trace.getTracer('exception').startSpan(error.name || 'Error', {
|
|
26
|
+
attributes: {
|
|
27
|
+
[ATTR_EXCEPTION_MESSAGE]: error.message,
|
|
28
|
+
[ATTR_EXCEPTION_STACKTRACE]: error.stack,
|
|
29
|
+
[ATTR_EXCEPTION_TYPE]: error.name,
|
|
30
|
+
...getResourceAttributes()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
39
33
|
trace.setSpan(activeContext, span)
|
|
40
34
|
isNewSpan = true
|
|
41
35
|
} else {
|
|
42
36
|
span.setAttributes({
|
|
43
37
|
[ATTR_EXCEPTION_MESSAGE]: error.message,
|
|
44
38
|
[ATTR_EXCEPTION_STACKTRACE]: error.stack,
|
|
45
|
-
[ATTR_EXCEPTION_TYPE]: error.name
|
|
39
|
+
[ATTR_EXCEPTION_TYPE]: error.name
|
|
46
40
|
})
|
|
47
41
|
}
|
|
48
42
|
|
|
@@ -55,10 +49,54 @@ export const captureException = (
|
|
|
55
49
|
span.recordException(error)
|
|
56
50
|
span.setStatus({
|
|
57
51
|
code: SpanStatusCode.ERROR,
|
|
58
|
-
message: error.message
|
|
52
|
+
message: error.message
|
|
59
53
|
})
|
|
60
54
|
|
|
61
55
|
if (isNewSpan) {
|
|
62
56
|
span.end()
|
|
63
57
|
}
|
|
64
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Best-effort deduplication of exceptions that fire multiple times
|
|
62
|
+
* (e.g. framework handler + global handlers) within a short time window.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
const exceptionDedupeWindowMs = 2000
|
|
66
|
+
const recentExceptionFingerprints = new Map<string, number>()
|
|
67
|
+
|
|
68
|
+
export const shouldCaptureException = (error: Error, _errorInfo?: Record<string, any>): boolean => {
|
|
69
|
+
if (!error) return false
|
|
70
|
+
|
|
71
|
+
const now = Date.now()
|
|
72
|
+
|
|
73
|
+
// Build a fingerprint that is stable enough across repeated emissions
|
|
74
|
+
// but not so broad that different errors collapse into one.
|
|
75
|
+
const keyParts: string[] = []
|
|
76
|
+
keyParts.push(error.name || 'Error')
|
|
77
|
+
keyParts.push(error.message || '')
|
|
78
|
+
|
|
79
|
+
// First stack line tends to include file/line where it originated.
|
|
80
|
+
if (typeof error.stack === 'string') {
|
|
81
|
+
const firstFrame = error.stack.split('\n')[1] || ''
|
|
82
|
+
keyParts.push(firstFrame.trim())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const fingerprint = keyParts.join('|').slice(0, 500)
|
|
86
|
+
|
|
87
|
+
const lastSeen = recentExceptionFingerprints.get(fingerprint)
|
|
88
|
+
if (lastSeen && now - lastSeen < exceptionDedupeWindowMs) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
recentExceptionFingerprints.set(fingerprint, now)
|
|
93
|
+
|
|
94
|
+
// Cheap cleanup of old entries to avoid unbounded growth.
|
|
95
|
+
for (const [key, ts] of recentExceptionFingerprints) {
|
|
96
|
+
if (now - ts > exceptionDedupeWindowMs * 5) {
|
|
97
|
+
recentExceptionFingerprints.delete(key)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return true
|
|
102
|
+
}
|