Skip to content

A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus metrics in standard exposition format.

License

Notifications You must be signed in to change notification settings

SourceRegistry/node-prometheus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

12 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“Š @sourceregistry/node-prometheus

A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus & OpenMetrics-compatible metrics.

License: Apache 2.0 npm version

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 count

Summary

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, # EOF is 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.server

Then 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: +Inf bucket, # EOF, correct Content-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 build and npm test pass

πŸ’‘ 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.

About

A lightweight, zero-dependency TypeScript library for creating and exposing Prometheus metrics in standard exposition format.

Resources

License

Stars

Watchers

Forks

Packages

No packages published