@envelop/prometheus 9.1.0 → 9.2.0-rc-20240118143850-de12c7d2
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/index.js +88 -42
- package/cjs/utils.js +5 -5
- package/esm/index.js +88 -42
- package/esm/utils.js +3 -3
- package/package.json +1 -1
- package/typings/config.d.cts +8 -0
- package/typings/config.d.ts +8 -0
- package/typings/index.d.cts +3 -7
- package/typings/index.d.ts +3 -7
- package/typings/utils.d.cts +1 -1
- package/typings/utils.d.ts +1 -1
package/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.usePrometheus = exports.createSummary = exports.createHistogram = exports.createCounter = void 0;
|
|
3
|
+
exports.usePrometheus = exports.execStartTimeMap = exports.fillLabelsFnParamsMap = exports.createSummary = exports.createHistogram = exports.createCounter = void 0;
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
|
|
5
5
|
const graphql_1 = require("graphql");
|
|
6
6
|
const prom_client_1 = require("prom-client");
|
|
@@ -10,14 +10,24 @@ const utils_js_1 = require("./utils.js");
|
|
|
10
10
|
Object.defineProperty(exports, "createCounter", { enumerable: true, get: function () { return utils_js_1.createCounter; } });
|
|
11
11
|
Object.defineProperty(exports, "createHistogram", { enumerable: true, get: function () { return utils_js_1.createHistogram; } });
|
|
12
12
|
Object.defineProperty(exports, "createSummary", { enumerable: true, get: function () { return utils_js_1.createSummary; } });
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
exports.fillLabelsFnParamsMap = new WeakMap();
|
|
14
|
+
exports.execStartTimeMap = new WeakMap();
|
|
15
15
|
const usePrometheus = (config = {}) => {
|
|
16
16
|
let typeInfo = null;
|
|
17
17
|
const parseHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'parse', 'graphql_envelop_phase_parse', 'Time spent on running GraphQL "parse" function');
|
|
18
18
|
const validateHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'validate', 'graphql_envelop_phase_validate', 'Time spent on running GraphQL "validate" function');
|
|
19
19
|
const contextBuildingHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'contextBuilding', 'graphql_envelop_phase_context', 'Time spent on building the GraphQL context');
|
|
20
20
|
const executeHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'execute', 'graphql_envelop_phase_execute', 'Time spent on running the GraphQL "execute" function');
|
|
21
|
+
function labelExists(label) {
|
|
22
|
+
const labelFlag = config.labels?.[label];
|
|
23
|
+
if (labelFlag == null) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return labelFlag;
|
|
27
|
+
}
|
|
28
|
+
function filterFillParamsFnParams(params) {
|
|
29
|
+
return Object.fromEntries(Object.entries(params).filter(([key]) => labelExists(key)));
|
|
30
|
+
}
|
|
21
31
|
const resolversHistogram = typeof config.resolvers === 'object'
|
|
22
32
|
? config.resolvers
|
|
23
33
|
: config.resolvers === true
|
|
@@ -31,10 +41,10 @@ const usePrometheus = (config = {}) => {
|
|
|
31
41
|
'fieldName',
|
|
32
42
|
'typeName',
|
|
33
43
|
'returnType',
|
|
34
|
-
],
|
|
44
|
+
].filter(labelExists),
|
|
35
45
|
registers: [config.registry || prom_client_1.register],
|
|
36
46
|
}),
|
|
37
|
-
fillLabelsFn: params => ({
|
|
47
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
38
48
|
operationName: params.operationName,
|
|
39
49
|
operationType: params.operationType,
|
|
40
50
|
fieldName: params.info?.fieldName,
|
|
@@ -50,10 +60,10 @@ const usePrometheus = (config = {}) => {
|
|
|
50
60
|
histogram: new prom_client_1.Histogram({
|
|
51
61
|
name: 'graphql_envelop_request_duration',
|
|
52
62
|
help: 'Time spent on running the GraphQL operation from parse to execute',
|
|
53
|
-
labelNames: ['operationType', 'operationName'],
|
|
63
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
54
64
|
registers: [config.registry || prom_client_1.register],
|
|
55
65
|
}),
|
|
56
|
-
fillLabelsFn: params => ({
|
|
66
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
57
67
|
operationName: params.operationName,
|
|
58
68
|
operationType: params.operationType,
|
|
59
69
|
}),
|
|
@@ -66,10 +76,10 @@ const usePrometheus = (config = {}) => {
|
|
|
66
76
|
summary: new prom_client_1.Summary({
|
|
67
77
|
name: 'graphql_envelop_request_time_summary',
|
|
68
78
|
help: 'Summary to measure the time to complete GraphQL operations',
|
|
69
|
-
labelNames: ['operationType', 'operationName'],
|
|
79
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
70
80
|
registers: [config.registry || prom_client_1.register],
|
|
71
81
|
}),
|
|
72
|
-
fillLabelsFn: params => ({
|
|
82
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
73
83
|
operationName: params.operationName,
|
|
74
84
|
operationType: params.operationType,
|
|
75
85
|
}),
|
|
@@ -82,10 +92,10 @@ const usePrometheus = (config = {}) => {
|
|
|
82
92
|
counter: new prom_client_1.Counter({
|
|
83
93
|
name: 'graphql_envelop_error_result',
|
|
84
94
|
help: 'Counts the amount of errors reported from all phases',
|
|
85
|
-
labelNames: ['operationType', 'operationName', 'path', 'phase'],
|
|
95
|
+
labelNames: ['operationType', 'operationName', 'path', 'phase'].filter(labelExists),
|
|
86
96
|
registers: [config.registry || prom_client_1.register],
|
|
87
97
|
}),
|
|
88
|
-
fillLabelsFn: params => ({
|
|
98
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
89
99
|
operationName: params.operationName,
|
|
90
100
|
operationType: params.operationType,
|
|
91
101
|
path: params.error?.path?.join('.'),
|
|
@@ -100,10 +110,10 @@ const usePrometheus = (config = {}) => {
|
|
|
100
110
|
counter: new prom_client_1.Counter({
|
|
101
111
|
name: 'graphql_envelop_request',
|
|
102
112
|
help: 'Counts the amount of GraphQL requests executed through Envelop',
|
|
103
|
-
labelNames: ['operationType', 'operationName'],
|
|
113
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
104
114
|
registers: [config.registry || prom_client_1.register],
|
|
105
115
|
}),
|
|
106
|
-
fillLabelsFn: params => ({
|
|
116
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
107
117
|
operationName: params.operationName,
|
|
108
118
|
operationType: params.operationType,
|
|
109
119
|
}),
|
|
@@ -116,10 +126,10 @@ const usePrometheus = (config = {}) => {
|
|
|
116
126
|
counter: new prom_client_1.Counter({
|
|
117
127
|
name: 'graphql_envelop_deprecated_field',
|
|
118
128
|
help: 'Counts the amount of deprecated fields used in selection sets',
|
|
119
|
-
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'],
|
|
129
|
+
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'].filter(labelExists),
|
|
120
130
|
registers: [config.registry || prom_client_1.register],
|
|
121
131
|
}),
|
|
122
|
-
fillLabelsFn: params => ({
|
|
132
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
123
133
|
operationName: params.operationName,
|
|
124
134
|
operationType: params.operationType,
|
|
125
135
|
fieldName: params.deprecationInfo?.fieldName,
|
|
@@ -127,26 +137,39 @@ const usePrometheus = (config = {}) => {
|
|
|
127
137
|
}),
|
|
128
138
|
})
|
|
129
139
|
: undefined;
|
|
130
|
-
const
|
|
140
|
+
const schemaChangeCounter = typeof config.schemaChangeCount === 'object'
|
|
141
|
+
? config.schemaChangeCount
|
|
142
|
+
: config.schemaChangeCount === true
|
|
143
|
+
? (0, utils_js_1.createCounter)({
|
|
144
|
+
counter: new prom_client_1.Counter({
|
|
145
|
+
name: 'graphql_envelop_schema_change',
|
|
146
|
+
help: 'Counts the amount of schema changes',
|
|
147
|
+
registers: [config.registry || prom_client_1.register],
|
|
148
|
+
}),
|
|
149
|
+
fillLabelsFn: () => ({}),
|
|
150
|
+
})
|
|
151
|
+
: undefined;
|
|
152
|
+
const onParse = ({ context, params }) => {
|
|
131
153
|
if (config.skipIntrospection && (0, core_1.isIntrospectionOperationString)(params.source)) {
|
|
132
154
|
return;
|
|
133
155
|
}
|
|
134
156
|
const startTime = Date.now();
|
|
135
157
|
return params => {
|
|
136
158
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
159
|
+
let fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(params.result);
|
|
160
|
+
if (!fillLabelsFnParams) {
|
|
161
|
+
fillLabelsFnParams = (0, utils_js_1.createFillLabelFnParams)(params.result, filterFillParamsFnParams);
|
|
162
|
+
exports.fillLabelsFnParamsMap.set(context, fillLabelsFnParams);
|
|
163
|
+
}
|
|
164
|
+
if (fillLabelsFnParams) {
|
|
165
|
+
parseHistogram?.histogram.observe(parseHistogram.fillLabelsFn(fillLabelsFnParams, context), totalTime);
|
|
143
166
|
if (deprecationCounter && typeInfo) {
|
|
144
|
-
const deprecatedFields = (0, utils_js_1.extractDeprecatedFields)(
|
|
167
|
+
const deprecatedFields = (0, utils_js_1.extractDeprecatedFields)(fillLabelsFnParams.document, typeInfo);
|
|
145
168
|
if (deprecatedFields.length > 0) {
|
|
146
169
|
for (const depField of deprecatedFields) {
|
|
147
170
|
deprecationCounter.counter
|
|
148
171
|
.labels(deprecationCounter.fillLabelsFn({
|
|
149
|
-
...
|
|
172
|
+
...fillLabelsFnParams,
|
|
150
173
|
deprecationInfo: depField,
|
|
151
174
|
}, context))
|
|
152
175
|
.inc();
|
|
@@ -166,13 +189,14 @@ const usePrometheus = (config = {}) => {
|
|
|
166
189
|
};
|
|
167
190
|
const onValidate = validateHistogram
|
|
168
191
|
? ({ context }) => {
|
|
169
|
-
|
|
192
|
+
const fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(context);
|
|
193
|
+
if (!fillLabelsFnParams) {
|
|
170
194
|
return undefined;
|
|
171
195
|
}
|
|
172
196
|
const startTime = Date.now();
|
|
173
197
|
return ({ valid }) => {
|
|
174
198
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
175
|
-
const labels = validateHistogram.fillLabelsFn(
|
|
199
|
+
const labels = validateHistogram.fillLabelsFn(fillLabelsFnParams, context);
|
|
176
200
|
validateHistogram.histogram.observe(labels, totalTime);
|
|
177
201
|
if (!valid) {
|
|
178
202
|
errorsCounter?.counter
|
|
@@ -187,33 +211,36 @@ const usePrometheus = (config = {}) => {
|
|
|
187
211
|
: undefined;
|
|
188
212
|
const onContextBuilding = contextBuildingHistogram
|
|
189
213
|
? ({ context }) => {
|
|
190
|
-
|
|
214
|
+
const fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(context);
|
|
215
|
+
if (!fillLabelsFnParams) {
|
|
191
216
|
return undefined;
|
|
192
217
|
}
|
|
193
218
|
const startTime = Date.now();
|
|
194
219
|
return () => {
|
|
195
220
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
196
|
-
contextBuildingHistogram.histogram.observe(contextBuildingHistogram.fillLabelsFn(
|
|
221
|
+
contextBuildingHistogram.histogram.observe(contextBuildingHistogram.fillLabelsFn(fillLabelsFnParams, context), totalTime);
|
|
197
222
|
};
|
|
198
223
|
}
|
|
199
224
|
: undefined;
|
|
200
225
|
const onExecute = executeHistogram
|
|
201
226
|
? ({ args }) => {
|
|
202
|
-
|
|
227
|
+
const fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(args.contextValue);
|
|
228
|
+
if (!fillLabelsFnParams) {
|
|
203
229
|
return undefined;
|
|
204
230
|
}
|
|
205
231
|
const startTime = Date.now();
|
|
206
232
|
reqCounter?.counter
|
|
207
|
-
.labels(reqCounter.fillLabelsFn(
|
|
233
|
+
.labels(reqCounter.fillLabelsFn(fillLabelsFnParams, args.contextValue))
|
|
208
234
|
.inc();
|
|
209
235
|
const result = {
|
|
210
236
|
onExecuteDone: ({ result }) => {
|
|
211
237
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
212
|
-
executeHistogram.histogram.observe(executeHistogram.fillLabelsFn(
|
|
213
|
-
requestTotalHistogram?.histogram.observe(requestTotalHistogram.fillLabelsFn(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
238
|
+
executeHistogram.histogram.observe(executeHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), totalTime);
|
|
239
|
+
requestTotalHistogram?.histogram.observe(requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), totalTime);
|
|
240
|
+
const execStartTime = exports.execStartTimeMap.get(args.contextValue);
|
|
241
|
+
if (requestSummary && execStartTime) {
|
|
242
|
+
const summaryTime = (Date.now() - execStartTime) / 1000;
|
|
243
|
+
requestSummary.summary.observe(requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue), summaryTime);
|
|
217
244
|
}
|
|
218
245
|
if (errorsCounter &&
|
|
219
246
|
!(0, core_1.isAsyncIterable)(result) &&
|
|
@@ -222,7 +249,7 @@ const usePrometheus = (config = {}) => {
|
|
|
222
249
|
for (const error of result.errors) {
|
|
223
250
|
errorsCounter.counter
|
|
224
251
|
.labels(errorsCounter.fillLabelsFn({
|
|
225
|
-
...
|
|
252
|
+
...fillLabelsFnParams,
|
|
226
253
|
errorPhase: 'execute',
|
|
227
254
|
error,
|
|
228
255
|
}, args.contextValue))
|
|
@@ -234,13 +261,14 @@ const usePrometheus = (config = {}) => {
|
|
|
234
261
|
return result;
|
|
235
262
|
}
|
|
236
263
|
: undefined;
|
|
264
|
+
const countedSchemas = new WeakSet();
|
|
237
265
|
return {
|
|
238
|
-
onEnveloped({
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
266
|
+
onEnveloped({ context }) {
|
|
267
|
+
if (!exports.execStartTimeMap.has(context)) {
|
|
268
|
+
exports.execStartTimeMap.set(context, Date.now());
|
|
269
|
+
}
|
|
242
270
|
},
|
|
243
|
-
onPluginInit({ addPlugin }) {
|
|
271
|
+
onPluginInit({ addPlugin, registerContextErrorHandler }) {
|
|
244
272
|
if (resolversHistogram) {
|
|
245
273
|
addPlugin((0, on_resolve_1.useOnResolve)(({ info, context }) => {
|
|
246
274
|
const shouldTrace = (0, utils_js_1.shouldTraceFieldResolver)(info, config.resolversWhitelist);
|
|
@@ -250,17 +278,35 @@ const usePrometheus = (config = {}) => {
|
|
|
250
278
|
const startTime = Date.now();
|
|
251
279
|
return () => {
|
|
252
280
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
281
|
+
const fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(context);
|
|
253
282
|
const paramsCtx = {
|
|
254
|
-
...
|
|
283
|
+
...fillLabelsFnParams,
|
|
255
284
|
info,
|
|
256
285
|
};
|
|
257
286
|
resolversHistogram.histogram.observe(resolversHistogram.fillLabelsFn(paramsCtx, context), totalTime);
|
|
258
287
|
};
|
|
259
288
|
}));
|
|
260
289
|
}
|
|
290
|
+
registerContextErrorHandler(({ context }) => {
|
|
291
|
+
const fillLabelsFnParams = exports.fillLabelsFnParamsMap.get(context);
|
|
292
|
+
let extraLabels;
|
|
293
|
+
if (fillLabelsFnParams) {
|
|
294
|
+
extraLabels = contextBuildingHistogram?.fillLabelsFn(fillLabelsFnParams, context);
|
|
295
|
+
}
|
|
296
|
+
errorsCounter?.counter
|
|
297
|
+
.labels({
|
|
298
|
+
...extraLabels,
|
|
299
|
+
phase: 'context',
|
|
300
|
+
})
|
|
301
|
+
.inc();
|
|
302
|
+
});
|
|
261
303
|
},
|
|
262
304
|
onSchemaChange({ schema }) {
|
|
263
305
|
typeInfo = new graphql_1.TypeInfo(schema);
|
|
306
|
+
if (schemaChangeCounter && !countedSchemas.has(schema)) {
|
|
307
|
+
schemaChangeCounter.counter.inc();
|
|
308
|
+
countedSchemas.add(schema);
|
|
309
|
+
}
|
|
264
310
|
},
|
|
265
311
|
onParse,
|
|
266
312
|
onValidate,
|
package/cjs/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractDeprecatedFields = exports.getHistogramFromConfig = exports.createCounter = exports.createSummary = exports.createHistogram = exports.
|
|
3
|
+
exports.extractDeprecatedFields = exports.getHistogramFromConfig = exports.createCounter = exports.createSummary = exports.createHistogram = exports.createFillLabelFnParams = exports.shouldTraceFieldResolver = void 0;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
const prom_client_1 = require("prom-client");
|
|
6
6
|
function shouldTraceFieldResolver(info, whitelist) {
|
|
@@ -16,7 +16,7 @@ exports.shouldTraceFieldResolver = shouldTraceFieldResolver;
|
|
|
16
16
|
function getOperation(document) {
|
|
17
17
|
return document.definitions[0];
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function createFillLabelFnParams(parseResult, filterParams) {
|
|
20
20
|
if (parseResult === null) {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
@@ -24,13 +24,13 @@ function createInternalContext(parseResult) {
|
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
const operation = getOperation(parseResult);
|
|
27
|
-
return {
|
|
27
|
+
return filterParams({
|
|
28
28
|
document: parseResult,
|
|
29
29
|
operationName: operation.name?.value || 'Anonymous',
|
|
30
30
|
operationType: operation.operation,
|
|
31
|
-
};
|
|
31
|
+
});
|
|
32
32
|
}
|
|
33
|
-
exports.
|
|
33
|
+
exports.createFillLabelFnParams = createFillLabelFnParams;
|
|
34
34
|
function createHistogram(options) {
|
|
35
35
|
return options;
|
|
36
36
|
}
|
package/esm/index.js
CHANGED
|
@@ -3,16 +3,26 @@ import { TypeInfo } from 'graphql';
|
|
|
3
3
|
import { Counter, register as defaultRegistry, Histogram, Summary } from 'prom-client';
|
|
4
4
|
import { isAsyncIterable, isIntrospectionOperationString, } from '@envelop/core';
|
|
5
5
|
import { useOnResolve } from '@envelop/on-resolve';
|
|
6
|
-
import { createCounter,
|
|
6
|
+
import { createCounter, createFillLabelFnParams, createHistogram, createSummary, extractDeprecatedFields, getHistogramFromConfig, shouldTraceFieldResolver, } from './utils.js';
|
|
7
7
|
export { createCounter, createHistogram, createSummary, };
|
|
8
|
-
const
|
|
9
|
-
const
|
|
8
|
+
export const fillLabelsFnParamsMap = new WeakMap();
|
|
9
|
+
export const execStartTimeMap = new WeakMap();
|
|
10
10
|
export const usePrometheus = (config = {}) => {
|
|
11
11
|
let typeInfo = null;
|
|
12
12
|
const parseHistogram = getHistogramFromConfig(config, 'parse', 'graphql_envelop_phase_parse', 'Time spent on running GraphQL "parse" function');
|
|
13
13
|
const validateHistogram = getHistogramFromConfig(config, 'validate', 'graphql_envelop_phase_validate', 'Time spent on running GraphQL "validate" function');
|
|
14
14
|
const contextBuildingHistogram = getHistogramFromConfig(config, 'contextBuilding', 'graphql_envelop_phase_context', 'Time spent on building the GraphQL context');
|
|
15
15
|
const executeHistogram = getHistogramFromConfig(config, 'execute', 'graphql_envelop_phase_execute', 'Time spent on running the GraphQL "execute" function');
|
|
16
|
+
function labelExists(label) {
|
|
17
|
+
const labelFlag = config.labels?.[label];
|
|
18
|
+
if (labelFlag == null) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return labelFlag;
|
|
22
|
+
}
|
|
23
|
+
function filterFillParamsFnParams(params) {
|
|
24
|
+
return Object.fromEntries(Object.entries(params).filter(([key]) => labelExists(key)));
|
|
25
|
+
}
|
|
16
26
|
const resolversHistogram = typeof config.resolvers === 'object'
|
|
17
27
|
? config.resolvers
|
|
18
28
|
: config.resolvers === true
|
|
@@ -26,10 +36,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
26
36
|
'fieldName',
|
|
27
37
|
'typeName',
|
|
28
38
|
'returnType',
|
|
29
|
-
],
|
|
39
|
+
].filter(labelExists),
|
|
30
40
|
registers: [config.registry || defaultRegistry],
|
|
31
41
|
}),
|
|
32
|
-
fillLabelsFn: params => ({
|
|
42
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
33
43
|
operationName: params.operationName,
|
|
34
44
|
operationType: params.operationType,
|
|
35
45
|
fieldName: params.info?.fieldName,
|
|
@@ -45,10 +55,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
45
55
|
histogram: new Histogram({
|
|
46
56
|
name: 'graphql_envelop_request_duration',
|
|
47
57
|
help: 'Time spent on running the GraphQL operation from parse to execute',
|
|
48
|
-
labelNames: ['operationType', 'operationName'],
|
|
58
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
49
59
|
registers: [config.registry || defaultRegistry],
|
|
50
60
|
}),
|
|
51
|
-
fillLabelsFn: params => ({
|
|
61
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
52
62
|
operationName: params.operationName,
|
|
53
63
|
operationType: params.operationType,
|
|
54
64
|
}),
|
|
@@ -61,10 +71,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
61
71
|
summary: new Summary({
|
|
62
72
|
name: 'graphql_envelop_request_time_summary',
|
|
63
73
|
help: 'Summary to measure the time to complete GraphQL operations',
|
|
64
|
-
labelNames: ['operationType', 'operationName'],
|
|
74
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
65
75
|
registers: [config.registry || defaultRegistry],
|
|
66
76
|
}),
|
|
67
|
-
fillLabelsFn: params => ({
|
|
77
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
68
78
|
operationName: params.operationName,
|
|
69
79
|
operationType: params.operationType,
|
|
70
80
|
}),
|
|
@@ -77,10 +87,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
77
87
|
counter: new Counter({
|
|
78
88
|
name: 'graphql_envelop_error_result',
|
|
79
89
|
help: 'Counts the amount of errors reported from all phases',
|
|
80
|
-
labelNames: ['operationType', 'operationName', 'path', 'phase'],
|
|
90
|
+
labelNames: ['operationType', 'operationName', 'path', 'phase'].filter(labelExists),
|
|
81
91
|
registers: [config.registry || defaultRegistry],
|
|
82
92
|
}),
|
|
83
|
-
fillLabelsFn: params => ({
|
|
93
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
84
94
|
operationName: params.operationName,
|
|
85
95
|
operationType: params.operationType,
|
|
86
96
|
path: params.error?.path?.join('.'),
|
|
@@ -95,10 +105,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
95
105
|
counter: new Counter({
|
|
96
106
|
name: 'graphql_envelop_request',
|
|
97
107
|
help: 'Counts the amount of GraphQL requests executed through Envelop',
|
|
98
|
-
labelNames: ['operationType', 'operationName'],
|
|
108
|
+
labelNames: ['operationType', 'operationName'].filter(labelExists),
|
|
99
109
|
registers: [config.registry || defaultRegistry],
|
|
100
110
|
}),
|
|
101
|
-
fillLabelsFn: params => ({
|
|
111
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
102
112
|
operationName: params.operationName,
|
|
103
113
|
operationType: params.operationType,
|
|
104
114
|
}),
|
|
@@ -111,10 +121,10 @@ export const usePrometheus = (config = {}) => {
|
|
|
111
121
|
counter: new Counter({
|
|
112
122
|
name: 'graphql_envelop_deprecated_field',
|
|
113
123
|
help: 'Counts the amount of deprecated fields used in selection sets',
|
|
114
|
-
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'],
|
|
124
|
+
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'].filter(labelExists),
|
|
115
125
|
registers: [config.registry || defaultRegistry],
|
|
116
126
|
}),
|
|
117
|
-
fillLabelsFn: params => ({
|
|
127
|
+
fillLabelsFn: params => filterFillParamsFnParams({
|
|
118
128
|
operationName: params.operationName,
|
|
119
129
|
operationType: params.operationType,
|
|
120
130
|
fieldName: params.deprecationInfo?.fieldName,
|
|
@@ -122,26 +132,39 @@ export const usePrometheus = (config = {}) => {
|
|
|
122
132
|
}),
|
|
123
133
|
})
|
|
124
134
|
: undefined;
|
|
125
|
-
const
|
|
135
|
+
const schemaChangeCounter = typeof config.schemaChangeCount === 'object'
|
|
136
|
+
? config.schemaChangeCount
|
|
137
|
+
: config.schemaChangeCount === true
|
|
138
|
+
? createCounter({
|
|
139
|
+
counter: new Counter({
|
|
140
|
+
name: 'graphql_envelop_schema_change',
|
|
141
|
+
help: 'Counts the amount of schema changes',
|
|
142
|
+
registers: [config.registry || defaultRegistry],
|
|
143
|
+
}),
|
|
144
|
+
fillLabelsFn: () => ({}),
|
|
145
|
+
})
|
|
146
|
+
: undefined;
|
|
147
|
+
const onParse = ({ context, params }) => {
|
|
126
148
|
if (config.skipIntrospection && isIntrospectionOperationString(params.source)) {
|
|
127
149
|
return;
|
|
128
150
|
}
|
|
129
151
|
const startTime = Date.now();
|
|
130
152
|
return params => {
|
|
131
153
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
let fillLabelsFnParams = fillLabelsFnParamsMap.get(params.result);
|
|
155
|
+
if (!fillLabelsFnParams) {
|
|
156
|
+
fillLabelsFnParams = createFillLabelFnParams(params.result, filterFillParamsFnParams);
|
|
157
|
+
fillLabelsFnParamsMap.set(context, fillLabelsFnParams);
|
|
158
|
+
}
|
|
159
|
+
if (fillLabelsFnParams) {
|
|
160
|
+
parseHistogram?.histogram.observe(parseHistogram.fillLabelsFn(fillLabelsFnParams, context), totalTime);
|
|
138
161
|
if (deprecationCounter && typeInfo) {
|
|
139
|
-
const deprecatedFields = extractDeprecatedFields(
|
|
162
|
+
const deprecatedFields = extractDeprecatedFields(fillLabelsFnParams.document, typeInfo);
|
|
140
163
|
if (deprecatedFields.length > 0) {
|
|
141
164
|
for (const depField of deprecatedFields) {
|
|
142
165
|
deprecationCounter.counter
|
|
143
166
|
.labels(deprecationCounter.fillLabelsFn({
|
|
144
|
-
...
|
|
167
|
+
...fillLabelsFnParams,
|
|
145
168
|
deprecationInfo: depField,
|
|
146
169
|
}, context))
|
|
147
170
|
.inc();
|
|
@@ -161,13 +184,14 @@ export const usePrometheus = (config = {}) => {
|
|
|
161
184
|
};
|
|
162
185
|
const onValidate = validateHistogram
|
|
163
186
|
? ({ context }) => {
|
|
164
|
-
|
|
187
|
+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(context);
|
|
188
|
+
if (!fillLabelsFnParams) {
|
|
165
189
|
return undefined;
|
|
166
190
|
}
|
|
167
191
|
const startTime = Date.now();
|
|
168
192
|
return ({ valid }) => {
|
|
169
193
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
170
|
-
const labels = validateHistogram.fillLabelsFn(
|
|
194
|
+
const labels = validateHistogram.fillLabelsFn(fillLabelsFnParams, context);
|
|
171
195
|
validateHistogram.histogram.observe(labels, totalTime);
|
|
172
196
|
if (!valid) {
|
|
173
197
|
errorsCounter?.counter
|
|
@@ -182,33 +206,36 @@ export const usePrometheus = (config = {}) => {
|
|
|
182
206
|
: undefined;
|
|
183
207
|
const onContextBuilding = contextBuildingHistogram
|
|
184
208
|
? ({ context }) => {
|
|
185
|
-
|
|
209
|
+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(context);
|
|
210
|
+
if (!fillLabelsFnParams) {
|
|
186
211
|
return undefined;
|
|
187
212
|
}
|
|
188
213
|
const startTime = Date.now();
|
|
189
214
|
return () => {
|
|
190
215
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
191
|
-
contextBuildingHistogram.histogram.observe(contextBuildingHistogram.fillLabelsFn(
|
|
216
|
+
contextBuildingHistogram.histogram.observe(contextBuildingHistogram.fillLabelsFn(fillLabelsFnParams, context), totalTime);
|
|
192
217
|
};
|
|
193
218
|
}
|
|
194
219
|
: undefined;
|
|
195
220
|
const onExecute = executeHistogram
|
|
196
221
|
? ({ args }) => {
|
|
197
|
-
|
|
222
|
+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(args.contextValue);
|
|
223
|
+
if (!fillLabelsFnParams) {
|
|
198
224
|
return undefined;
|
|
199
225
|
}
|
|
200
226
|
const startTime = Date.now();
|
|
201
227
|
reqCounter?.counter
|
|
202
|
-
.labels(reqCounter.fillLabelsFn(
|
|
228
|
+
.labels(reqCounter.fillLabelsFn(fillLabelsFnParams, args.contextValue))
|
|
203
229
|
.inc();
|
|
204
230
|
const result = {
|
|
205
231
|
onExecuteDone: ({ result }) => {
|
|
206
232
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
207
|
-
executeHistogram.histogram.observe(executeHistogram.fillLabelsFn(
|
|
208
|
-
requestTotalHistogram?.histogram.observe(requestTotalHistogram.fillLabelsFn(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
233
|
+
executeHistogram.histogram.observe(executeHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), totalTime);
|
|
234
|
+
requestTotalHistogram?.histogram.observe(requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), totalTime);
|
|
235
|
+
const execStartTime = execStartTimeMap.get(args.contextValue);
|
|
236
|
+
if (requestSummary && execStartTime) {
|
|
237
|
+
const summaryTime = (Date.now() - execStartTime) / 1000;
|
|
238
|
+
requestSummary.summary.observe(requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue), summaryTime);
|
|
212
239
|
}
|
|
213
240
|
if (errorsCounter &&
|
|
214
241
|
!isAsyncIterable(result) &&
|
|
@@ -217,7 +244,7 @@ export const usePrometheus = (config = {}) => {
|
|
|
217
244
|
for (const error of result.errors) {
|
|
218
245
|
errorsCounter.counter
|
|
219
246
|
.labels(errorsCounter.fillLabelsFn({
|
|
220
|
-
...
|
|
247
|
+
...fillLabelsFnParams,
|
|
221
248
|
errorPhase: 'execute',
|
|
222
249
|
error,
|
|
223
250
|
}, args.contextValue))
|
|
@@ -229,13 +256,14 @@ export const usePrometheus = (config = {}) => {
|
|
|
229
256
|
return result;
|
|
230
257
|
}
|
|
231
258
|
: undefined;
|
|
259
|
+
const countedSchemas = new WeakSet();
|
|
232
260
|
return {
|
|
233
|
-
onEnveloped({
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
261
|
+
onEnveloped({ context }) {
|
|
262
|
+
if (!execStartTimeMap.has(context)) {
|
|
263
|
+
execStartTimeMap.set(context, Date.now());
|
|
264
|
+
}
|
|
237
265
|
},
|
|
238
|
-
onPluginInit({ addPlugin }) {
|
|
266
|
+
onPluginInit({ addPlugin, registerContextErrorHandler }) {
|
|
239
267
|
if (resolversHistogram) {
|
|
240
268
|
addPlugin(useOnResolve(({ info, context }) => {
|
|
241
269
|
const shouldTrace = shouldTraceFieldResolver(info, config.resolversWhitelist);
|
|
@@ -245,17 +273,35 @@ export const usePrometheus = (config = {}) => {
|
|
|
245
273
|
const startTime = Date.now();
|
|
246
274
|
return () => {
|
|
247
275
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
276
|
+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(context);
|
|
248
277
|
const paramsCtx = {
|
|
249
|
-
...
|
|
278
|
+
...fillLabelsFnParams,
|
|
250
279
|
info,
|
|
251
280
|
};
|
|
252
281
|
resolversHistogram.histogram.observe(resolversHistogram.fillLabelsFn(paramsCtx, context), totalTime);
|
|
253
282
|
};
|
|
254
283
|
}));
|
|
255
284
|
}
|
|
285
|
+
registerContextErrorHandler(({ context }) => {
|
|
286
|
+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(context);
|
|
287
|
+
let extraLabels;
|
|
288
|
+
if (fillLabelsFnParams) {
|
|
289
|
+
extraLabels = contextBuildingHistogram?.fillLabelsFn(fillLabelsFnParams, context);
|
|
290
|
+
}
|
|
291
|
+
errorsCounter?.counter
|
|
292
|
+
.labels({
|
|
293
|
+
...extraLabels,
|
|
294
|
+
phase: 'context',
|
|
295
|
+
})
|
|
296
|
+
.inc();
|
|
297
|
+
});
|
|
256
298
|
},
|
|
257
299
|
onSchemaChange({ schema }) {
|
|
258
300
|
typeInfo = new TypeInfo(schema);
|
|
301
|
+
if (schemaChangeCounter && !countedSchemas.has(schema)) {
|
|
302
|
+
schemaChangeCounter.counter.inc();
|
|
303
|
+
countedSchemas.add(schema);
|
|
304
|
+
}
|
|
259
305
|
},
|
|
260
306
|
onParse,
|
|
261
307
|
onValidate,
|
package/esm/utils.js
CHANGED
|
@@ -12,7 +12,7 @@ export function shouldTraceFieldResolver(info, whitelist) {
|
|
|
12
12
|
function getOperation(document) {
|
|
13
13
|
return document.definitions[0];
|
|
14
14
|
}
|
|
15
|
-
export function
|
|
15
|
+
export function createFillLabelFnParams(parseResult, filterParams) {
|
|
16
16
|
if (parseResult === null) {
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
@@ -20,11 +20,11 @@ export function createInternalContext(parseResult) {
|
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
22
22
|
const operation = getOperation(parseResult);
|
|
23
|
-
return {
|
|
23
|
+
return filterParams({
|
|
24
24
|
document: parseResult,
|
|
25
25
|
operationName: operation.name?.value || 'Anonymous',
|
|
26
26
|
operationType: operation.operation,
|
|
27
|
-
};
|
|
27
|
+
});
|
|
28
28
|
}
|
|
29
29
|
export function createHistogram(options) {
|
|
30
30
|
return options;
|
package/package.json
CHANGED
package/typings/config.d.cts
CHANGED
|
@@ -14,4 +14,12 @@ export type PrometheusTracingPluginConfig = {
|
|
|
14
14
|
deprecatedFields?: boolean | ReturnType<typeof createCounter>;
|
|
15
15
|
registry?: Registry;
|
|
16
16
|
skipIntrospection?: boolean;
|
|
17
|
+
schemaChangeCount?: boolean | ReturnType<typeof createCounter>;
|
|
18
|
+
labels?: {
|
|
19
|
+
operationName?: boolean;
|
|
20
|
+
operationType?: boolean;
|
|
21
|
+
fieldName?: boolean;
|
|
22
|
+
typeName?: boolean;
|
|
23
|
+
returnType?: boolean;
|
|
24
|
+
};
|
|
17
25
|
};
|
package/typings/config.d.ts
CHANGED
|
@@ -14,4 +14,12 @@ export type PrometheusTracingPluginConfig = {
|
|
|
14
14
|
deprecatedFields?: boolean | ReturnType<typeof createCounter>;
|
|
15
15
|
registry?: Registry;
|
|
16
16
|
skipIntrospection?: boolean;
|
|
17
|
+
schemaChangeCount?: boolean | ReturnType<typeof createCounter>;
|
|
18
|
+
labels?: {
|
|
19
|
+
operationName?: boolean;
|
|
20
|
+
operationType?: boolean;
|
|
21
|
+
fieldName?: boolean;
|
|
22
|
+
typeName?: boolean;
|
|
23
|
+
returnType?: boolean;
|
|
24
|
+
};
|
|
17
25
|
};
|
package/typings/index.d.cts
CHANGED
|
@@ -2,10 +2,6 @@ import { Plugin } from '@envelop/core';
|
|
|
2
2
|
import { PrometheusTracingPluginConfig } from './config.cjs';
|
|
3
3
|
import { createCounter, createHistogram, createSummary, FillLabelsFnParams } from './utils.cjs';
|
|
4
4
|
export { PrometheusTracingPluginConfig, createCounter, createHistogram, createSummary, FillLabelsFnParams, };
|
|
5
|
-
declare const
|
|
6
|
-
declare const
|
|
7
|
-
|
|
8
|
-
[promPluginContext]: FillLabelsFnParams;
|
|
9
|
-
[promPluginExecutionStartTimeSymbol]: number;
|
|
10
|
-
};
|
|
11
|
-
export declare const usePrometheus: (config?: PrometheusTracingPluginConfig) => Plugin<PluginInternalContext>;
|
|
5
|
+
export declare const fillLabelsFnParamsMap: WeakMap<any, FillLabelsFnParams | null>;
|
|
6
|
+
export declare const execStartTimeMap: WeakMap<any, number>;
|
|
7
|
+
export declare const usePrometheus: (config?: PrometheusTracingPluginConfig) => Plugin;
|
package/typings/index.d.ts
CHANGED
|
@@ -2,10 +2,6 @@ import { Plugin } from '@envelop/core';
|
|
|
2
2
|
import { PrometheusTracingPluginConfig } from './config.js';
|
|
3
3
|
import { createCounter, createHistogram, createSummary, FillLabelsFnParams } from './utils.js';
|
|
4
4
|
export { PrometheusTracingPluginConfig, createCounter, createHistogram, createSummary, FillLabelsFnParams, };
|
|
5
|
-
declare const
|
|
6
|
-
declare const
|
|
7
|
-
|
|
8
|
-
[promPluginContext]: FillLabelsFnParams;
|
|
9
|
-
[promPluginExecutionStartTimeSymbol]: number;
|
|
10
|
-
};
|
|
11
|
-
export declare const usePrometheus: (config?: PrometheusTracingPluginConfig) => Plugin<PluginInternalContext>;
|
|
5
|
+
export declare const fillLabelsFnParamsMap: WeakMap<any, FillLabelsFnParams | null>;
|
|
6
|
+
export declare const execStartTimeMap: WeakMap<any, number>;
|
|
7
|
+
export declare const usePrometheus: (config?: PrometheusTracingPluginConfig) => Plugin;
|
package/typings/utils.d.cts
CHANGED
|
@@ -16,7 +16,7 @@ export type FillLabelsFnParams = {
|
|
|
16
16
|
deprecationInfo?: DeprecatedFieldInfo;
|
|
17
17
|
};
|
|
18
18
|
export declare function shouldTraceFieldResolver(info: GraphQLResolveInfo, whitelist: string[] | undefined): boolean;
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function createFillLabelFnParams(parseResult: AfterParseEventPayload<any>['result'], filterParams: (params: FillLabelsFnParams) => FillLabelsFnParams | null): FillLabelsFnParams | null;
|
|
20
20
|
export type FillLabelsFn<LabelNames extends string> = (params: FillLabelsFnParams, rawContext: any) => Record<LabelNames, string>;
|
|
21
21
|
export declare function createHistogram<LabelNames extends string>(options: {
|
|
22
22
|
histogram: Histogram<LabelNames>;
|
package/typings/utils.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type FillLabelsFnParams = {
|
|
|
16
16
|
deprecationInfo?: DeprecatedFieldInfo;
|
|
17
17
|
};
|
|
18
18
|
export declare function shouldTraceFieldResolver(info: GraphQLResolveInfo, whitelist: string[] | undefined): boolean;
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function createFillLabelFnParams(parseResult: AfterParseEventPayload<any>['result'], filterParams: (params: FillLabelsFnParams) => FillLabelsFnParams | null): FillLabelsFnParams | null;
|
|
20
20
|
export type FillLabelsFn<LabelNames extends string> = (params: FillLabelsFnParams, rawContext: any) => Record<LabelNames, string>;
|
|
21
21
|
export declare function createHistogram<LabelNames extends string>(options: {
|
|
22
22
|
histogram: Histogram<LabelNames>;
|