sentry-vir 4.1.0 → 4.2.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/LICENSE-MIT +1 -1
- package/dist/processing/throttling.d.ts +16 -13
- package/dist/processing/throttling.js +41 -33
- package/package.json +23 -22
package/LICENSE-MIT
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PartialWithUndefined } from '@augment-vir/common';
|
|
2
2
|
import { type ErrorEvent, type EventHint, type TransactionEvent } from '@sentry/core';
|
|
3
3
|
import { type AnyDuration, type FullDate, type UtcTimezone } from 'date-vir';
|
|
4
|
+
import { FuzzyIndex, type FuzzyIndexKey } from 'fuzzy-vir';
|
|
4
5
|
/**
|
|
5
6
|
* Type for entries in {@link throttleCache}.
|
|
6
7
|
*
|
|
@@ -9,14 +10,21 @@ import { type AnyDuration, type FullDate, type UtcTimezone } from 'date-vir';
|
|
|
9
10
|
export type ThrottleCacheEntry = {
|
|
10
11
|
intervalCount: number;
|
|
11
12
|
intervalStartAt: FullDate<UtcTimezone>;
|
|
12
|
-
throttleStartedAt: FullDate<UtcTimezone> | undefined;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
* The current throttle cache
|
|
15
|
+
* The current throttle cache, keyed by a fuzzy cluster key so near-duplicate error messages share a
|
|
16
|
+
* throttle bucket.
|
|
16
17
|
*
|
|
17
18
|
* @category Internal
|
|
18
19
|
*/
|
|
19
|
-
export declare const throttleCache: Map<
|
|
20
|
+
export declare const throttleCache: Map<FuzzyIndexKey, ThrottleCacheEntry>;
|
|
21
|
+
/**
|
|
22
|
+
* Fuzzy index used to group near-duplicate error messages together so they share a single throttle
|
|
23
|
+
* bucket. The `onEvict` hook keeps {@link throttleCache} in sync when a cluster is dropped.
|
|
24
|
+
*
|
|
25
|
+
* @category Internal
|
|
26
|
+
*/
|
|
27
|
+
export declare const fuzzyErrorIndex: FuzzyIndex;
|
|
20
28
|
/**
|
|
21
29
|
* Throttling options.
|
|
22
30
|
*
|
|
@@ -24,19 +32,14 @@ export declare const throttleCache: Map<string, ThrottleCacheEntry>;
|
|
|
24
32
|
*/
|
|
25
33
|
export type ThrottleOptions = {
|
|
26
34
|
disableThrottling: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* When throttling begins, this determines how much time must pass before the error will be
|
|
29
|
-
* logged again.
|
|
30
|
-
*/
|
|
35
|
+
/** Duration over which up to `throttleThreshold` events of the same message are allowed. */
|
|
31
36
|
thresholdInterval: AnyDuration;
|
|
37
|
+
/** Disable the sentry log that fires the first time an error is throttled in an interval. */
|
|
38
|
+
disableThrottleLog: boolean;
|
|
32
39
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
40
|
+
* Within `thresholdInterval`, if an error message is logged more than this many times,
|
|
41
|
+
* additional events are dropped until the interval rolls over.
|
|
35
42
|
*/
|
|
36
|
-
throttleCooldown: AnyDuration;
|
|
37
|
-
/** Enable a sentry log that indicates that an error is being throttled. */
|
|
38
|
-
disableThrottleLog: boolean;
|
|
39
|
-
/** If an error is logged this many times within `logInterval`, it starts getting throttled. */
|
|
40
43
|
throttleThreshold: number;
|
|
41
44
|
};
|
|
42
45
|
/**
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { getOrSetFromMap, mergeDefinedProperties, } from '@augment-vir/common';
|
|
2
2
|
import { calculateRelativeDate, getNowInUtcTimezone, isDateAfter, } from 'date-vir';
|
|
3
|
+
import { FuzzyIndex } from 'fuzzy-vir';
|
|
3
4
|
import { sendLog } from '../logging/send-log.js';
|
|
4
5
|
import { extractOriginalMessage } from './event-processor.js';
|
|
5
6
|
/**
|
|
6
|
-
* The current throttle cache
|
|
7
|
+
* The current throttle cache, keyed by a fuzzy cluster key so near-duplicate error messages share a
|
|
8
|
+
* throttle bucket.
|
|
7
9
|
*
|
|
8
10
|
* @category Internal
|
|
9
11
|
*/
|
|
10
12
|
export const throttleCache = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Fuzzy index used to group near-duplicate error messages together so they share a single throttle
|
|
15
|
+
* bucket. The `onEvict` hook keeps {@link throttleCache} in sync when a cluster is dropped.
|
|
16
|
+
*
|
|
17
|
+
* @category Internal
|
|
18
|
+
*/
|
|
19
|
+
export const fuzzyErrorIndex = new FuzzyIndex({
|
|
20
|
+
onEvict(clusterKey) {
|
|
21
|
+
throttleCache.delete(clusterKey);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
11
24
|
/**
|
|
12
25
|
* Default values for {@link ThrottleOptions}.
|
|
13
26
|
*
|
|
@@ -19,10 +32,7 @@ export const defaultThrottleOptions = {
|
|
|
19
32
|
hours: 1,
|
|
20
33
|
},
|
|
21
34
|
disableThrottleLog: false,
|
|
22
|
-
|
|
23
|
-
days: 1,
|
|
24
|
-
},
|
|
25
|
-
throttleThreshold: 50,
|
|
35
|
+
throttleThreshold: 500,
|
|
26
36
|
};
|
|
27
37
|
/**
|
|
28
38
|
* Determines if an event should be throttled based on previous event counts.
|
|
@@ -38,49 +48,47 @@ hint, userOptions = defaultThrottleOptions) {
|
|
|
38
48
|
if (options.disableThrottling) {
|
|
39
49
|
return false;
|
|
40
50
|
}
|
|
41
|
-
const errorKey = extractOriginalMessage(event, hint);
|
|
51
|
+
const errorKey = fuzzyErrorIndex.insert(extractOriginalMessage(event, hint));
|
|
42
52
|
const now = getNowInUtcTimezone();
|
|
43
53
|
const errorThrottleData = getOrSetFromMap(throttleCache, errorKey, () => {
|
|
44
54
|
return {
|
|
45
55
|
intervalCount: 0,
|
|
46
56
|
intervalStartAt: now,
|
|
47
|
-
throttleStartedAt: undefined,
|
|
48
57
|
};
|
|
49
58
|
});
|
|
50
|
-
errorThrottleData.intervalCount++;
|
|
51
|
-
const thresholdSurpassed = errorThrottleData.intervalCount > options.throttleThreshold;
|
|
52
|
-
if (thresholdSurpassed && !errorThrottleData.throttleStartedAt) {
|
|
53
|
-
errorThrottleData.throttleStartedAt = now;
|
|
54
|
-
if (options.disableThrottleLog) {
|
|
55
|
-
sendLog.warning(`Error throttled: ${errorKey}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
59
|
const intervalNeedsRestart = isDateAfter({
|
|
59
60
|
fullDate: now,
|
|
60
61
|
relativeTo: calculateRelativeDate(errorThrottleData.intervalStartAt, options.thresholdInterval),
|
|
61
62
|
});
|
|
62
63
|
if (intervalNeedsRestart) {
|
|
63
|
-
errorThrottleData.
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
const suppressedCount = errorThrottleData.intervalCount - options.throttleThreshold;
|
|
65
|
+
if (suppressedCount > 0 && !options.disableThrottleLog) {
|
|
66
|
+
sendLog.warning(`Throttling ended after suppressing ${suppressedCount} events: ${errorKey}`, {
|
|
67
|
+
context: {
|
|
68
|
+
suppressedErrorKey: errorKey,
|
|
69
|
+
suppressedCount,
|
|
70
|
+
},
|
|
71
|
+
tags: {
|
|
72
|
+
suppressedErrorKey: errorKey,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
73
75
|
}
|
|
76
|
+
errorThrottleData.intervalStartAt = now;
|
|
74
77
|
errorThrottleData.intervalCount = 0;
|
|
75
78
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
errorThrottleData.intervalCount++;
|
|
80
|
+
const shouldThrottle = errorThrottleData.intervalCount > options.throttleThreshold;
|
|
81
|
+
if (shouldThrottle &&
|
|
82
|
+
errorThrottleData.intervalCount === options.throttleThreshold + 1 &&
|
|
83
|
+
!options.disableThrottleLog) {
|
|
84
|
+
sendLog.warning(`Throttling started: ${errorKey}`, {
|
|
85
|
+
context: {
|
|
86
|
+
suppressedErrorKey: errorKey,
|
|
87
|
+
},
|
|
88
|
+
tags: {
|
|
89
|
+
suppressedErrorKey: errorKey,
|
|
90
|
+
},
|
|
80
91
|
});
|
|
81
|
-
if (shouldStopThrottle && !thresholdSurpassed) {
|
|
82
|
-
errorThrottleData.throttleStartedAt = undefined;
|
|
83
|
-
}
|
|
84
92
|
}
|
|
85
|
-
return
|
|
93
|
+
return shouldThrottle;
|
|
86
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sentry-vir",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Easily use Sentry.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -44,55 +44,56 @@
|
|
|
44
44
|
"test:update": "npm test update"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@augment-vir/assert": "^31.
|
|
48
|
-
"@augment-vir/common": "^31.
|
|
49
|
-
"@sentry/browser": "^10.
|
|
50
|
-
"@sentry/core": "^10.
|
|
51
|
-
"@sentry/node": "^10.
|
|
52
|
-
"date-vir": "^8.2
|
|
53
|
-
"
|
|
47
|
+
"@augment-vir/assert": "^31.70.0",
|
|
48
|
+
"@augment-vir/common": "^31.70.0",
|
|
49
|
+
"@sentry/browser": "^10.53.1",
|
|
50
|
+
"@sentry/core": "^10.53.1",
|
|
51
|
+
"@sentry/node": "^10.53.1",
|
|
52
|
+
"date-vir": "^8.3.2",
|
|
53
|
+
"fuzzy-vir": "^0.0.2",
|
|
54
|
+
"type-fest": "^5.6.0"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
|
-
"@augment-vir/test": "^31.
|
|
57
|
+
"@augment-vir/test": "^31.70.0",
|
|
57
58
|
"@eslint/eslintrc": "^3.3.5",
|
|
58
59
|
"@eslint/js": "^9.39.2",
|
|
59
60
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
60
61
|
"@stylistic/eslint-plugin-ts": "^4.4.1",
|
|
61
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
|
62
63
|
"@web/dev-server-esbuild": "^1.0.5",
|
|
63
64
|
"@web/test-runner": "^0.20.2",
|
|
64
65
|
"@web/test-runner-commands": "^0.9.0",
|
|
65
66
|
"@web/test-runner-playwright": "^0.11.1",
|
|
66
67
|
"@web/test-runner-visual-regression": "^0.10.0",
|
|
67
|
-
"cspell": "^
|
|
68
|
-
"dependency-cruiser": "^17.
|
|
69
|
-
"esbuild": "^0.
|
|
68
|
+
"cspell": "^10.0.0",
|
|
69
|
+
"dependency-cruiser": "^17.4.0",
|
|
70
|
+
"esbuild": "^0.28.0",
|
|
70
71
|
"eslint": "^9.39.2",
|
|
71
72
|
"eslint-config-prettier": "^10.1.8",
|
|
72
|
-
"eslint-plugin-jsdoc": "^
|
|
73
|
+
"eslint-plugin-jsdoc": "^63.0.0",
|
|
73
74
|
"eslint-plugin-monorepo-cop": "^1.0.2",
|
|
74
|
-
"eslint-plugin-playwright": "^2.10.
|
|
75
|
+
"eslint-plugin-playwright": "^2.10.4",
|
|
75
76
|
"eslint-plugin-prettier": "^5.5.5",
|
|
76
77
|
"eslint-plugin-require-extensions": "^0.1.3",
|
|
77
|
-
"eslint-plugin-sonarjs": "^4.0.
|
|
78
|
+
"eslint-plugin-sonarjs": "^4.0.3",
|
|
78
79
|
"eslint-plugin-unicorn": "^64.0.0",
|
|
79
80
|
"istanbul-smart-text-reporter": "^1.1.5",
|
|
80
|
-
"markdown-code-example-inserter": "^3.0.
|
|
81
|
-
"npm-check-updates": "^
|
|
81
|
+
"markdown-code-example-inserter": "^3.0.5",
|
|
82
|
+
"npm-check-updates": "^22.2.0",
|
|
82
83
|
"prettier": "~3.3.3",
|
|
83
84
|
"prettier-plugin-interpolated-html-tags": "^2.0.1",
|
|
84
85
|
"prettier-plugin-jsdoc": "^1.8.0",
|
|
85
|
-
"prettier-plugin-multiline-arrays": "^4.1.
|
|
86
|
+
"prettier-plugin-multiline-arrays": "^4.1.8",
|
|
86
87
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
87
88
|
"prettier-plugin-packagejson": "^3.0.2",
|
|
88
89
|
"prettier-plugin-sort-json": "^4.2.0",
|
|
89
90
|
"prettier-plugin-toml": "^2.0.6",
|
|
90
91
|
"runstorm": "^1.0.0",
|
|
91
|
-
"typedoc": "^0.28.
|
|
92
|
+
"typedoc": "^0.28.19",
|
|
92
93
|
"typescript": "^5.9.3",
|
|
93
94
|
"typescript-eslint": "^8.57.1",
|
|
94
|
-
"virmator": "^14.
|
|
95
|
-
"vite": "^8.0.
|
|
95
|
+
"virmator": "^14.16.0",
|
|
96
|
+
"vite": "^8.0.13"
|
|
96
97
|
},
|
|
97
98
|
"engines": {
|
|
98
99
|
"node": ">=22"
|