Skip to content

Commit

Permalink
feat(metrics): implement ability to collect default prometheus metrics
Browse files Browse the repository at this point in the history
Add configuration option `collectDefaultMetrics` that when set to `true` will enable the plugin to
collect default prometheus client metrics (e.g. cpu/ram)

fix #4
  • Loading branch information
Ed Clement committed Dec 23, 2021
1 parent dbd0afe commit 001e8f8
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ middlewares:
## Optional. Defaults to `false` so make sure to set this to `true` if you want to collect metrics.
metricsEnabled: true

## Optional. Collect default prometheus metrics. Defaults to `false`.
## Refer to: https://github.com/siimon/prom-client/tree/v14.0.1#default-metrics
collectDefaultMetrics: false

## Optional. Defaults to `/-/metrics`.
metricsPath: /custom/path/metrics

Expand Down
9 changes: 7 additions & 2 deletions src/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { register, Counter } from 'prom-client';
import { collectDefaultMetrics, register, Counter } from 'prom-client';
import { Logger, IPluginMiddleware, IBasicAuth, IStorageManager, PluginOptions } from '@verdaccio/types';
import { Request, Response, NextFunction, Application } from 'express';

Expand All @@ -22,14 +22,16 @@ export const REQUEST_COUNTER_OPTIONS = {
export default class VerdaccioMiddlewarePlugin implements IPluginMiddleware<MetricsConfig> {
public logger: Logger;
public metricsEnabled: boolean;
public collectDefaultMetrics: boolean;
public metricsPath: string;
public packageGroups: Record<string, string>;
private requestsCounter = new Counter(REQUEST_COUNTER_OPTIONS);

public constructor(config: MetricsConfig, options: PluginOptions<MetricsConfig>) {
this.metricsEnabled = [true, 'true'].includes(config.metricsEnabled);
this.collectDefaultMetrics = [true, 'true'].includes(config.collectDefaultMetrics);
this.metricsPath = config.metricsPath || '/-/metrics';
this.packageGroups = config.packageGroups || [];
this.packageGroups = config.packageGroups || {};
this.logger = options.logger;
}

Expand All @@ -48,6 +50,9 @@ export default class VerdaccioMiddlewarePlugin implements IPluginMiddleware<Metr
this.logger.info(`metrics: [register_middlewares] metrics are enabled and exposed at '${this.metricsPath}'`);
app.get(/.*[.]tgz$/i, this.collectMetrics.bind(this));
app.get(this.metricsPath, this.getMetrics.bind(this));
if (this.collectDefaultMetrics) {
collectDefaultMetrics();
}
} else {
this.logger.warn('metrics: [register_middlewares] metrics are disabled');
}
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Config } from '@verdaccio/types';

export interface MetricsConfig extends Config {
metricsEnabled: boolean;
collectDefaultMetrics: boolean;
metricsPath: string;
packageGroups: Record<string, string>;
}
Expand Down
52 changes: 52 additions & 0 deletions tests/metrics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,56 @@ describe('Metrics Plugin', () => {
);
});
});

describe('should collect default prometheus metrics when `collectDefaultMetrics` is set to `true`', () => {
const { req, res, next } = getExpressMocks();
const app = { get: jest.fn() } as unknown as Application;

beforeAll(() => {
register.clear();
const metricsPlugin = new MetricsPlugin(
{ metricsEnabled: true, collectDefaultMetrics: true } as MetricsConfig,
{ logger: getLogger() } as PluginOptions<MetricsConfig>
);
metricsPlugin.register_middlewares(app, {} as IBasicAuth<MetricsConfig>, {} as IStorageManager<MetricsConfig>);
metricsPlugin.collectMetrics(req, res, next);
metricsPlugin.getMetrics(req, res);
});

test('should collect default prometheus metrics', async () => {
const metrics = await register.getMetricsAsJSON();
expect(metrics.map(({ name }) => name).sort()).toEqual([
'nodejs_active_handles',
'nodejs_active_handles_total',
'nodejs_active_requests',
'nodejs_active_requests_total',
'nodejs_eventloop_lag_max_seconds',
'nodejs_eventloop_lag_mean_seconds',
'nodejs_eventloop_lag_min_seconds',
'nodejs_eventloop_lag_p50_seconds',
'nodejs_eventloop_lag_p90_seconds',
'nodejs_eventloop_lag_p99_seconds',
'nodejs_eventloop_lag_seconds',
'nodejs_eventloop_lag_stddev_seconds',
'nodejs_external_memory_bytes',
'nodejs_gc_duration_seconds',
'nodejs_heap_size_total_bytes',
'nodejs_heap_size_used_bytes',
'nodejs_heap_space_size_available_bytes',
'nodejs_heap_space_size_total_bytes',
'nodejs_heap_space_size_used_bytes',
'nodejs_version_info',
'process_cpu_seconds_total',
'process_cpu_system_seconds_total',
'process_cpu_user_seconds_total',
'process_heap_bytes',
'process_max_fds',
'process_open_fds',
'process_resident_memory_bytes',
'process_start_time_seconds',
'process_virtual_memory_bytes',
'registry_requests',
]);
});
});
});

0 comments on commit 001e8f8

Please sign in to comment.