Skip to content

Commit 37d4dac

Browse files
committed
feat(prom): support subscriptions
1 parent 13bb9ff commit 37d4dac

File tree

3 files changed

+139
-35
lines changed

3 files changed

+139
-35
lines changed

.changeset/famous-apples-hunt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@envelop/prometheus': minor
3+
---
4+
5+
Support subscriptions

packages/plugins/prometheus/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type PrometheusTracingPluginConfig = {
99
validate?: boolean | ReturnType<typeof createHistogram>;
1010
contextBuilding?: boolean | ReturnType<typeof createHistogram>;
1111
execute?: boolean | ReturnType<typeof createHistogram>;
12+
subscribe?: boolean | ReturnType<typeof createHistogram>;
1213
errors?: boolean | ReturnType<typeof createCounter>;
1314
resolvers?: boolean | ReturnType<typeof createHistogram>;
1415
resolversWhitelist?: string[];

packages/plugins/prometheus/src/index.ts

Lines changed: 133 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
2-
import { GraphQLSchema, TypeInfo } from 'graphql';
2+
import { ExecutionResult, GraphQLSchema, TypeInfo } from 'graphql';
33
import { Counter, register as defaultRegistry, Histogram, Summary } from 'prom-client';
44
import {
55
isAsyncIterable,
@@ -8,6 +8,8 @@ import {
88
OnExecuteHook,
99
OnExecuteHookResult,
1010
OnParseHook,
11+
OnSubscribeHook,
12+
OnSubscribeHookResult,
1113
OnValidateHook,
1214
Plugin,
1315
} from '@envelop/core';
@@ -62,6 +64,12 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
6264
'graphql_envelop_phase_execute',
6365
'Time spent on running the GraphQL "execute" function',
6466
);
67+
const subscribeHistogram = getHistogramFromConfig(
68+
config,
69+
'subscribe',
70+
'graphql_envelop_phase_subscribe',
71+
'Time spent on running the GraphQL "subscribe" function',
72+
);
6573

6674
function labelExists(label: string) {
6775
const labelFlag = config.labels?.[label];
@@ -330,49 +338,138 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
330338
.labels(reqCounter.fillLabelsFn(fillLabelsFnParams, args.contextValue))
331339
.inc();
332340

341+
function handleResult(result: ExecutionResult) {
342+
if (errorsCounter && result.errors && result.errors.length > 0) {
343+
for (const error of result.errors) {
344+
errorsCounter.counter
345+
.labels(
346+
errorsCounter.fillLabelsFn(
347+
{
348+
...fillLabelsFnParams,
349+
errorPhase: 'execute',
350+
error,
351+
},
352+
args.contextValue,
353+
),
354+
)
355+
.inc();
356+
}
357+
}
358+
}
359+
333360
const result: OnExecuteHookResult<{}> = {
334361
onExecuteDone: ({ result }) => {
335-
const totalTime = (Date.now() - startTime) / 1000;
336-
executeHistogram.histogram.observe(
337-
executeHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
338-
totalTime,
339-
);
362+
const execStartTime = execStartTimeMap.get(args.contextValue);
363+
const handleEnd = () => {
364+
const totalTime = (Date.now() - startTime) / 1000;
365+
executeHistogram.histogram.observe(
366+
executeHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
367+
totalTime,
368+
);
369+
370+
requestTotalHistogram?.histogram.observe(
371+
requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
372+
totalTime,
373+
);
374+
375+
if (requestSummary && execStartTime) {
376+
const summaryTime = (Date.now() - execStartTime) / 1000;
377+
378+
requestSummary.summary.observe(
379+
requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue),
380+
summaryTime,
381+
);
382+
}
383+
};
384+
if (!isAsyncIterable(result)) {
385+
handleResult(result);
386+
handleEnd();
387+
return undefined;
388+
} else {
389+
return {
390+
onNext({ result }) {
391+
handleResult(result);
392+
},
393+
onEnd() {
394+
handleEnd();
395+
},
396+
};
397+
}
398+
},
399+
};
340400

341-
requestTotalHistogram?.histogram.observe(
342-
requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
343-
totalTime,
344-
);
401+
return result;
402+
}
403+
: undefined;
345404

405+
const onSubscribe: OnSubscribeHook<{}> | undefined = subscribeHistogram
406+
? ({ args }) => {
407+
const fillLabelsFnParams = fillLabelsFnParamsMap.get(args.contextValue);
408+
if (!fillLabelsFnParams) {
409+
return undefined;
410+
}
411+
412+
const startTime = Date.now();
413+
reqCounter?.counter
414+
.labels(reqCounter.fillLabelsFn(fillLabelsFnParams, args.contextValue))
415+
.inc();
416+
417+
function handleResult(result: ExecutionResult) {
418+
if (errorsCounter && result.errors && result.errors.length > 0) {
419+
for (const error of result.errors) {
420+
errorsCounter.counter
421+
.labels(
422+
errorsCounter.fillLabelsFn(
423+
{
424+
...fillLabelsFnParams,
425+
errorPhase: 'execute',
426+
error,
427+
},
428+
args.contextValue,
429+
),
430+
)
431+
.inc();
432+
}
433+
}
434+
}
435+
436+
const result: OnSubscribeHookResult<{}> = {
437+
onSubscribeResult: ({ result }) => {
346438
const execStartTime = execStartTimeMap.get(args.contextValue);
347-
if (requestSummary && execStartTime) {
348-
const summaryTime = (Date.now() - execStartTime) / 1000;
439+
const handleEnd = () => {
440+
const totalTime = (Date.now() - startTime) / 1000;
441+
subscribeHistogram.histogram.observe(
442+
subscribeHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
443+
totalTime,
444+
);
349445

350-
requestSummary.summary.observe(
351-
requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue),
352-
summaryTime,
446+
requestTotalHistogram?.histogram.observe(
447+
requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue),
448+
totalTime,
353449
);
354-
}
355450

356-
if (
357-
errorsCounter &&
358-
!isAsyncIterable(result) &&
359-
result.errors &&
360-
result.errors.length > 0
361-
) {
362-
for (const error of result.errors) {
363-
errorsCounter.counter
364-
.labels(
365-
errorsCounter.fillLabelsFn(
366-
{
367-
...fillLabelsFnParams,
368-
errorPhase: 'execute',
369-
error,
370-
},
371-
args.contextValue,
372-
),
373-
)
374-
.inc();
451+
if (requestSummary && execStartTime) {
452+
const summaryTime = (Date.now() - execStartTime) / 1000;
453+
454+
requestSummary.summary.observe(
455+
requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue),
456+
summaryTime,
457+
);
375458
}
459+
};
460+
if (!isAsyncIterable(result)) {
461+
handleResult(result);
462+
handleEnd();
463+
return undefined;
464+
} else {
465+
return {
466+
onNext({ result }) {
467+
handleResult(result);
468+
},
469+
onEnd() {
470+
handleEnd();
471+
},
472+
};
376473
}
377474
},
378475
};
@@ -440,5 +537,6 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
440537
onValidate,
441538
onContextBuilding,
442539
onExecute,
540+
onSubscribe,
443541
};
444542
};

0 commit comments

Comments
 (0)