@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
JavaScript
;
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;