@vtex/api 6.52.0 → 6.52.5
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/CHANGELOG.md +50 -24
- package/lib/HttpClient/middlewares/cache.js +2 -2
- package/lib/HttpClient/middlewares/request/index.js +1 -1
- package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/spanSetup.js +1 -1
- package/lib/caches/MultilayeredCache.js +1 -1
- package/lib/service/logger/client.js +3 -3
- package/lib/service/logger/logger.d.ts +1 -1
- package/lib/service/logger/logger.js +3 -3
- package/lib/service/master.d.ts +0 -2
- package/lib/service/master.js +3 -15
- package/lib/service/metrics/requestMetricsMiddleware.js +4 -5
- package/lib/service/telemetry/client.js +18 -18
- package/lib/service/worker/index.d.ts +0 -1
- package/lib/service/worker/index.js +7 -17
- package/lib/service/worker/listeners.js +5 -3
- package/lib/service/worker/runtime/builtIn/middlewares.d.ts +1 -1
- package/lib/service/worker/runtime/builtIn/middlewares.js +2 -7
- package/lib/service/worker/runtime/graphql/middlewares/error.js +8 -2
- package/lib/service/worker/runtime/graphql/schema/schemaDirectives/Auth.js +2 -2
- package/lib/service/worker/runtime/http/middlewares/error.js +20 -2
- package/lib/service/worker/runtime/http/middlewares/sanitizeResponse.d.ts +37 -0
- package/lib/service/worker/runtime/http/middlewares/sanitizeResponse.js +143 -0
- package/lib/service/worker/runtime/statusTrack.js +1 -4
- package/lib/utils/buildFullPath.js +2 -2
- package/lib/utils/responseSanitizer.d.ts +52 -0
- package/lib/utils/responseSanitizer.js +354 -0
- package/lib/utils/stringRedactor.d.ts +19 -0
- package/lib/utils/stringRedactor.js +94 -0
- package/package.json +1 -1
- package/lib/service/metrics/clusterMetricsAggregator.d.ts +0 -72
- package/lib/service/metrics/clusterMetricsAggregator.js +0 -181
- package/lib/service/metrics/requestHandlerLabel.d.ts +0 -26
- package/lib/service/metrics/requestHandlerLabel.js +0 -30
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.redactSecrets = exports.REDACTED = void 0;
|
|
4
|
+
exports.REDACTED = '[Redacted]';
|
|
5
|
+
// Shorter values are not credentials, and replacing them everywhere would damage ordinary text.
|
|
6
|
+
const MIN_SECRET_LENGTH = 16;
|
|
7
|
+
/**
|
|
8
|
+
* Names under which credentials travel in HTTP headers. When an error is turned into text with
|
|
9
|
+
* `JSON.stringify`, its request config, and the headers in it, end up in the text under these keys.
|
|
10
|
+
*/
|
|
11
|
+
const SENSITIVE_KEYS = [
|
|
12
|
+
'authorization',
|
|
13
|
+
'cookie',
|
|
14
|
+
'proxy-authorization',
|
|
15
|
+
'vtexidclientautcookie',
|
|
16
|
+
'x-vtex-api-appkey',
|
|
17
|
+
'x-vtex-api-apptoken',
|
|
18
|
+
'x-vtex-credential',
|
|
19
|
+
'x-vtex-session',
|
|
20
|
+
];
|
|
21
|
+
// `"key":"value"`, also when the text was serialized twice (`\"key\":\"value\"`). Captures
|
|
22
|
+
// everything up to the value, which is what is replaced. The value is a run of characters that are
|
|
23
|
+
// not a quote, where a backslash only escapes the character after it. The two alternatives of the
|
|
24
|
+
// run exclude each other, so the match is linear.
|
|
25
|
+
const KEYED_VALUE = new RegExp(String.raw `(\\{0,2}"(?:${SENSITIVE_KEYS.join('|')})\\{0,2}"\s*:\s*\\{0,2}")(?:[^"\\]|\\[^"])+`, 'gi');
|
|
26
|
+
// A header written as a line of the request (`Proxy-Authorization: value`), which is how a
|
|
27
|
+
// serialized request object (`_header`) holds it. Short values are not credentials. `Cookie` is
|
|
28
|
+
// not in this list: the word opens ordinary sentences, so its lines are matched separately.
|
|
29
|
+
const HEADER_LINE_NAMES = [
|
|
30
|
+
'authorization',
|
|
31
|
+
'proxy-authorization',
|
|
32
|
+
'vtexidclientautcookie',
|
|
33
|
+
'x-vtex-api-appkey',
|
|
34
|
+
'x-vtex-api-apptoken',
|
|
35
|
+
'x-vtex-credential',
|
|
36
|
+
'x-vtex-session',
|
|
37
|
+
];
|
|
38
|
+
// The name starts a line. In a serialized request the line break is the two characters `\n`, so a
|
|
39
|
+
// word boundary never fires there: the `n` and the name are both word characters.
|
|
40
|
+
const AT_LINE_START = String.raw `(?<=^|\\n|\\r|\n|\r)`;
|
|
41
|
+
const HEADER_LINE = new RegExp(String.raw `${AT_LINE_START}((?:${HEADER_LINE_NAMES.join('|')})[ \t]*:[ \t]*)[^\s"\\][^\r\n"\\]{7,}`, 'gi');
|
|
42
|
+
// A `Cookie` or `Set-Cookie` line. The value has to be cookie pairs (`name=value`), so a sentence
|
|
43
|
+
// that happens to start with "Cookie:" is left as it is.
|
|
44
|
+
const COOKIE_LINE = new RegExp(String.raw `${AT_LINE_START}((?:set-)?cookie[ \t]*:[ \t]*)[^\s=;"\\]+=[^\r\n"\\]{6,}`, 'gi');
|
|
45
|
+
// The VTEX ID cookie, in a `Cookie` header or a `Set-Cookie` line.
|
|
46
|
+
const VTEX_ID_COOKIE = /\b(VtexIdclientAutCookie(?:_[a-z0-9-]+)?=)[^;\s"\\]+/gi;
|
|
47
|
+
// An `Authorization` value that is not under a key, as in a message or a copied header line.
|
|
48
|
+
const BEARER_TOKEN = /\b(Bearer\s+)[a-z0-9._~+/=-]{16,}/gi;
|
|
49
|
+
const redactKnown = (text, secrets) => {
|
|
50
|
+
let value = text;
|
|
51
|
+
let redacted = 0;
|
|
52
|
+
for (const secret of secrets) {
|
|
53
|
+
if (!secret || secret.length < MIN_SECRET_LENGTH || !value.includes(secret)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const parts = value.split(secret);
|
|
57
|
+
redacted += parts.length - 1;
|
|
58
|
+
value = parts.join(exports.REDACTED);
|
|
59
|
+
}
|
|
60
|
+
return { redacted, value };
|
|
61
|
+
};
|
|
62
|
+
const redactPattern = (text, pattern) => {
|
|
63
|
+
let redacted = 0;
|
|
64
|
+
const value = text.replace(pattern, (match, prefix) => {
|
|
65
|
+
if (match === `${prefix}${exports.REDACTED}`) {
|
|
66
|
+
return match;
|
|
67
|
+
}
|
|
68
|
+
redacted++;
|
|
69
|
+
return `${prefix}${exports.REDACTED}`;
|
|
70
|
+
});
|
|
71
|
+
return { redacted, value: redacted > 0 ? value : text };
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Replaces credentials in a text: the given secrets wherever they appear, the value of the
|
|
75
|
+
* credential headers in JSON text and in header lines, `Cookie` and `Set-Cookie` lines, the VTEX
|
|
76
|
+
* ID cookie, and `Bearer` tokens.
|
|
77
|
+
* The rest of the text is kept.
|
|
78
|
+
*
|
|
79
|
+
* It is for text that was built from an error, where the error object is no longer there to
|
|
80
|
+
* detect. It does not inspect anything else: a credential that is neither one of the secrets nor
|
|
81
|
+
* under one of those names is not found.
|
|
82
|
+
*/
|
|
83
|
+
const redactSecrets = (text, secrets = []) => {
|
|
84
|
+
const known = redactKnown(text, secrets);
|
|
85
|
+
let value = known.value;
|
|
86
|
+
let redacted = known.redacted;
|
|
87
|
+
for (const pattern of [KEYED_VALUE, HEADER_LINE, COOKIE_LINE, VTEX_ID_COOKIE, BEARER_TOKEN]) {
|
|
88
|
+
const result = redactPattern(value, pattern);
|
|
89
|
+
value = result.value;
|
|
90
|
+
redacted += result.redacted;
|
|
91
|
+
}
|
|
92
|
+
return { redacted, value: redacted > 0 ? value : text };
|
|
93
|
+
};
|
|
94
|
+
exports.redactSecrets = redactSecrets;
|
package/package.json
CHANGED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { Worker } from 'cluster';
|
|
2
|
-
import { AggregatorRegistry } from 'prom-client';
|
|
3
|
-
/**
|
|
4
|
-
* Cluster-wide Prometheus metrics aggregation.
|
|
5
|
-
*
|
|
6
|
-
* In multi-worker mode each cluster worker keeps its own prom-client default
|
|
7
|
-
* registry. Serving `/metrics` from a single (round-robin selected) worker
|
|
8
|
-
* therefore exposes only that worker's local counters, which makes Prometheus
|
|
9
|
-
* treat the per-scrape braid of independent counters as counter resets and
|
|
10
|
-
* inflates `rate()`/`increase()` by orders of magnitude.
|
|
11
|
-
*
|
|
12
|
-
* This module uses prom-client's `AggregatorRegistry` cluster IPC protocol so
|
|
13
|
-
* that the worker answering a scrape asks the master for a merged, monotonic
|
|
14
|
-
* view built from every worker's registry in one pass (no double-counting of
|
|
15
|
-
* the answering worker).
|
|
16
|
-
*/
|
|
17
|
-
/** Tagged IPC message: worker -> master, "please build the cluster aggregate". */
|
|
18
|
-
export declare const AGG_METRICS_REQ = "vtex-api:aggMetricsReq";
|
|
19
|
-
/** Tagged IPC message: master -> worker, carries the aggregate (or an error). */
|
|
20
|
-
export declare const AGG_METRICS_RES = "vtex-api:aggMetricsRes";
|
|
21
|
-
export interface AggMetricsReqMessage {
|
|
22
|
-
type: typeof AGG_METRICS_REQ;
|
|
23
|
-
id: number;
|
|
24
|
-
}
|
|
25
|
-
export interface AggMetricsResMessage {
|
|
26
|
-
type: typeof AGG_METRICS_RES;
|
|
27
|
-
id: number;
|
|
28
|
-
body?: string;
|
|
29
|
-
error?: string;
|
|
30
|
-
}
|
|
31
|
-
export declare const isAggMetricsRequest: (message: any) => message is AggMetricsReqMessage;
|
|
32
|
-
export declare const isAggMetricsResponse: (message: any) => message is AggMetricsResMessage;
|
|
33
|
-
/**
|
|
34
|
-
* prom-client's cluster protocol emits its own tagged IPC messages
|
|
35
|
-
* (`prom-client:getMetricsReq` / `prom-client:getMetricsRes`). They are handled
|
|
36
|
-
* by prom-client's own cluster listeners, so our `onMessage` handlers must
|
|
37
|
-
* ignore them instead of warning about "unknown" messages.
|
|
38
|
-
*/
|
|
39
|
-
export declare const isPromClientMessage: (message: any) => boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Constructs the master-side `AggregatorRegistry` (idempotent). Its constructor
|
|
42
|
-
* installs prom-client's master `cluster.on('message')` collector, which is
|
|
43
|
-
* what makes `clusterMetrics()` work. Must be called in the master before any
|
|
44
|
-
* worker can answer a scrape.
|
|
45
|
-
*/
|
|
46
|
-
export declare const initMasterAggregatorRegistry: () => AggregatorRegistry;
|
|
47
|
-
/**
|
|
48
|
-
* Handles an `AGG_METRICS_REQ` from a worker: builds the cluster aggregate and
|
|
49
|
-
* replies to that worker only. prom-client fans the request out to *all*
|
|
50
|
-
* connected workers (including the requester), so the requester is one of the N
|
|
51
|
-
* merged sources and is never double-counted.
|
|
52
|
-
*/
|
|
53
|
-
export declare const handleWorkerMetricsRequest: (worker: Worker, message: AggMetricsReqMessage) => Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Constructs the worker-side `AggregatorRegistry` (idempotent). Its constructor
|
|
56
|
-
* installs prom-client's worker `process.on('message')` responder that answers
|
|
57
|
-
* the master's `getMetricsReq` with this worker's registry JSON.
|
|
58
|
-
*/
|
|
59
|
-
export declare const ensureWorkerAggregatorRegistry: () => AggregatorRegistry;
|
|
60
|
-
/**
|
|
61
|
-
* Requests the cluster aggregate from the master over IPC and resolves with the
|
|
62
|
-
* merged exposition string. On timeout or any error it falls back to this
|
|
63
|
-
* worker's local `register.metrics()` so `/metrics` always answers promptly.
|
|
64
|
-
*/
|
|
65
|
-
export declare const requestAggregatedMetrics: () => Promise<string>;
|
|
66
|
-
/**
|
|
67
|
-
* Resolves the pending `requestAggregatedMetrics` promise correlated by id when
|
|
68
|
-
* the master replies. On error it falls back to the local registry.
|
|
69
|
-
*/
|
|
70
|
-
export declare const handleMasterMetricsResponse: (message: AggMetricsResMessage) => void;
|
|
71
|
-
/** Test-only helper to reset module state between test cases. */
|
|
72
|
-
export declare const __resetForTests: () => void;
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.__resetForTests = exports.handleMasterMetricsResponse = exports.requestAggregatedMetrics = exports.ensureWorkerAggregatorRegistry = exports.handleWorkerMetricsRequest = exports.initMasterAggregatorRegistry = exports.isPromClientMessage = exports.isAggMetricsResponse = exports.isAggMetricsRequest = exports.AGG_METRICS_RES = exports.AGG_METRICS_REQ = void 0;
|
|
4
|
-
const prom_client_1 = require("prom-client");
|
|
5
|
-
const listeners_1 = require("../worker/listeners");
|
|
6
|
-
/**
|
|
7
|
-
* Cluster-wide Prometheus metrics aggregation.
|
|
8
|
-
*
|
|
9
|
-
* In multi-worker mode each cluster worker keeps its own prom-client default
|
|
10
|
-
* registry. Serving `/metrics` from a single (round-robin selected) worker
|
|
11
|
-
* therefore exposes only that worker's local counters, which makes Prometheus
|
|
12
|
-
* treat the per-scrape braid of independent counters as counter resets and
|
|
13
|
-
* inflates `rate()`/`increase()` by orders of magnitude.
|
|
14
|
-
*
|
|
15
|
-
* This module uses prom-client's `AggregatorRegistry` cluster IPC protocol so
|
|
16
|
-
* that the worker answering a scrape asks the master for a merged, monotonic
|
|
17
|
-
* view built from every worker's registry in one pass (no double-counting of
|
|
18
|
-
* the answering worker).
|
|
19
|
-
*/
|
|
20
|
-
/** Tagged IPC message: worker -> master, "please build the cluster aggregate". */
|
|
21
|
-
exports.AGG_METRICS_REQ = 'vtex-api:aggMetricsReq';
|
|
22
|
-
/** Tagged IPC message: master -> worker, carries the aggregate (or an error). */
|
|
23
|
-
exports.AGG_METRICS_RES = 'vtex-api:aggMetricsRes';
|
|
24
|
-
/**
|
|
25
|
-
* Upper bound for a worker waiting on the master aggregate. prom-client's own
|
|
26
|
-
* `clusterMetrics()` has a 5s timeout collecting worker registries; we guard
|
|
27
|
-
* slightly above that and fall back to the local registry on expiry so that
|
|
28
|
-
* `/metrics` never hangs or returns empty.
|
|
29
|
-
*/
|
|
30
|
-
const AGG_METRICS_TIMEOUT_MS = 6000;
|
|
31
|
-
const isAggMetricsRequest = (message) => (message === null || message === void 0 ? void 0 : message.type) === exports.AGG_METRICS_REQ && typeof (message === null || message === void 0 ? void 0 : message.id) === 'number';
|
|
32
|
-
exports.isAggMetricsRequest = isAggMetricsRequest;
|
|
33
|
-
const isAggMetricsResponse = (message) => (message === null || message === void 0 ? void 0 : message.type) === exports.AGG_METRICS_RES && typeof (message === null || message === void 0 ? void 0 : message.id) === 'number';
|
|
34
|
-
exports.isAggMetricsResponse = isAggMetricsResponse;
|
|
35
|
-
/**
|
|
36
|
-
* prom-client's cluster protocol emits its own tagged IPC messages
|
|
37
|
-
* (`prom-client:getMetricsReq` / `prom-client:getMetricsRes`). They are handled
|
|
38
|
-
* by prom-client's own cluster listeners, so our `onMessage` handlers must
|
|
39
|
-
* ignore them instead of warning about "unknown" messages.
|
|
40
|
-
*/
|
|
41
|
-
const isPromClientMessage = (message) => typeof (message === null || message === void 0 ? void 0 : message.type) === 'string' && message.type.startsWith('prom-client:');
|
|
42
|
-
exports.isPromClientMessage = isPromClientMessage;
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
// Master side
|
|
45
|
-
// ---------------------------------------------------------------------------
|
|
46
|
-
let masterAggregatorRegistry;
|
|
47
|
-
/**
|
|
48
|
-
* Constructs the master-side `AggregatorRegistry` (idempotent). Its constructor
|
|
49
|
-
* installs prom-client's master `cluster.on('message')` collector, which is
|
|
50
|
-
* what makes `clusterMetrics()` work. Must be called in the master before any
|
|
51
|
-
* worker can answer a scrape.
|
|
52
|
-
*/
|
|
53
|
-
const initMasterAggregatorRegistry = () => {
|
|
54
|
-
if (!masterAggregatorRegistry) {
|
|
55
|
-
masterAggregatorRegistry = new prom_client_1.AggregatorRegistry();
|
|
56
|
-
}
|
|
57
|
-
return masterAggregatorRegistry;
|
|
58
|
-
};
|
|
59
|
-
exports.initMasterAggregatorRegistry = initMasterAggregatorRegistry;
|
|
60
|
-
/**
|
|
61
|
-
* Handles an `AGG_METRICS_REQ` from a worker: builds the cluster aggregate and
|
|
62
|
-
* replies to that worker only. prom-client fans the request out to *all*
|
|
63
|
-
* connected workers (including the requester), so the requester is one of the N
|
|
64
|
-
* merged sources and is never double-counted.
|
|
65
|
-
*/
|
|
66
|
-
const handleWorkerMetricsRequest = async (worker, message) => {
|
|
67
|
-
var _a, _b;
|
|
68
|
-
const aggregator = (0, exports.initMasterAggregatorRegistry)();
|
|
69
|
-
let response;
|
|
70
|
-
try {
|
|
71
|
-
const body = await aggregator.clusterMetrics();
|
|
72
|
-
response = { type: exports.AGG_METRICS_RES, id: message.id, body };
|
|
73
|
-
}
|
|
74
|
-
catch (err) {
|
|
75
|
-
response = { type: exports.AGG_METRICS_RES, id: message.id, error: (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
76
|
-
}
|
|
77
|
-
// `worker.send` can throw (e.g. the worker disconnected between the request
|
|
78
|
-
// and the reply). This handler is invoked without `await` from the master's
|
|
79
|
-
// message listener, so a thrown send would surface as an unhandled rejection
|
|
80
|
-
// in the master process. Swallow and log it instead.
|
|
81
|
-
try {
|
|
82
|
-
worker.send(response);
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
listeners_1.logger.warn({
|
|
86
|
-
content: (_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : String(err),
|
|
87
|
-
message: 'Failed to send aggregated cluster metrics to worker',
|
|
88
|
-
pid: process.pid,
|
|
89
|
-
workerId: worker.id,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
exports.handleWorkerMetricsRequest = handleWorkerMetricsRequest;
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
// Worker side
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
|
-
let workerAggregatorRegistry;
|
|
98
|
-
const pendingRequests = new Map();
|
|
99
|
-
let requestCounter = 0;
|
|
100
|
-
/**
|
|
101
|
-
* Constructs the worker-side `AggregatorRegistry` (idempotent). Its constructor
|
|
102
|
-
* installs prom-client's worker `process.on('message')` responder that answers
|
|
103
|
-
* the master's `getMetricsReq` with this worker's registry JSON.
|
|
104
|
-
*/
|
|
105
|
-
const ensureWorkerAggregatorRegistry = () => {
|
|
106
|
-
if (!workerAggregatorRegistry) {
|
|
107
|
-
workerAggregatorRegistry = new prom_client_1.AggregatorRegistry();
|
|
108
|
-
}
|
|
109
|
-
return workerAggregatorRegistry;
|
|
110
|
-
};
|
|
111
|
-
exports.ensureWorkerAggregatorRegistry = ensureWorkerAggregatorRegistry;
|
|
112
|
-
/**
|
|
113
|
-
* Requests the cluster aggregate from the master over IPC and resolves with the
|
|
114
|
-
* merged exposition string. On timeout or any error it falls back to this
|
|
115
|
-
* worker's local `register.metrics()` so `/metrics` always answers promptly.
|
|
116
|
-
*/
|
|
117
|
-
const requestAggregatedMetrics = async () => {
|
|
118
|
-
if (typeof process.send !== 'function') {
|
|
119
|
-
return prom_client_1.register.metrics();
|
|
120
|
-
}
|
|
121
|
-
const id = requestCounter++;
|
|
122
|
-
return new Promise((resolve) => {
|
|
123
|
-
var _a;
|
|
124
|
-
const timer = setTimeout(() => {
|
|
125
|
-
pendingRequests.delete(id);
|
|
126
|
-
listeners_1.logger.warn({
|
|
127
|
-
message: 'Timed out waiting for aggregated cluster metrics; falling back to local registry',
|
|
128
|
-
pid: process.pid,
|
|
129
|
-
});
|
|
130
|
-
prom_client_1.register.metrics().then(resolve).catch(() => resolve(''));
|
|
131
|
-
}, AGG_METRICS_TIMEOUT_MS);
|
|
132
|
-
pendingRequests.set(id, { resolve, timer });
|
|
133
|
-
try {
|
|
134
|
-
process.send({ type: exports.AGG_METRICS_REQ, id });
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
clearTimeout(timer);
|
|
138
|
-
pendingRequests.delete(id);
|
|
139
|
-
listeners_1.logger.warn({
|
|
140
|
-
content: (_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : String(err),
|
|
141
|
-
message: 'Failed to request aggregated cluster metrics; falling back to local registry',
|
|
142
|
-
pid: process.pid,
|
|
143
|
-
});
|
|
144
|
-
prom_client_1.register.metrics().then(resolve).catch(() => resolve(''));
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
};
|
|
148
|
-
exports.requestAggregatedMetrics = requestAggregatedMetrics;
|
|
149
|
-
/**
|
|
150
|
-
* Resolves the pending `requestAggregatedMetrics` promise correlated by id when
|
|
151
|
-
* the master replies. On error it falls back to the local registry.
|
|
152
|
-
*/
|
|
153
|
-
const handleMasterMetricsResponse = (message) => {
|
|
154
|
-
const pending = pendingRequests.get(message.id);
|
|
155
|
-
if (!pending) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
pendingRequests.delete(message.id);
|
|
159
|
-
clearTimeout(pending.timer);
|
|
160
|
-
if (message.error != null || message.body == null) {
|
|
161
|
-
listeners_1.logger.warn({
|
|
162
|
-
content: message.error,
|
|
163
|
-
message: 'Master failed to aggregate cluster metrics; falling back to local registry',
|
|
164
|
-
pid: process.pid,
|
|
165
|
-
});
|
|
166
|
-
prom_client_1.register.metrics().then(pending.resolve).catch(() => pending.resolve(''));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
pending.resolve(message.body);
|
|
170
|
-
};
|
|
171
|
-
exports.handleMasterMetricsResponse = handleMasterMetricsResponse;
|
|
172
|
-
/** Test-only helper to reset module state between test cases. */
|
|
173
|
-
// tslint:disable-next-line:variable-name
|
|
174
|
-
const __resetForTests = () => {
|
|
175
|
-
masterAggregatorRegistry = undefined;
|
|
176
|
-
workerAggregatorRegistry = undefined;
|
|
177
|
-
pendingRequests.forEach((p) => clearTimeout(p.timer));
|
|
178
|
-
pendingRequests.clear();
|
|
179
|
-
requestCounter = 0;
|
|
180
|
-
};
|
|
181
|
-
exports.__resetForTests = __resetForTests;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Label value reported for requests that never reached a named handler: unmatched
|
|
3
|
-
* paths (Koa answers its default 404), requests rejected before the route pipeline
|
|
4
|
-
* (replica-level rate limiter, errors in compress/recorder), and handlers that
|
|
5
|
-
* don't set `ctx.requestHandlerName`.
|
|
6
|
-
*
|
|
7
|
-
* The value must be a non-empty string. `ctx.requestHandlerName` is `undefined`
|
|
8
|
-
* for those requests, and Node's cluster IPC serializes each worker's registry as
|
|
9
|
-
* JSON, which drops properties whose value is `undefined`. The sample then reaches
|
|
10
|
-
* the aggregated `/metrics` with the `handler` label missing altogether, which
|
|
11
|
-
* Prometheus reads as `handler=""` — a distinct, unnamed series that shows up as
|
|
12
|
-
* an extra "Value" line in dashboards.
|
|
13
|
-
*
|
|
14
|
-
* `'undefined'` is deliberate rather than a nicer word: prom-client's local
|
|
15
|
-
* exposition already rendered `handler: undefined` as `handler="undefined"` before
|
|
16
|
-
* the cluster aggregation was introduced, so keeping that value makes the
|
|
17
|
-
* aggregated output match the historical series identity and keeps existing
|
|
18
|
-
* dashboards, filters and alerts (e.g. `handler!~"builtin:.*|undefined"`) working.
|
|
19
|
-
*/
|
|
20
|
-
export declare const UNNAMED_REQUEST_HANDLER = "undefined";
|
|
21
|
-
/**
|
|
22
|
-
* Resolves the `handler` label value for a request, falling back to
|
|
23
|
-
* {@link UNNAMED_REQUEST_HANDLER} when the pipeline never named the handler.
|
|
24
|
-
* Empty strings fall back too, so the label is never emitted empty.
|
|
25
|
-
*/
|
|
26
|
-
export declare const requestHandlerLabel: (requestHandlerName?: string) => string;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.requestHandlerLabel = exports.UNNAMED_REQUEST_HANDLER = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Label value reported for requests that never reached a named handler: unmatched
|
|
6
|
-
* paths (Koa answers its default 404), requests rejected before the route pipeline
|
|
7
|
-
* (replica-level rate limiter, errors in compress/recorder), and handlers that
|
|
8
|
-
* don't set `ctx.requestHandlerName`.
|
|
9
|
-
*
|
|
10
|
-
* The value must be a non-empty string. `ctx.requestHandlerName` is `undefined`
|
|
11
|
-
* for those requests, and Node's cluster IPC serializes each worker's registry as
|
|
12
|
-
* JSON, which drops properties whose value is `undefined`. The sample then reaches
|
|
13
|
-
* the aggregated `/metrics` with the `handler` label missing altogether, which
|
|
14
|
-
* Prometheus reads as `handler=""` — a distinct, unnamed series that shows up as
|
|
15
|
-
* an extra "Value" line in dashboards.
|
|
16
|
-
*
|
|
17
|
-
* `'undefined'` is deliberate rather than a nicer word: prom-client's local
|
|
18
|
-
* exposition already rendered `handler: undefined` as `handler="undefined"` before
|
|
19
|
-
* the cluster aggregation was introduced, so keeping that value makes the
|
|
20
|
-
* aggregated output match the historical series identity and keeps existing
|
|
21
|
-
* dashboards, filters and alerts (e.g. `handler!~"builtin:.*|undefined"`) working.
|
|
22
|
-
*/
|
|
23
|
-
exports.UNNAMED_REQUEST_HANDLER = 'undefined';
|
|
24
|
-
/**
|
|
25
|
-
* Resolves the `handler` label value for a request, falling back to
|
|
26
|
-
* {@link UNNAMED_REQUEST_HANDLER} when the pipeline never named the handler.
|
|
27
|
-
* Empty strings fall back too, so the label is never emitted empty.
|
|
28
|
-
*/
|
|
29
|
-
const requestHandlerLabel = (requestHandlerName) => requestHandlerName || exports.UNNAMED_REQUEST_HANDLER;
|
|
30
|
-
exports.requestHandlerLabel = requestHandlerLabel;
|