133 lines
4.1 KiB
TypeScript
133 lines
4.1 KiB
TypeScript
import Prometheus from 'prom-client';
|
|
import * as utils from './utils.js';
|
|
import { SetupOptions, ApiMetricsOpts } from './types.js';
|
|
import { ExpressMiddleware } from './middleware.js';
|
|
|
|
export function prometheusApiVersion(options: ApiMetricsOpts = {}) {
|
|
const appVersion = '1.0.0';
|
|
const projectName = 'tianji';
|
|
|
|
const {
|
|
metricsPath,
|
|
defaultMetricsInterval = 10000,
|
|
durationBuckets,
|
|
requestSizeBuckets,
|
|
responseSizeBuckets,
|
|
useUniqueHistogramName,
|
|
metricsPrefix,
|
|
excludeRoutes,
|
|
includeQueryParams,
|
|
additionalLabels = [],
|
|
extractAdditionalLabelValuesFn,
|
|
} = options;
|
|
|
|
const setupOptions: SetupOptions = {};
|
|
|
|
setupOptions.metricsRoute = utils.validateInput({
|
|
input: metricsPath,
|
|
isValidInputFn: utils.isString,
|
|
defaultValue: '/_prom/metrics',
|
|
errorMessage: 'metricsPath should be an string',
|
|
});
|
|
|
|
setupOptions.excludeRoutes = utils.validateInput({
|
|
input: excludeRoutes,
|
|
isValidInputFn: utils.isArray,
|
|
defaultValue: [],
|
|
errorMessage: 'excludeRoutes should be an array',
|
|
});
|
|
|
|
setupOptions.includeQueryParams = includeQueryParams;
|
|
setupOptions.defaultMetricsInterval = defaultMetricsInterval;
|
|
|
|
setupOptions.additionalLabels = utils.validateInput({
|
|
input: additionalLabels,
|
|
isValidInputFn: utils.isArray,
|
|
defaultValue: [],
|
|
errorMessage: 'additionalLabels should be an array',
|
|
});
|
|
|
|
setupOptions.extractAdditionalLabelValuesFn = utils.validateInput({
|
|
input: extractAdditionalLabelValuesFn,
|
|
isValidInputFn: utils.isFunction,
|
|
defaultValue: () => ({}),
|
|
errorMessage: 'extractAdditionalLabelValuesFn should be a function',
|
|
});
|
|
|
|
const metricNames = utils.getMetricNames(
|
|
{
|
|
http_request_duration_seconds: 'http_request_duration_seconds',
|
|
app_version: 'app_version',
|
|
http_request_size_bytes: 'http_request_size_bytes',
|
|
http_response_size_bytes: 'http_response_size_bytes',
|
|
defaultMetricsPrefix: '',
|
|
},
|
|
useUniqueHistogramName ?? false,
|
|
metricsPrefix ?? '',
|
|
projectName
|
|
);
|
|
|
|
Prometheus.collectDefaultMetrics({
|
|
eventLoopMonitoringPrecision: defaultMetricsInterval,
|
|
prefix: `${metricNames.defaultMetricsPrefix}`,
|
|
});
|
|
|
|
PrometheusRegisterAppVersion(appVersion, metricNames.app_version);
|
|
|
|
const metricLabels = ['method', 'route', 'code', ...additionalLabels].filter(
|
|
Boolean
|
|
);
|
|
|
|
// Buckets for response time from 1ms to 500ms
|
|
const defaultDurationSecondsBuckets = [
|
|
0.001, 0.005, 0.015, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5,
|
|
];
|
|
// Buckets for request size from 5 bytes to 10000 bytes
|
|
const defaultSizeBytesBuckets = [
|
|
5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000,
|
|
];
|
|
|
|
setupOptions.responseTimeHistogram =
|
|
Prometheus.register.getSingleMetric(
|
|
metricNames.http_request_duration_seconds
|
|
) ||
|
|
new Prometheus.Histogram({
|
|
name: metricNames.http_request_duration_seconds,
|
|
help: 'Duration of HTTP requests in seconds',
|
|
labelNames: metricLabels,
|
|
buckets: durationBuckets || defaultDurationSecondsBuckets,
|
|
});
|
|
|
|
setupOptions.requestSizeHistogram =
|
|
Prometheus.register.getSingleMetric(metricNames.http_request_size_bytes) ||
|
|
new Prometheus.Histogram({
|
|
name: metricNames.http_request_size_bytes,
|
|
help: 'Size of HTTP requests in bytes',
|
|
labelNames: metricLabels,
|
|
buckets: requestSizeBuckets || defaultSizeBytesBuckets,
|
|
});
|
|
|
|
setupOptions.responseSizeHistogram =
|
|
Prometheus.register.getSingleMetric(metricNames.http_response_size_bytes) ||
|
|
new Prometheus.Histogram({
|
|
name: metricNames.http_response_size_bytes,
|
|
help: 'Size of HTTP response in bytes',
|
|
labelNames: metricLabels,
|
|
buckets: responseSizeBuckets || defaultSizeBytesBuckets,
|
|
});
|
|
|
|
const middleware = new ExpressMiddleware(setupOptions);
|
|
return middleware.middleware.bind(middleware);
|
|
}
|
|
|
|
function PrometheusRegisterAppVersion(appVersion: string, metricName: string) {
|
|
const version = new Prometheus.Gauge({
|
|
name: metricName,
|
|
help: 'The service version by package.json',
|
|
labelNames: ['version', 'major', 'minor', 'patch'],
|
|
});
|
|
|
|
const [major, minor, patch] = appVersion.split('.');
|
|
version.labels(appVersion, major, minor, patch).set(1);
|
|
}
|