@sourceregistry/node-prometheus
v1.0.2
Published
A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus metrics in standard exposition and OpenMetrics format.
Maintainers
Readme
📊 @sourceregistry/node-prometheus
A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus & OpenMetrics-compatible metrics.
Supports:
- ✅ Counter
- ✅ Gauge
- ✅ Histogram
- ✅ Summary
- ✅ Untyped
Exports metrics in Prometheus exposition format and OpenMetrics v1.0.0 — ready for modern observability stacks.
🚀 Installation
npm install @sourceregistry/node-prometheus✅ Zero external dependencies — pure TypeScript.
🧩 Usage Examples
Counter
import { Counter } from '@sourceregistry/node-prometheus';
let requestCount = 0;
const counter = new Counter({
name: 'http_requests_total',
description: 'Total number of HTTP requests',
reader: () => [requestCount]
});
// Later...
requestCount++;Gauge
import { Gauge } from '@sourceregistry/node-prometheus';
const gauge = new Gauge({
name: 'current_temperature_celsius',
description: 'Current temperature in Celsius',
reader: () => [getTemperature()] // e.g., returns 23.5
});Histogram
import { Histogram } from '@sourceregistry/node-prometheus';
const histogram = new Histogram({
name: 'response_time_seconds',
description: 'HTTP response time in seconds',
buckets: [0.1, 0.5, 1, 2.5, 5]
});
histogram.observe(0.73); // Updates buckets, sum, and countSummary
import { Summary } from '@sourceregistry/node-prometheus';
const summary = new Summary({
name: 'request_duration_seconds',
description: 'Request duration with quantiles',
quantiles: [0.5, 0.9, 0.99],
calculate: (value, quantile) => {
// Example: simple moving average
const current = /* your state */ 0;
return current * 0.9 + value * 0.1;
}
});
summary.observe(1.2);Untyped
import { Untyped } from '@sourceregistry/node-prometheus';
const untyped = new Untyped({
name: 'some_legacy_metric',
value: 42
});
untyped.set(43);Combine Multiple Metrics (with Format Support)
Use Metric.concat() to serialize multiple metrics into a single string. You can specify 'prometheus' (default) or 'openmetrics' format.
import { Metric, Counter, Gauge } from '@sourceregistry/node-prometheus';
let requestCount = 0;
const counter = new Counter({
name: 'http_requests_total',
description: 'Total HTTP requests',
reader: () => [requestCount]
});
const gauge = new Gauge({
name: 'cpu_usage_percent',
description: 'Current CPU usage',
reader: () => [Math.random() * 100]
});
// Serialize in Prometheus format (default)
const promOutput = await Metric.concat('prometheus', counter, gauge);
console.log(promOutput);
// # HELP http_requests_total ...
// # TYPE http_requests_total counter
// http_requests_total 0 1712345678901
// ...
// Serialize in OpenMetrics format
const omOutput = await Metric.concat('openmetrics', counter, gauge);
console.log(omOutput);
// # HELP http_requests_total ...
// # TYPE http_requests_total counter
// http_requests_total 0 1712345678901
// ...
// # EOF✅ In OpenMetrics mode,
# EOFis automatically appended, and trailing whitespace is trimmed.
🌐 HTTP Server Example (Auto-negotiates Prometheus/OpenMetrics)
This example uses Metric.concat(format, ...) to serve the correct format based on the client’s Accept header.
// examples/server.ts
import { createServer } from 'http';
import { Counter, Gauge, Histogram, Metric, Untyped } from '@sourceregistry/node-prometheus';
const gauge = new Gauge({
name: 'random_gauge',
description: 'Random gauge value updated on each scrape',
reader: () => [Math.random() * 100],
});
const histogram = new Histogram({
name: 'random_histogram',
description: 'Histogram of random values',
buckets: [0.1, 0.2, 0.5, 1.0],
});
let hits = 0;
const counter = new Counter({
name: 'http_requests_total',
description: 'Total HTTP requests handled',
reader: () => [[hits, { method: 'GET', endpoint: '/metrics' }]],
});
const untyped = new Untyped({
name: 'uptime_seconds',
description: 'Server uptime in seconds',
value: [0, Date.now()],
});
// Simulate background metric updates
setInterval(() => {
histogram.observe(Math.random());
const uptime = (Date.now() - untyped.get()[1]) / 1000;
untyped.set(uptime);
}, 2000);
// HTTP Server with OpenMetrics negotiation
createServer(async (req, res) => {
console.log(`Scraped at ${new Date().toISOString()}`);
hits++;
const acceptHeader = req.headers['accept'] || '';
const format = acceptHeader.includes('application/openmetrics-text') ? 'openmetrics' : 'prometheus';
res.writeHead(200, {
'Content-Type': format === 'openmetrics'
? 'application/openmetrics-text; version=1.0.0; charset=utf-8'
: 'text/plain; version=0.0.4; charset=utf-8',
});
const output = await Metric.concat(format, gauge, histogram, counter, untyped);
res.end(output);
}).listen(8080, '0.0.0.0', () => {
console.log('✅ Metrics server: http://localhost:8080');
});Run it:
npm run example::http.serverThen test:
curl -H "Accept: application/openmetrics-text" http://localhost:8080
curl http://localhost:8080 # defaults to Prometheus format📏 Prometheus Exposition Format
Compatible with classic Prometheus scrapers:
# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",endpoint="/metrics"} 5 1712345678901
# HELP response_time_seconds HTTP response time in seconds
# TYPE response_time_seconds histogram
response_time_seconds_bucket{le="0.1"} 3
response_time_seconds_bucket{le="0.5"} 15
response_time_seconds_bucket{le="1"} 20
response_time_seconds_bucket{le="+Inf"} 20
response_time_seconds_sum 12.34
response_time_seconds_count 20📐 OpenMetrics Format (Modern Standard)
When client sends Accept: application/openmetrics-text, server responds with:
# HELP random_gauge Random gauge value updated on each scrape
# TYPE random_gauge gauge
random_gauge 42.17 1712345678901
# HELP random_histogram Histogram of random values
# TYPE random_histogram histogram
random_histogram_bucket{le="0.1"} 3 1712345678901
random_histogram_bucket{le="0.2"} 7 1712345678901
random_histogram_bucket{le="0.5"} 15 1712345678901
random_histogram_bucket{le="1.0"} 20 1712345678901
random_histogram_bucket{le="+Inf"} 20 1712345678901
random_histogram_sum 12.34 1712345678901
random_histogram_count 20 1712345678901
# HELP http_requests_total Total HTTP requests handled
# TYPE http_requests_total counter
http_requests_total{method="GET",endpoint="/metrics"} 5 1712345678901
# HELP uptime_seconds Server uptime in seconds
# TYPE uptime_seconds untyped
uptime_seconds 124.3 1712345678901
# EOF✅ Required:
+Infbucket,# EOF, correctContent-Type.
🛠️ Features
- ✅ Type-safe — Full TypeScript support with JSDoc.
- ✅ Async/Sync Readers — Gauge/Counter support both.
- ✅ Label Support — Per-metric and per-sample labels.
- ✅ Timestamps — Optional sample timestamps.
- ✅ Validation — Input sanitization and error handling.
- ✅ Extensible — Easy to extend or override behavior.
- ✅ OpenMetrics Ready — Auto-negotiation support in HTTP example.
- ✅ Zero Dependencies — Pure TypeScript, no bloat.
🧪 Testing
Comes with Vitest-compatible tests covering all metric types, serialization, edge cases, and error handling.
Run tests:
npm test # Watch mode
npm run test:ui # GUI
npm run test:coverage # Coverage report👾 Examples
See the examples/ folder in this repository for:
- Basic metric usage
- HTTP server with Prometheus/OpenMetrics support
- Histogram/Summary simulations
📜 License
Apache License 2.0 — See LICENSE for details.
🤝 Contributing
PRs welcome! Please ensure:
- ✅ Code matches existing style and JSDoc standards
- ✅ Tests are added for new features
- ✅ No external dependencies added
- ✅
npm run buildandnpm testpass
💡 Note: This library generates exposition format only. You must expose it via HTTP (e.g., Express, Fastify, or plain
http) for Prometheus to scrape. See the HTTP example above to get started quickly.
