Skip to content

Commit

Permalink
feat: add prometheus report support
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Oct 21, 2024
1 parent f080830 commit fcb8f22
Show file tree
Hide file tree
Showing 16 changed files with 512 additions and 19 deletions.
56 changes: 40 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import path from 'path';
import { monitorPageManager } from './model/monitor/page/manager.js';
import { ExpressAuth } from '@auth/express';
import { authConfig } from './model/auth.js';
import { prometheusApiVersion } from './middleware/prometheus/index.js';

const app = express();

app.set('trust proxy', true);
app.use(prometheusApiVersion());
app.use(compression());
app.use(
express.json({
Expand Down
3 changes: 3 additions & 0 deletions src/server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { initCronjob } from './cronjob/index.js';
import { logger } from './utils/logger.js';
import { app } from './app.js';
import { runMQWorker } from './mq/worker.js';
import { initCounter } from './utils/prometheus/index.js';

const port = env.port;

Expand All @@ -20,6 +21,8 @@ initSocketio(httpServer);

initCronjob();

initCounter();

runMQWorker();

monitorManager.startAll();
Expand Down
1 change: 1 addition & 0 deletions src/server/middleware/prometheus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This folder is fork from https://github.com/PayU/prometheus-api-metrics
132 changes: 132 additions & 0 deletions src/server/middleware/prometheus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
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);
}
Loading

0 comments on commit fcb8f22

Please sign in to comment.