|
1 | 1 | /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
|
2 |
| -import { GraphQLSchema, TypeInfo } from 'graphql'; |
| 2 | +import { ExecutionResult, GraphQLSchema, TypeInfo } from 'graphql'; |
3 | 3 | import { Counter, register as defaultRegistry, Histogram, Summary } from 'prom-client';
|
4 | 4 | import {
|
5 | 5 | isAsyncIterable,
|
|
8 | 8 | OnExecuteHook,
|
9 | 9 | OnExecuteHookResult,
|
10 | 10 | OnParseHook,
|
| 11 | + OnSubscribeHook, |
| 12 | + OnSubscribeHookResult, |
11 | 13 | OnValidateHook,
|
12 | 14 | Plugin,
|
13 | 15 | } from '@envelop/core';
|
@@ -62,6 +64,12 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
|
62 | 64 | 'graphql_envelop_phase_execute',
|
63 | 65 | 'Time spent on running the GraphQL "execute" function',
|
64 | 66 | );
|
| 67 | + const subscribeHistogram = getHistogramFromConfig( |
| 68 | + config, |
| 69 | + 'subscribe', |
| 70 | + 'graphql_envelop_phase_subscribe', |
| 71 | + 'Time spent on running the GraphQL "subscribe" function', |
| 72 | + ); |
65 | 73 |
|
66 | 74 | function labelExists(label: string) {
|
67 | 75 | const labelFlag = config.labels?.[label];
|
@@ -330,49 +338,138 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
|
330 | 338 | .labels(reqCounter.fillLabelsFn(fillLabelsFnParams, args.contextValue))
|
331 | 339 | .inc();
|
332 | 340 |
|
| 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 | + |
333 | 360 | const result: OnExecuteHookResult<{}> = {
|
334 | 361 | 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 | + }; |
340 | 400 |
|
341 |
| - requestTotalHistogram?.histogram.observe( |
342 |
| - requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), |
343 |
| - totalTime, |
344 |
| - ); |
| 401 | + return result; |
| 402 | + } |
| 403 | + : undefined; |
345 | 404 |
|
| 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 }) => { |
346 | 438 | 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 | + ); |
349 | 445 |
|
350 |
| - requestSummary.summary.observe( |
351 |
| - requestSummary.fillLabelsFn(fillLabelsFnParams, args.contextValue), |
352 |
| - summaryTime, |
| 446 | + requestTotalHistogram?.histogram.observe( |
| 447 | + requestTotalHistogram.fillLabelsFn(fillLabelsFnParams, args.contextValue), |
| 448 | + totalTime, |
353 | 449 | );
|
354 |
| - } |
355 | 450 |
|
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 | + ); |
375 | 458 | }
|
| 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 | + }; |
376 | 473 | }
|
377 | 474 | },
|
378 | 475 | };
|
@@ -440,5 +537,6 @@ export const usePrometheus = (config: PrometheusTracingPluginConfig = {}): Plugi
|
440 | 537 | onValidate,
|
441 | 538 | onContextBuilding,
|
442 | 539 | onExecute,
|
| 540 | + onSubscribe, |
443 | 541 | };
|
444 | 542 | };
|
0 commit comments