UNPKG

@graphql-yoga/plugin-prometheus

Version:
98 lines (97 loc) 4.2 kB
import { getOperationAST } from 'graphql'; import { register as defaultRegistry } from 'prom-client'; import { createCounter, createHistogram, createSummary, getCounterFromConfig, getHistogramFromConfig, getSummaryFromConfig, usePrometheus as useEnvelopPrometheus, } from '@envelop/prometheus'; export { createCounter, createHistogram, createSummary, getHistogramFromConfig, getCounterFromConfig, getSummaryFromConfig, }; const DEFAULT_METRICS_CONFIG = { graphql_envelop_deprecated_field: true, graphql_envelop_request: true, graphql_envelop_request_duration: true, graphql_envelop_request_time_summary: true, graphql_envelop_phase_parse: true, graphql_envelop_phase_validate: true, graphql_envelop_phase_context: true, graphql_envelop_error_result: true, graphql_envelop_execute_resolver: false, graphql_envelop_phase_execute: true, graphql_envelop_phase_subscribe: true, graphql_envelop_schema_change: true, graphql_yoga_http_duration: true, }; export function usePrometheus(options) { const endpoint = options.endpoint || '/metrics'; const registry = options.registry || defaultRegistry; const resolvedOptions = { ...options, metrics: { ...DEFAULT_METRICS_CONFIG, ...options.metrics, }, }; const basePlugin = { onPluginInit({ addPlugin }) { addPlugin(useEnvelopPrometheus({ ...resolvedOptions, registry })); addPlugin({ onRequest({ url, fetchAPI, endResponse }) { if (endpoint && url.pathname === endpoint) { return registry.metrics().then(metrics => { endResponse(new fetchAPI.Response(metrics, { headers: { 'Content-Type': registry.contentType, }, })); }); } return undefined; }, }); }, }; const httpHistogram = getHistogramFromConfig(resolvedOptions, 'graphql_yoga_http_duration', ['request'], { help: 'Time spent on HTTP connection', labelNames: ['operationName', 'operationType', 'method', 'statusCode', 'url'], }, (params, { request, response }) => ({ method: request.method, statusCode: response.status, operationType: params.operationType || 'unknown', operationName: params.operationName || 'Anonymous', url: request.url, })); // We don't need to register any hooks if the metric is not enabled if (!httpHistogram) { return basePlugin; } const startByRequest = new WeakMap(); const paramsByRequest = new WeakMap(); return { ...basePlugin, onRequest({ request }) { startByRequest.set(request, Date.now()); }, onParse({ context }) { // If only it is Yoga, we calculate HTTP request time if (context.request) { return ({ result: document, context }) => { const operationAST = getOperationAST(document, context.params.operationName); const params = { document, operationName: operationAST?.name?.value, operationType: operationAST?.operation, }; paramsByRequest.set(context.request, params); }; } return undefined; }, onResponse({ request, response, serverContext }) { const start = startByRequest.get(request); const params = paramsByRequest.get(request); if (start && params) { const context = { ...serverContext, request, response }; const completeParams = { ...params, request, response }; if (httpHistogram.shouldObserve(completeParams, context)) { httpHistogram.histogram.observe(httpHistogram.fillLabelsFn(completeParams, context), (Date.now() - start) / 1000); } } }, }; }