Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ project adheres to [Semantic Versioning](http://semver.org/).
- chore: add test for `process_start_time_seconds`

### Added
- `nodejs_gc_runs` metric to the `collectDefaultMetrics()`. It counts number of GC runs with split by GC type.
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
- `nodejs_gc_duration_summary` metric to the `collectDefaultMetrics()`. It counts 0.5, 0.75, 0.9, 0.99 percentiles of GC duration (in seconds).
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated

## [11.5.3] - 2019-06-27

Expand Down
13 changes: 13 additions & 0 deletions example/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ if (cluster.isWorker) {
}, 2000);
}

// Generate some garbage
const t = [];
setInterval(() => {
for (let i = 0; i < 100; i++) {
t.push(new Date());
}
}, 10);
setInterval(() => {
while (t.length > 0) {
t.pop();
}
});

server.get('/metrics', (req, res) => {
res.set('Content-Type', register.contentType);
res.end(register.metrics());
Expand Down
4 changes: 3 additions & 1 deletion lib/defaultMetrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const processRequests = require('./metrics/processRequests');
const heapSizeAndUsed = require('./metrics/heapSizeAndUsed');
const heapSpacesSizeAndUsed = require('./metrics/heapSpacesSizeAndUsed');
const version = require('./metrics/version');
const gc = require('./metrics/gc');
const { globalRegistry } = require('./registry');
const { printDeprecationCollectDefaultMetricsNumber } = require('./util');

Expand All @@ -25,7 +26,8 @@ const metrics = {
processRequests,
heapSizeAndUsed,
heapSpacesSizeAndUsed,
version
version,
Comment thread
zbjornson marked this conversation as resolved.
gc
};
const metricsList = Object.keys(metrics);

Expand Down
76 changes: 76 additions & 0 deletions lib/metrics/gc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict';
const Counter = require('../counter');
const Histogram = require('../histogram');

let perf_hooks;

try {
// eslint-disable-next-line
perf_hooks = require('perf_hooks');
} catch (e) {
// node version is too old
}

const NODEJS_GC_RUNS_TOTAL = 'nodejs_gc_runs_total';
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
const NODEJS_GC_DURATION = 'nodejs_gc_duration';
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
const DEFAULT_GC_DURATION_BUCKETS = [
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
0.001,
0.005,
0.01,
0.025,
0.05,
0.1,
0.25,
0.5,
1,
2.5,
5
];

const kinds = [];
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR] = 'major';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR] = 'minor';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL] = 'incremental';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB] = 'weakcb';

module.exports = (registry, config = {}) => {
if (!perf_hooks) {
return () => {};
}
Comment thread
yvasiyarov marked this conversation as resolved.

const namePrefix = config.prefix ? config.prefix : '';
const buckets = config.gcDurationBuckets
? config.gcDurationBuckets
: DEFAULT_GC_DURATION_BUCKETS;
const gcCount = new Counter({
name: namePrefix + NODEJS_GC_RUNS_TOTAL,
help:
'Count of garbage collections. kind label is one of major, minor, incremental or weakcb.',
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
labelNames: ['kind'],
registers: registry ? [registry] : undefined
});
const gcHistogram = new Histogram({
name: namePrefix + NODEJS_GC_DURATION,
help:
'Histogram of garbage collections. kind label is one of major, minor, incremental or weakcb.',
Comment thread
yvasiyarov marked this conversation as resolved.
Outdated
labelNames: ['kind'],
buckets,
registers: registry ? [registry] : undefined
});

const obs = new perf_hooks.PerformanceObserver(list => {
const entry = list.getEntries()[0];
const labels = { kind: kinds[entry.kind] };

gcCount.inc(labels, 1);
// Convert duration from milliseconds to seconds
gcHistogram.observe(labels, entry.duration / 1000);
});

// We do not expect too many gc events per second, so we do not use buffering
obs.observe({ entryTypes: ['gc'], buffered: false });

return () => {};
};

module.exports.metricNames = [NODEJS_GC_RUNS_TOTAL, NODEJS_GC_DURATION];
49 changes: 49 additions & 0 deletions test/metrics/gcTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

describe('gc', () => {
const register = require('../../index').register;
const processHandles = require('../../lib/metrics/gc');

beforeAll(() => {
register.clear();
});

afterEach(() => {
register.clear();
});

it('should add metric to the registry', () => {
expect(register.getMetricsAsJSON()).toHaveLength(0);

processHandles()();

const metrics = register.getMetricsAsJSON();

// Check if perf_hooks module is available
let perf_hooks;
try {
// eslint-disable-next-line
perf_hooks = require('perf_hooks');
} catch (e) {
// node version is too old
}

if (perf_hooks) {
expect(metrics).toHaveLength(2);

expect(metrics[0].help).toEqual(
'Count of garbage collections. kind label is one of major, minor, incremental or weakcb.'
);
expect(metrics[0].type).toEqual('counter');
expect(metrics[0].name).toEqual('nodejs_gc_runs_total');

expect(metrics[1].help).toEqual(
'Histogram of garbage collections. kind label is one of major, minor, incremental or weakcb.'
);
expect(metrics[1].type).toEqual('histogram');
expect(metrics[1].name).toEqual('nodejs_gc_duration');
} else {
expect(metrics).toHaveLength(0);
}
});
});