@graphql-yoga/plugin-prometheus
Version:
Prometheus plugin for GraphQL Yoga.
104 lines (103 loc) • 4.12 kB
JavaScript
import { usePrometheus as useEnvelopPrometheus, createCounter, createHistogram, createSummary, } from '@envelop/prometheus';
import { getOperationAST } from 'graphql';
import { Histogram, register as defaultRegistry } from 'prom-client';
export { createCounter, createHistogram, createSummary };
function headersToObj(headers) {
const obj = {};
headers.forEach((value, key) => {
obj[key] = value;
});
return obj;
}
export function usePrometheus(options) {
const endpoint = options.endpoint || '/metrics';
const registry = options.registry || defaultRegistry;
let httpHistogram;
if (options.http) {
const labelNames = [
'url',
'method',
'statusCode',
'statusText',
'operationName',
'operationType',
];
if (options.httpRequestHeaders) {
labelNames.push('requestHeaders');
}
if (options.httpResponseHeaders) {
labelNames.push('responseHeaders');
}
httpHistogram =
typeof options.http === 'object'
? options.http
: createHistogram({
histogram: new Histogram({
name: 'graphql_yoga_http_duration',
help: 'Time spent on HTTP connection',
labelNames,
registers: [registry],
}),
fillLabelsFn(params, { request, response }) {
const labels = {
operationName: params.operationName || 'Anonymous',
url: request.url,
method: request.method,
statusCode: response.status,
statusText: response.statusText,
};
if (params?.operationType) {
labels.operationType = params.operationType;
}
if (options.httpRequestHeaders) {
labels.requestHeaders = JSON.stringify(headersToObj(request.headers));
}
if (options.httpResponseHeaders) {
labels.responseHeaders = JSON.stringify(headersToObj(response.headers));
}
return labels;
},
});
}
const startByRequest = new WeakMap();
const paramsByRequest = new WeakMap();
return {
onPluginInit({ addPlugin }) {
addPlugin(useEnvelopPrometheus({ ...options, registry }));
},
async onRequest({ request, url, fetchAPI, endResponse }) {
startByRequest.set(request, Date.now());
if (url.pathname === endpoint) {
const metrics = await registry.metrics();
const response = new fetchAPI.Response(metrics, {
headers: {
'Content-Type': registry.contentType,
},
});
endResponse(response);
}
},
onExecute({ args }) {
const operationAST = getOperationAST(args.document, args.operationName);
const operationType = operationAST?.operation;
const operationName = operationAST?.name?.value;
paramsByRequest.set(args.contextValue.request, {
document: args.document,
operationName,
operationType,
});
},
onResponse({ request, response, serverContext }) {
const start = startByRequest.get(request);
if (start) {
const duration = Date.now() - start;
const params = paramsByRequest.get(request);
httpHistogram?.histogram.observe(httpHistogram.fillLabelsFn(params || {}, {
...serverContext,
request,
response,
}), duration);
}
},
};
}