@envelop/prometheus 6.4.0-alpha-db79afa.0 → 6.4.0-alpha-05dbec7.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/config.js +2 -0
- package/{index.js → cjs/index.js} +38 -112
- package/cjs/package.json +1 -0
- package/cjs/utils.js +81 -0
- package/esm/config.js +1 -0
- package/{index.mjs → esm/index.js} +12 -85
- package/esm/utils.js +71 -0
- package/package.json +35 -14
- package/{config.d.ts → typings/config.d.ts} +1 -1
- package/{index.d.ts → typings/index.d.ts} +2 -2
- package/{utils.d.ts → typings/utils.d.ts} +1 -1
- package/README.md +0 -112
package/cjs/config.js
ADDED
|
@@ -1,101 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const core = require('@envelop/core');
|
|
6
|
-
const graphql = require('graphql');
|
|
7
|
-
const promClient = require('prom-client');
|
|
8
|
-
|
|
9
|
-
function shouldTraceFieldResolver(info, whitelist) {
|
|
10
|
-
if (!whitelist) {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
const parentType = info.parentType.name;
|
|
14
|
-
const fieldName = info.fieldName;
|
|
15
|
-
const coordinate = `${parentType}.${fieldName}`;
|
|
16
|
-
return whitelist.includes(coordinate) || whitelist.includes(`${parentType}.*`);
|
|
17
|
-
}
|
|
18
|
-
function getOperation(document) {
|
|
19
|
-
return document.definitions[0];
|
|
20
|
-
}
|
|
21
|
-
function createInternalContext(parseResult) {
|
|
22
|
-
var _a;
|
|
23
|
-
if (parseResult === null) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
else if (parseResult instanceof Error) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
const operation = getOperation(parseResult);
|
|
31
|
-
return {
|
|
32
|
-
document: parseResult,
|
|
33
|
-
operationName: ((_a = operation.name) === null || _a === void 0 ? void 0 : _a.value) || 'Anonymous',
|
|
34
|
-
operationType: operation.operation,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function createHistogram(options) {
|
|
39
|
-
return options;
|
|
40
|
-
}
|
|
41
|
-
function createSummary(options) {
|
|
42
|
-
return options;
|
|
43
|
-
}
|
|
44
|
-
function createCounter(options) {
|
|
45
|
-
return options;
|
|
46
|
-
}
|
|
47
|
-
function getHistogramFromConfig(config, phase, name, help) {
|
|
48
|
-
return typeof config[phase] === 'object'
|
|
49
|
-
? config[phase]
|
|
50
|
-
: config[phase] === true
|
|
51
|
-
? createHistogram({
|
|
52
|
-
histogram: new promClient.Histogram({
|
|
53
|
-
name,
|
|
54
|
-
help,
|
|
55
|
-
labelNames: ['operationType', 'operationName'],
|
|
56
|
-
registers: [config.registry || promClient.register],
|
|
57
|
-
}),
|
|
58
|
-
fillLabelsFn: params => ({
|
|
59
|
-
operationName: params.operationName,
|
|
60
|
-
operationType: params.operationType,
|
|
61
|
-
}),
|
|
62
|
-
})
|
|
63
|
-
: undefined;
|
|
64
|
-
}
|
|
65
|
-
function extractDeprecatedFields(node, typeInfo) {
|
|
66
|
-
const found = [];
|
|
67
|
-
graphql.visit(node, graphql.visitWithTypeInfo(typeInfo, {
|
|
68
|
-
Field: () => {
|
|
69
|
-
const field = typeInfo.getFieldDef();
|
|
70
|
-
if (field && (field.deprecationReason != null || field.isDeprecated)) {
|
|
71
|
-
found.push({
|
|
72
|
-
fieldName: field.name,
|
|
73
|
-
typeName: typeInfo.getParentType().name || '',
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
}));
|
|
78
|
-
return found;
|
|
79
|
-
}
|
|
80
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePrometheus = exports.createSummary = exports.createHistogram = exports.createCounter = void 0;
|
|
81
4
|
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
|
|
5
|
+
const core_1 = require("@envelop/core");
|
|
6
|
+
const graphql_1 = require("graphql");
|
|
7
|
+
const prom_client_1 = require("prom-client");
|
|
8
|
+
const utils_js_1 = require("./utils.js");
|
|
9
|
+
Object.defineProperty(exports, "createHistogram", { enumerable: true, get: function () { return utils_js_1.createHistogram; } });
|
|
10
|
+
Object.defineProperty(exports, "createCounter", { enumerable: true, get: function () { return utils_js_1.createCounter; } });
|
|
11
|
+
Object.defineProperty(exports, "createSummary", { enumerable: true, get: function () { return utils_js_1.createSummary; } });
|
|
82
12
|
const promPluginContext = Symbol('promPluginContext');
|
|
83
13
|
const promPluginExecutionStartTimeSymbol = Symbol('promPluginExecutionStartTimeSymbol');
|
|
84
14
|
const usePrometheus = (config = {}) => {
|
|
85
15
|
let typeInfo = null;
|
|
86
|
-
const parseHistogram = getHistogramFromConfig(config, 'parse', 'graphql_envelop_phase_parse', 'Time spent on running GraphQL "parse" function');
|
|
87
|
-
const validateHistogram = getHistogramFromConfig(config, 'validate', 'graphql_envelop_phase_validate', 'Time spent on running GraphQL "validate" function');
|
|
88
|
-
const contextBuildingHistogram = getHistogramFromConfig(config, 'contextBuilding', 'graphql_envelop_phase_context', 'Time spent on building the GraphQL context');
|
|
89
|
-
const executeHistogram = getHistogramFromConfig(config, 'execute', 'graphql_envelop_phase_execute', 'Time spent on running the GraphQL "execute" function');
|
|
16
|
+
const parseHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'parse', 'graphql_envelop_phase_parse', 'Time spent on running GraphQL "parse" function');
|
|
17
|
+
const validateHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'validate', 'graphql_envelop_phase_validate', 'Time spent on running GraphQL "validate" function');
|
|
18
|
+
const contextBuildingHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'contextBuilding', 'graphql_envelop_phase_context', 'Time spent on building the GraphQL context');
|
|
19
|
+
const executeHistogram = (0, utils_js_1.getHistogramFromConfig)(config, 'execute', 'graphql_envelop_phase_execute', 'Time spent on running the GraphQL "execute" function');
|
|
90
20
|
const resolversHistogram = typeof config.resolvers === 'object'
|
|
91
21
|
? config.resolvers
|
|
92
22
|
: config.resolvers === true
|
|
93
|
-
? createHistogram({
|
|
94
|
-
histogram: new
|
|
23
|
+
? (0, utils_js_1.createHistogram)({
|
|
24
|
+
histogram: new prom_client_1.Histogram({
|
|
95
25
|
name: 'graphql_envelop_execute_resolver',
|
|
96
26
|
help: 'Time spent on running the GraphQL resolvers',
|
|
97
27
|
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName', 'returnType'],
|
|
98
|
-
registers: [config.registry ||
|
|
28
|
+
registers: [config.registry || prom_client_1.register],
|
|
99
29
|
}),
|
|
100
30
|
fillLabelsFn: params => {
|
|
101
31
|
var _a, _b, _c;
|
|
@@ -112,12 +42,12 @@ const usePrometheus = (config = {}) => {
|
|
|
112
42
|
const requestTotalHistogram = typeof config.requestTotalDuration === 'object'
|
|
113
43
|
? config.requestTotalDuration
|
|
114
44
|
: config.requestTotalDuration === true
|
|
115
|
-
? createHistogram({
|
|
116
|
-
histogram: new
|
|
45
|
+
? (0, utils_js_1.createHistogram)({
|
|
46
|
+
histogram: new prom_client_1.Histogram({
|
|
117
47
|
name: 'graphql_envelop_request_duration',
|
|
118
48
|
help: 'Time spent on running the GraphQL operation from parse to execute',
|
|
119
49
|
labelNames: ['operationType', 'operationName'],
|
|
120
|
-
registers: [config.registry ||
|
|
50
|
+
registers: [config.registry || prom_client_1.register],
|
|
121
51
|
}),
|
|
122
52
|
fillLabelsFn: params => ({
|
|
123
53
|
operationName: params.operationName,
|
|
@@ -128,12 +58,12 @@ const usePrometheus = (config = {}) => {
|
|
|
128
58
|
const requestSummary = typeof config.requestSummary === 'object'
|
|
129
59
|
? config.requestSummary
|
|
130
60
|
: config.requestSummary === true
|
|
131
|
-
? createSummary({
|
|
132
|
-
summary: new
|
|
61
|
+
? (0, utils_js_1.createSummary)({
|
|
62
|
+
summary: new prom_client_1.Summary({
|
|
133
63
|
name: 'graphql_envelop_request_time_summary',
|
|
134
64
|
help: 'Summary to measure the time to complete GraphQL operations',
|
|
135
65
|
labelNames: ['operationType', 'operationName'],
|
|
136
|
-
registers: [config.registry ||
|
|
66
|
+
registers: [config.registry || prom_client_1.register],
|
|
137
67
|
}),
|
|
138
68
|
fillLabelsFn: params => ({
|
|
139
69
|
operationName: params.operationName,
|
|
@@ -144,12 +74,12 @@ const usePrometheus = (config = {}) => {
|
|
|
144
74
|
const errorsCounter = typeof config.errors === 'object'
|
|
145
75
|
? config.errors
|
|
146
76
|
: config.errors === true
|
|
147
|
-
? createCounter({
|
|
148
|
-
counter: new
|
|
77
|
+
? (0, utils_js_1.createCounter)({
|
|
78
|
+
counter: new prom_client_1.Counter({
|
|
149
79
|
name: 'graphql_envelop_error_result',
|
|
150
80
|
help: 'Counts the amount of errors reported from all phases',
|
|
151
81
|
labelNames: ['operationType', 'operationName', 'path', 'phase'],
|
|
152
|
-
registers: [config.registry ||
|
|
82
|
+
registers: [config.registry || prom_client_1.register],
|
|
153
83
|
}),
|
|
154
84
|
fillLabelsFn: params => {
|
|
155
85
|
var _a, _b;
|
|
@@ -165,12 +95,12 @@ const usePrometheus = (config = {}) => {
|
|
|
165
95
|
const reqCounter = typeof config.requestCount === 'object'
|
|
166
96
|
? config.requestCount
|
|
167
97
|
: config.requestCount === true
|
|
168
|
-
? createCounter({
|
|
169
|
-
counter: new
|
|
98
|
+
? (0, utils_js_1.createCounter)({
|
|
99
|
+
counter: new prom_client_1.Counter({
|
|
170
100
|
name: 'graphql_envelop_request',
|
|
171
101
|
help: 'Counts the amount of GraphQL requests executed through Envelop',
|
|
172
102
|
labelNames: ['operationType', 'operationName'],
|
|
173
|
-
registers: [config.registry ||
|
|
103
|
+
registers: [config.registry || prom_client_1.register],
|
|
174
104
|
}),
|
|
175
105
|
fillLabelsFn: params => ({
|
|
176
106
|
operationName: params.operationName,
|
|
@@ -181,12 +111,12 @@ const usePrometheus = (config = {}) => {
|
|
|
181
111
|
const deprecationCounter = typeof config.deprecatedFields === 'object'
|
|
182
112
|
? config.deprecatedFields
|
|
183
113
|
: config.deprecatedFields === true
|
|
184
|
-
? createCounter({
|
|
185
|
-
counter: new
|
|
114
|
+
? (0, utils_js_1.createCounter)({
|
|
115
|
+
counter: new prom_client_1.Counter({
|
|
186
116
|
name: 'graphql_envelop_deprecated_field',
|
|
187
117
|
help: 'Counts the amount of deprecated fields used in selection sets',
|
|
188
118
|
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'],
|
|
189
|
-
registers: [config.registry ||
|
|
119
|
+
registers: [config.registry || prom_client_1.register],
|
|
190
120
|
}),
|
|
191
121
|
fillLabelsFn: params => {
|
|
192
122
|
var _a, _b;
|
|
@@ -200,20 +130,20 @@ const usePrometheus = (config = {}) => {
|
|
|
200
130
|
})
|
|
201
131
|
: undefined;
|
|
202
132
|
const onParse = ({ context, extendContext, params }) => {
|
|
203
|
-
if (config.skipIntrospection &&
|
|
133
|
+
if (config.skipIntrospection && (0, core_1.isIntrospectionOperationString)(params.source)) {
|
|
204
134
|
return;
|
|
205
135
|
}
|
|
206
136
|
const startTime = Date.now();
|
|
207
137
|
return params => {
|
|
208
138
|
const totalTime = (Date.now() - startTime) / 1000;
|
|
209
|
-
const internalContext = createInternalContext(params.result);
|
|
139
|
+
const internalContext = (0, utils_js_1.createInternalContext)(params.result);
|
|
210
140
|
if (internalContext) {
|
|
211
141
|
extendContext({
|
|
212
142
|
[promPluginContext]: internalContext,
|
|
213
143
|
});
|
|
214
144
|
parseHistogram === null || parseHistogram === void 0 ? void 0 : parseHistogram.histogram.observe(parseHistogram.fillLabelsFn(internalContext, context), totalTime);
|
|
215
145
|
if (deprecationCounter && typeInfo) {
|
|
216
|
-
const deprecatedFields = extractDeprecatedFields(internalContext.document, typeInfo);
|
|
146
|
+
const deprecatedFields = (0, utils_js_1.extractDeprecatedFields)(internalContext.document, typeInfo);
|
|
217
147
|
if (deprecatedFields.length > 0) {
|
|
218
148
|
for (const depField of deprecatedFields) {
|
|
219
149
|
deprecationCounter.counter
|
|
@@ -281,7 +211,7 @@ const usePrometheus = (config = {}) => {
|
|
|
281
211
|
const summaryTime = (Date.now() - args.contextValue[promPluginExecutionStartTimeSymbol]) / 1000;
|
|
282
212
|
requestSummary.summary.observe(requestSummary.fillLabelsFn(args.contextValue[promPluginContext], args.contextValue), summaryTime);
|
|
283
213
|
}
|
|
284
|
-
if (errorsCounter && !
|
|
214
|
+
if (errorsCounter && !(0, core_1.isAsyncIterable)(result) && result.errors && result.errors.length > 0) {
|
|
285
215
|
for (const error of result.errors) {
|
|
286
216
|
errorsCounter.counter
|
|
287
217
|
.labels(errorsCounter.fillLabelsFn({
|
|
@@ -300,7 +230,7 @@ const usePrometheus = (config = {}) => {
|
|
|
300
230
|
return {
|
|
301
231
|
onResolverCalled: resolversHistogram
|
|
302
232
|
? ({ info, context }) => {
|
|
303
|
-
const shouldTrace = shouldTraceFieldResolver(info, config.resolversWhitelist);
|
|
233
|
+
const shouldTrace = (0, utils_js_1.shouldTraceFieldResolver)(info, config.resolversWhitelist);
|
|
304
234
|
if (!shouldTrace) {
|
|
305
235
|
return undefined;
|
|
306
236
|
}
|
|
@@ -321,7 +251,7 @@ const usePrometheus = (config = {}) => {
|
|
|
321
251
|
});
|
|
322
252
|
},
|
|
323
253
|
onSchemaChange({ schema }) {
|
|
324
|
-
typeInfo = new
|
|
254
|
+
typeInfo = new graphql_1.TypeInfo(schema);
|
|
325
255
|
},
|
|
326
256
|
onParse,
|
|
327
257
|
onValidate,
|
|
@@ -329,8 +259,4 @@ const usePrometheus = (config = {}) => {
|
|
|
329
259
|
onExecute,
|
|
330
260
|
};
|
|
331
261
|
};
|
|
332
|
-
|
|
333
|
-
exports.createCounter = createCounter;
|
|
334
|
-
exports.createHistogram = createHistogram;
|
|
335
|
-
exports.createSummary = createSummary;
|
|
336
262
|
exports.usePrometheus = usePrometheus;
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
package/cjs/utils.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractDeprecatedFields = exports.getHistogramFromConfig = exports.createCounter = exports.createSummary = exports.createHistogram = exports.createInternalContext = exports.shouldTraceFieldResolver = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
const prom_client_1 = require("prom-client");
|
|
6
|
+
function shouldTraceFieldResolver(info, whitelist) {
|
|
7
|
+
if (!whitelist) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
const parentType = info.parentType.name;
|
|
11
|
+
const fieldName = info.fieldName;
|
|
12
|
+
const coordinate = `${parentType}.${fieldName}`;
|
|
13
|
+
return whitelist.includes(coordinate) || whitelist.includes(`${parentType}.*`);
|
|
14
|
+
}
|
|
15
|
+
exports.shouldTraceFieldResolver = shouldTraceFieldResolver;
|
|
16
|
+
function getOperation(document) {
|
|
17
|
+
return document.definitions[0];
|
|
18
|
+
}
|
|
19
|
+
function createInternalContext(parseResult) {
|
|
20
|
+
var _a;
|
|
21
|
+
if (parseResult === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (parseResult instanceof Error) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const operation = getOperation(parseResult);
|
|
28
|
+
return {
|
|
29
|
+
document: parseResult,
|
|
30
|
+
operationName: ((_a = operation.name) === null || _a === void 0 ? void 0 : _a.value) || 'Anonymous',
|
|
31
|
+
operationType: operation.operation,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
exports.createInternalContext = createInternalContext;
|
|
35
|
+
function createHistogram(options) {
|
|
36
|
+
return options;
|
|
37
|
+
}
|
|
38
|
+
exports.createHistogram = createHistogram;
|
|
39
|
+
function createSummary(options) {
|
|
40
|
+
return options;
|
|
41
|
+
}
|
|
42
|
+
exports.createSummary = createSummary;
|
|
43
|
+
function createCounter(options) {
|
|
44
|
+
return options;
|
|
45
|
+
}
|
|
46
|
+
exports.createCounter = createCounter;
|
|
47
|
+
function getHistogramFromConfig(config, phase, name, help) {
|
|
48
|
+
return typeof config[phase] === 'object'
|
|
49
|
+
? config[phase]
|
|
50
|
+
: config[phase] === true
|
|
51
|
+
? createHistogram({
|
|
52
|
+
histogram: new prom_client_1.Histogram({
|
|
53
|
+
name,
|
|
54
|
+
help,
|
|
55
|
+
labelNames: ['operationType', 'operationName'],
|
|
56
|
+
registers: [config.registry || prom_client_1.register],
|
|
57
|
+
}),
|
|
58
|
+
fillLabelsFn: params => ({
|
|
59
|
+
operationName: params.operationName,
|
|
60
|
+
operationType: params.operationType,
|
|
61
|
+
}),
|
|
62
|
+
})
|
|
63
|
+
: undefined;
|
|
64
|
+
}
|
|
65
|
+
exports.getHistogramFromConfig = getHistogramFromConfig;
|
|
66
|
+
function extractDeprecatedFields(node, typeInfo) {
|
|
67
|
+
const found = [];
|
|
68
|
+
(0, graphql_1.visit)(node, (0, graphql_1.visitWithTypeInfo)(typeInfo, {
|
|
69
|
+
Field: () => {
|
|
70
|
+
const field = typeInfo.getFieldDef();
|
|
71
|
+
if (field && (field.deprecationReason != null || field.isDeprecated)) {
|
|
72
|
+
found.push({
|
|
73
|
+
fieldName: field.name,
|
|
74
|
+
typeName: typeInfo.getParentType().name || '',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
return found;
|
|
80
|
+
}
|
|
81
|
+
exports.extractDeprecatedFields = extractDeprecatedFields;
|
package/esm/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,83 +1,12 @@
|
|
|
1
|
-
import { isIntrospectionOperationString, isAsyncIterable } from '@envelop/core';
|
|
2
|
-
import { visit, visitWithTypeInfo, TypeInfo } from 'graphql';
|
|
3
|
-
import { Histogram, register, Summary, Counter } from 'prom-client';
|
|
4
|
-
|
|
5
|
-
function shouldTraceFieldResolver(info, whitelist) {
|
|
6
|
-
if (!whitelist) {
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
const parentType = info.parentType.name;
|
|
10
|
-
const fieldName = info.fieldName;
|
|
11
|
-
const coordinate = `${parentType}.${fieldName}`;
|
|
12
|
-
return whitelist.includes(coordinate) || whitelist.includes(`${parentType}.*`);
|
|
13
|
-
}
|
|
14
|
-
function getOperation(document) {
|
|
15
|
-
return document.definitions[0];
|
|
16
|
-
}
|
|
17
|
-
function createInternalContext(parseResult) {
|
|
18
|
-
var _a;
|
|
19
|
-
if (parseResult === null) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
else if (parseResult instanceof Error) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
const operation = getOperation(parseResult);
|
|
27
|
-
return {
|
|
28
|
-
document: parseResult,
|
|
29
|
-
operationName: ((_a = operation.name) === null || _a === void 0 ? void 0 : _a.value) || 'Anonymous',
|
|
30
|
-
operationType: operation.operation,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function createHistogram(options) {
|
|
35
|
-
return options;
|
|
36
|
-
}
|
|
37
|
-
function createSummary(options) {
|
|
38
|
-
return options;
|
|
39
|
-
}
|
|
40
|
-
function createCounter(options) {
|
|
41
|
-
return options;
|
|
42
|
-
}
|
|
43
|
-
function getHistogramFromConfig(config, phase, name, help) {
|
|
44
|
-
return typeof config[phase] === 'object'
|
|
45
|
-
? config[phase]
|
|
46
|
-
: config[phase] === true
|
|
47
|
-
? createHistogram({
|
|
48
|
-
histogram: new Histogram({
|
|
49
|
-
name,
|
|
50
|
-
help,
|
|
51
|
-
labelNames: ['operationType', 'operationName'],
|
|
52
|
-
registers: [config.registry || register],
|
|
53
|
-
}),
|
|
54
|
-
fillLabelsFn: params => ({
|
|
55
|
-
operationName: params.operationName,
|
|
56
|
-
operationType: params.operationType,
|
|
57
|
-
}),
|
|
58
|
-
})
|
|
59
|
-
: undefined;
|
|
60
|
-
}
|
|
61
|
-
function extractDeprecatedFields(node, typeInfo) {
|
|
62
|
-
const found = [];
|
|
63
|
-
visit(node, visitWithTypeInfo(typeInfo, {
|
|
64
|
-
Field: () => {
|
|
65
|
-
const field = typeInfo.getFieldDef();
|
|
66
|
-
if (field && (field.deprecationReason != null || field.isDeprecated)) {
|
|
67
|
-
found.push({
|
|
68
|
-
fieldName: field.name,
|
|
69
|
-
typeName: typeInfo.getParentType().name || '',
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
}));
|
|
74
|
-
return found;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
1
|
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
|
|
2
|
+
import { isIntrospectionOperationString, isAsyncIterable, } from '@envelop/core';
|
|
3
|
+
import { TypeInfo } from 'graphql';
|
|
4
|
+
import { Summary, Counter, Histogram, register as defaultRegistry } from 'prom-client';
|
|
5
|
+
import { getHistogramFromConfig, createHistogram, createCounter, shouldTraceFieldResolver, createInternalContext, extractDeprecatedFields, createSummary, } from './utils.js';
|
|
6
|
+
export { createCounter, createHistogram, createSummary };
|
|
78
7
|
const promPluginContext = Symbol('promPluginContext');
|
|
79
8
|
const promPluginExecutionStartTimeSymbol = Symbol('promPluginExecutionStartTimeSymbol');
|
|
80
|
-
const usePrometheus = (config = {}) => {
|
|
9
|
+
export const usePrometheus = (config = {}) => {
|
|
81
10
|
let typeInfo = null;
|
|
82
11
|
const parseHistogram = getHistogramFromConfig(config, 'parse', 'graphql_envelop_phase_parse', 'Time spent on running GraphQL "parse" function');
|
|
83
12
|
const validateHistogram = getHistogramFromConfig(config, 'validate', 'graphql_envelop_phase_validate', 'Time spent on running GraphQL "validate" function');
|
|
@@ -91,7 +20,7 @@ const usePrometheus = (config = {}) => {
|
|
|
91
20
|
name: 'graphql_envelop_execute_resolver',
|
|
92
21
|
help: 'Time spent on running the GraphQL resolvers',
|
|
93
22
|
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName', 'returnType'],
|
|
94
|
-
registers: [config.registry ||
|
|
23
|
+
registers: [config.registry || defaultRegistry],
|
|
95
24
|
}),
|
|
96
25
|
fillLabelsFn: params => {
|
|
97
26
|
var _a, _b, _c;
|
|
@@ -113,7 +42,7 @@ const usePrometheus = (config = {}) => {
|
|
|
113
42
|
name: 'graphql_envelop_request_duration',
|
|
114
43
|
help: 'Time spent on running the GraphQL operation from parse to execute',
|
|
115
44
|
labelNames: ['operationType', 'operationName'],
|
|
116
|
-
registers: [config.registry ||
|
|
45
|
+
registers: [config.registry || defaultRegistry],
|
|
117
46
|
}),
|
|
118
47
|
fillLabelsFn: params => ({
|
|
119
48
|
operationName: params.operationName,
|
|
@@ -129,7 +58,7 @@ const usePrometheus = (config = {}) => {
|
|
|
129
58
|
name: 'graphql_envelop_request_time_summary',
|
|
130
59
|
help: 'Summary to measure the time to complete GraphQL operations',
|
|
131
60
|
labelNames: ['operationType', 'operationName'],
|
|
132
|
-
registers: [config.registry ||
|
|
61
|
+
registers: [config.registry || defaultRegistry],
|
|
133
62
|
}),
|
|
134
63
|
fillLabelsFn: params => ({
|
|
135
64
|
operationName: params.operationName,
|
|
@@ -145,7 +74,7 @@ const usePrometheus = (config = {}) => {
|
|
|
145
74
|
name: 'graphql_envelop_error_result',
|
|
146
75
|
help: 'Counts the amount of errors reported from all phases',
|
|
147
76
|
labelNames: ['operationType', 'operationName', 'path', 'phase'],
|
|
148
|
-
registers: [config.registry ||
|
|
77
|
+
registers: [config.registry || defaultRegistry],
|
|
149
78
|
}),
|
|
150
79
|
fillLabelsFn: params => {
|
|
151
80
|
var _a, _b;
|
|
@@ -166,7 +95,7 @@ const usePrometheus = (config = {}) => {
|
|
|
166
95
|
name: 'graphql_envelop_request',
|
|
167
96
|
help: 'Counts the amount of GraphQL requests executed through Envelop',
|
|
168
97
|
labelNames: ['operationType', 'operationName'],
|
|
169
|
-
registers: [config.registry ||
|
|
98
|
+
registers: [config.registry || defaultRegistry],
|
|
170
99
|
}),
|
|
171
100
|
fillLabelsFn: params => ({
|
|
172
101
|
operationName: params.operationName,
|
|
@@ -182,7 +111,7 @@ const usePrometheus = (config = {}) => {
|
|
|
182
111
|
name: 'graphql_envelop_deprecated_field',
|
|
183
112
|
help: 'Counts the amount of deprecated fields used in selection sets',
|
|
184
113
|
labelNames: ['operationType', 'operationName', 'fieldName', 'typeName'],
|
|
185
|
-
registers: [config.registry ||
|
|
114
|
+
registers: [config.registry || defaultRegistry],
|
|
186
115
|
}),
|
|
187
116
|
fillLabelsFn: params => {
|
|
188
117
|
var _a, _b;
|
|
@@ -325,5 +254,3 @@ const usePrometheus = (config = {}) => {
|
|
|
325
254
|
onExecute,
|
|
326
255
|
};
|
|
327
256
|
};
|
|
328
|
-
|
|
329
|
-
export { createCounter, createHistogram, createSummary, usePrometheus };
|
package/esm/utils.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { visit, visitWithTypeInfo, } from 'graphql';
|
|
2
|
+
import { Histogram, register as defaultRegistry } from 'prom-client';
|
|
3
|
+
export function shouldTraceFieldResolver(info, whitelist) {
|
|
4
|
+
if (!whitelist) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
const parentType = info.parentType.name;
|
|
8
|
+
const fieldName = info.fieldName;
|
|
9
|
+
const coordinate = `${parentType}.${fieldName}`;
|
|
10
|
+
return whitelist.includes(coordinate) || whitelist.includes(`${parentType}.*`);
|
|
11
|
+
}
|
|
12
|
+
function getOperation(document) {
|
|
13
|
+
return document.definitions[0];
|
|
14
|
+
}
|
|
15
|
+
export function createInternalContext(parseResult) {
|
|
16
|
+
var _a;
|
|
17
|
+
if (parseResult === null) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (parseResult instanceof Error) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const operation = getOperation(parseResult);
|
|
24
|
+
return {
|
|
25
|
+
document: parseResult,
|
|
26
|
+
operationName: ((_a = operation.name) === null || _a === void 0 ? void 0 : _a.value) || 'Anonymous',
|
|
27
|
+
operationType: operation.operation,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function createHistogram(options) {
|
|
31
|
+
return options;
|
|
32
|
+
}
|
|
33
|
+
export function createSummary(options) {
|
|
34
|
+
return options;
|
|
35
|
+
}
|
|
36
|
+
export function createCounter(options) {
|
|
37
|
+
return options;
|
|
38
|
+
}
|
|
39
|
+
export function getHistogramFromConfig(config, phase, name, help) {
|
|
40
|
+
return typeof config[phase] === 'object'
|
|
41
|
+
? config[phase]
|
|
42
|
+
: config[phase] === true
|
|
43
|
+
? createHistogram({
|
|
44
|
+
histogram: new Histogram({
|
|
45
|
+
name,
|
|
46
|
+
help,
|
|
47
|
+
labelNames: ['operationType', 'operationName'],
|
|
48
|
+
registers: [config.registry || defaultRegistry],
|
|
49
|
+
}),
|
|
50
|
+
fillLabelsFn: params => ({
|
|
51
|
+
operationName: params.operationName,
|
|
52
|
+
operationType: params.operationType,
|
|
53
|
+
}),
|
|
54
|
+
})
|
|
55
|
+
: undefined;
|
|
56
|
+
}
|
|
57
|
+
export function extractDeprecatedFields(node, typeInfo) {
|
|
58
|
+
const found = [];
|
|
59
|
+
visit(node, visitWithTypeInfo(typeInfo, {
|
|
60
|
+
Field: () => {
|
|
61
|
+
const field = typeInfo.getFieldDef();
|
|
62
|
+
if (field && (field.deprecationReason != null || field.isDeprecated)) {
|
|
63
|
+
found.push({
|
|
64
|
+
fieldName: field.name,
|
|
65
|
+
typeName: typeInfo.getParentType().name || '',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
}));
|
|
70
|
+
return found;
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/prometheus",
|
|
3
|
-
"version": "6.4.0-alpha-
|
|
3
|
+
"version": "6.4.0-alpha-05dbec7.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "2.4.0-alpha-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
6
|
+
"@envelop/core": "2.4.0-alpha-05dbec7.0",
|
|
7
|
+
"prom-client": "^13 || ^14.0.0",
|
|
8
|
+
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/
|
|
12
|
+
"url": "https://github.com/n1ru4l/envelop.git",
|
|
13
13
|
"directory": "packages/plugins/prometheus"
|
|
14
14
|
},
|
|
15
15
|
"author": "Dotan Simha <dotansimha@gmail.com>",
|
|
16
16
|
"license": "MIT",
|
|
17
|
-
"main": "index.js",
|
|
18
|
-
"module": "index.
|
|
19
|
-
"typings": "index.d.ts",
|
|
17
|
+
"main": "cjs/index.js",
|
|
18
|
+
"module": "esm/index.js",
|
|
19
|
+
"typings": "typings/index.d.ts",
|
|
20
20
|
"typescript": {
|
|
21
|
-
"definition": "index.d.ts"
|
|
21
|
+
"definition": "typings/index.d.ts"
|
|
22
22
|
},
|
|
23
|
+
"type": "module",
|
|
23
24
|
"exports": {
|
|
24
25
|
".": {
|
|
25
|
-
"require":
|
|
26
|
-
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./typings/index.d.ts",
|
|
28
|
+
"default": "./cjs/index.js"
|
|
29
|
+
},
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./typings/index.d.ts",
|
|
32
|
+
"default": "./esm/index.js"
|
|
33
|
+
},
|
|
34
|
+
"default": {
|
|
35
|
+
"types": "./typings/index.d.ts",
|
|
36
|
+
"default": "./esm/index.js"
|
|
37
|
+
}
|
|
27
38
|
},
|
|
28
39
|
"./*": {
|
|
29
|
-
"require":
|
|
30
|
-
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./typings/*.d.ts",
|
|
42
|
+
"default": "./cjs/*.js"
|
|
43
|
+
},
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./typings/*.d.ts",
|
|
46
|
+
"default": "./esm/*.js"
|
|
47
|
+
},
|
|
48
|
+
"default": {
|
|
49
|
+
"types": "./typings/*.d.ts",
|
|
50
|
+
"default": "./esm/*.js"
|
|
51
|
+
}
|
|
31
52
|
},
|
|
32
53
|
"./package.json": "./package.json"
|
|
33
54
|
}
|
|
34
|
-
}
|
|
55
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createCounter, createHistogram, createSummary } from './utils';
|
|
1
|
+
import { createCounter, createHistogram, createSummary } from './utils.js';
|
|
2
2
|
import { Registry } from 'prom-client';
|
|
3
3
|
export declare type PrometheusTracingPluginConfig = {
|
|
4
4
|
requestCount?: boolean | ReturnType<typeof createCounter>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from '@envelop/core';
|
|
2
|
-
import { createHistogram, createCounter, FillLabelsFnParams, createSummary } from './utils';
|
|
3
|
-
import { PrometheusTracingPluginConfig } from './config';
|
|
2
|
+
import { createHistogram, createCounter, FillLabelsFnParams, createSummary } from './utils.js';
|
|
3
|
+
import { PrometheusTracingPluginConfig } from './config.js';
|
|
4
4
|
export { PrometheusTracingPluginConfig, createCounter, createHistogram, createSummary, FillLabelsFnParams };
|
|
5
5
|
declare const promPluginContext: unique symbol;
|
|
6
6
|
declare const promPluginExecutionStartTimeSymbol: unique symbol;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphQLError, DocumentNode, OperationDefinitionNode, GraphQLResolveInfo, TypeInfo, ASTNode } from 'graphql';
|
|
2
2
|
import { AfterParseEventPayload } from '@envelop/core';
|
|
3
|
-
import { PrometheusTracingPluginConfig } from './config';
|
|
3
|
+
import { PrometheusTracingPluginConfig } from './config.js';
|
|
4
4
|
import { Counter, Histogram, Summary } from 'prom-client';
|
|
5
5
|
export declare type DeprecatedFieldInfo = {
|
|
6
6
|
fieldName: string;
|
package/README.md
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
## `@envelop/prometheus`
|
|
2
|
-
|
|
3
|
-
This plugin tracks the complete execution flow, and reports metrics using Prometheus tracing (based on `prom-client`).
|
|
4
|
-
|
|
5
|
-
You can opt-in to collect tracing from the following phases:
|
|
6
|
-
|
|
7
|
-
- Sucessfull requests (`requestCount`)
|
|
8
|
-
- Request summary (`requestSummary`)
|
|
9
|
-
- errors (categorized by `phase`)
|
|
10
|
-
- resolvers tracing and runtime
|
|
11
|
-
- deprecated fields usage
|
|
12
|
-
- count of graphql operations
|
|
13
|
-
- `parse` execution time
|
|
14
|
-
- `validate` execution time
|
|
15
|
-
- `contextBuilding` execution time
|
|
16
|
-
- `execute` execution time
|
|
17
|
-
|
|
18
|
-
> You can also customize each phase reporter, and add custom metadata and labels to the metrics.
|
|
19
|
-
|
|
20
|
-
## Getting Started
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
yarn add prom-client @envelop/prometheus
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Usage Example
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
import { envelop } from '@envelop/core';
|
|
30
|
-
import { usePrometheus } from '@envelop/prometheus';
|
|
31
|
-
|
|
32
|
-
const getEnveloped = envelop({
|
|
33
|
-
plugins: [
|
|
34
|
-
// ... other plugins ...
|
|
35
|
-
usePrometheus({
|
|
36
|
-
// all optional, and by default, all set to false, please opt-in to the metrics you wish to get
|
|
37
|
-
requestCount: true, // requries `execute` to be true as well
|
|
38
|
-
requestSummary: true, // requries `execute` to be true as well
|
|
39
|
-
parse: true,
|
|
40
|
-
validate: true,
|
|
41
|
-
contextBuilding: true,
|
|
42
|
-
execute: true,
|
|
43
|
-
errors: true,
|
|
44
|
-
resolvers: true, // requires "execute" to be `true` as well
|
|
45
|
-
resolversWhitelist: ['Mutation.*', 'Query.user'], // reports metrics als for these resolvers, leave `undefined` to report all fields
|
|
46
|
-
deprecatedFields: true,
|
|
47
|
-
registry: myRegistry, // If you are using a custom prom-client registry, please set it here
|
|
48
|
-
}),
|
|
49
|
-
],
|
|
50
|
-
});
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
> Note: Tracing resolvers using `resovlers: true` might have a performance impact on your GraphQL runtime. Please consider to test it locally first and then decide if it's needed.
|
|
54
|
-
|
|
55
|
-
### Custom registry
|
|
56
|
-
|
|
57
|
-
You can customize the `prom-client` `Registry` object if you are using a custom one, by passing it along with the configuration object:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import { Registry } from 'prom-client';
|
|
61
|
-
|
|
62
|
-
const myRegistry = new Registry();
|
|
63
|
-
|
|
64
|
-
const getEnveloped = envelop({
|
|
65
|
-
plugins: [
|
|
66
|
-
// ... other plugins ...
|
|
67
|
-
usePrometheus({
|
|
68
|
-
// ... config ...
|
|
69
|
-
registry: myRegistry,
|
|
70
|
-
}),
|
|
71
|
-
],
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
> Note: if you are using custom `prom-client` instances, you need to make sure to pass your registry there as well.
|
|
76
|
-
|
|
77
|
-
### Introspection
|
|
78
|
-
|
|
79
|
-
If you wish to disable introspection logging, you can use `skipIntrospection: true` in your config object.
|
|
80
|
-
|
|
81
|
-
### Custom `prom-client` instances
|
|
82
|
-
|
|
83
|
-
Each tracing field supports custom `prom-client` objects, and custom `labels` a metadata, you can create a custom extraction function for every `Histogram` / `Summary` / `Counter`:
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import { Histogram } from 'prom-client';
|
|
87
|
-
import { envelop } from '@envelop/core';
|
|
88
|
-
import { createHistogram, usePrometheus } from '@envelop/prometheus';
|
|
89
|
-
|
|
90
|
-
const getEnveloped = envelop({
|
|
91
|
-
plugins: [
|
|
92
|
-
// ... other plugins ...
|
|
93
|
-
usePrometheus({
|
|
94
|
-
// all optional, and by default, all set to false, please opt-in to the metrics you wish to get
|
|
95
|
-
parse: createHistogram({
|
|
96
|
-
histogram: new Histogram({
|
|
97
|
-
name: 'my_custom_name',
|
|
98
|
-
help: 'HELP ME',
|
|
99
|
-
labelNames: ['opText'] as const,
|
|
100
|
-
registers: [registry], // make sure to add your custom registry, if you are not using the default one
|
|
101
|
-
}),
|
|
102
|
-
fillLabelsFn: params => {
|
|
103
|
-
// if you wish to fill your `lables` with metadata, you can use the params in order to get access to things like DocumentNode, operationName, operationType, `error` (for error metrics) and `info` (for resolvers metrics)
|
|
104
|
-
return {
|
|
105
|
-
opText: print(params.document),
|
|
106
|
-
};
|
|
107
|
-
},
|
|
108
|
-
}),
|
|
109
|
-
}),
|
|
110
|
-
],
|
|
111
|
-
});
|
|
112
|
-
```
|