UNPKG

@envelop/prometheus

Version:

This plugin tracks the complete execution flow, and reports metrics using Prometheus tracing (based on `prom-client`).

211 lines (210 loc) • 8.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.instrumentRegistry = exports.filterFillParamsFnParams = exports.labelExists = exports.extractDeprecatedFields = exports.getCounterFromConfig = exports.getSummaryFromConfig = exports.getHistogramFromConfig = exports.createCounter = exports.registerCounter = exports.createSummary = exports.registerSummary = exports.createHistogram = exports.registerHistogram = exports.createFillLabelFnParams = exports.shouldTraceFieldResolver = void 0; const graphql_1 = require("graphql"); const prom_client_1 = require("prom-client"); const histograms = new WeakMap(); const summaries = new WeakMap(); const counters = new WeakMap(); function shouldTraceFieldResolver(info, whitelist) { if (!whitelist) { return true; } const parentType = info.parentType.name; const fieldName = info.fieldName; const coordinate = `${parentType}.${fieldName}`; return whitelist.includes(coordinate) || whitelist.includes(`${parentType}.*`); } exports.shouldTraceFieldResolver = shouldTraceFieldResolver; function getOperation(document) { return document.definitions[0]; } function createFillLabelFnParams(parseResult, context, filterParams) { if (parseResult === null) { return null; } if (parseResult instanceof Error) { return null; } const operation = getOperation(parseResult); return filterParams({ document: parseResult, operationName: context?.params?.operationName || operation.name?.value || 'Anonymous', operationType: operation.operation, }); } exports.createFillLabelFnParams = createFillLabelFnParams; function registerHistogram(registry, conf) { if (!histograms.has(registry)) { histograms.set(registry, new Map()); } const registryHistograms = histograms.get(registry); if (!registryHistograms.has(conf.name)) { conf.registers = [registry]; registryHistograms.set(conf.name, new prom_client_1.Histogram(conf)); } return registryHistograms.get(conf.name); } exports.registerHistogram = registerHistogram; function createHistogram(options) { return { histogram: registerHistogram(options.registry, options.histogram), // histogram: new Histogram(options.histogram), fillLabelsFn: options.fillLabelsFn, }; } exports.createHistogram = createHistogram; function registerSummary(registry, conf) { if (!summaries.has(registry)) { summaries.set(registry, new Map()); } const registrySummaries = summaries.get(registry); if (!registrySummaries.has(conf.name)) { conf.registers = [registry]; registrySummaries.set(conf.name, new prom_client_1.Summary(conf)); } return registrySummaries.get(conf.name); } exports.registerSummary = registerSummary; function createSummary(options) { return { summary: registerSummary(options.registry, options.summary), fillLabelsFn: options.fillLabelsFn, }; } exports.createSummary = createSummary; function registerCounter(registry, conf) { if (!counters.has(registry)) { counters.set(registry, new Map()); } const registryCounters = counters.get(registry); if (!registryCounters.has(conf.name)) { conf.registers = [registry]; registryCounters.set(conf.name, new prom_client_1.Counter(conf)); } return registryCounters.get(conf.name); } exports.registerCounter = registerCounter; function createCounter(options) { return { counter: registerCounter(options.registry, options.counter), fillLabelsFn: options.fillLabelsFn, }; } exports.createCounter = createCounter; function getHistogramFromConfig(config, phase, histogram, fillLabelsFn = params => filterFillParamsFnParams(config, { operationName: params.operationName, operationType: params.operationType, })) { const metric = config.metrics[phase]; return typeof metric === 'object' ? metric : metric === true ? createHistogram({ registry: config.registry || prom_client_1.register, histogram: { name: typeof metric === 'string' ? metric : phase, labelNames: ['operationType', 'operationName'].filter(label => labelExists(config, label)), ...histogram, }, fillLabelsFn, }) : undefined; } exports.getHistogramFromConfig = getHistogramFromConfig; function getSummaryFromConfig(config, phase, summary, fillLabelsFn = params => filterFillParamsFnParams(config, { operationName: params.operationName, operationType: params.operationType, })) { const metric = config.metrics[phase]; return typeof metric === 'object' ? metric : metric === true ? createSummary({ registry: config.registry || prom_client_1.register, summary: { name: typeof metric === 'string' ? metric : phase, labelNames: ['operationType', 'operationName'].filter(label => labelExists(config, label)), ...summary, }, fillLabelsFn, }) : undefined; } exports.getSummaryFromConfig = getSummaryFromConfig; function getCounterFromConfig(config, phase, counter, fillLabelsFn = params => filterFillParamsFnParams(config, { operationName: params.operationName, operationType: params.operationType, })) { const metric = config.metrics[phase]; return typeof metric === 'object' ? metric : metric === true ? createCounter({ registry: config.registry || prom_client_1.register, counter: { name: typeof metric === 'string' ? metric : phase, labelNames: ['operationType', 'operationName'].filter(label => labelExists(config, label)), ...counter, }, fillLabelsFn, }) : undefined; } exports.getCounterFromConfig = getCounterFromConfig; function extractDeprecatedFields(node, typeInfo) { const found = []; (0, graphql_1.visit)(node, (0, graphql_1.visitWithTypeInfo)(typeInfo, { Argument: () => { const argument = typeInfo.getArgument(); const field = typeInfo.getFieldDef(); if (field && argument && (argument.deprecationReason != null || argument.isDeprecated)) { found.push({ fieldName: argument.name, // the GraphQLArgument type doesn't contain context regarding the mutation the argument was passed to // however, when visiting an argument, typeInfo.getFieldDef returns the mutation typeName: field.name, // this is the mutation name }); } }, Field: () => { const field = typeInfo.getFieldDef(); if (field && (field.deprecationReason != null || field.isDeprecated)) { found.push({ fieldName: field.name, typeName: typeInfo.getParentType().name || '', }); } }, })); return found; } exports.extractDeprecatedFields = extractDeprecatedFields; function labelExists(config, label) { const labelFlag = config.labels?.[label]; if (labelFlag == null) { return true; } return labelFlag; } exports.labelExists = labelExists; function filterFillParamsFnParams(config, params) { return Object.fromEntries(Object.entries(params).filter(([key]) => labelExists(config, key))); } exports.filterFillParamsFnParams = filterFillParamsFnParams; const clearRegistry = new WeakMap(); function instrumentRegistry(registry) { if (!clearRegistry.has(registry)) { clearRegistry.set(registry, registry.clear.bind(registry)); } registry.clear = () => { histograms.delete(registry); summaries.delete(registry); counters.delete(registry); clearRegistry.get(registry)(); }; return registry; } exports.instrumentRegistry = instrumentRegistry;