npm.io
1.1.1 • Published 3d ago

@pawells/prometheus

Licence
MIT
Version
1.1.1
Deps
7
Size
125 kB
Vulns
0
Weekly
0

Prometheus Utility Library

GitHub Release CI npm version Node License: MIT GitHub Sponsors

Description

A framework-agnostic Prometheus metrics exporter for TypeScript/Node.js built on prom-client. Implements the MetricsExporter interface from @pawells/metrics to provide vendor-neutral metrics collection with support for both pull mode (HTTP /metrics endpoint) and push mode (Prometheus Pushgateway).

Key features:

  • Pull-based export — Buffers metric values in memory and flushes them atomically when /metrics is scraped
  • Push-mode support — Optional periodic push to a Prometheus Pushgateway with Bearer token or Basic auth; each push request has a 10 s AbortController timeout
  • Metric type mapping — Counter, Gauge, Updown Counter, Histogram with automatic bucket configuration, and Summary with server-side quantile configuration
  • Node.js integration — Automatically collects process metrics (CPU, memory, garbage collection, event loop)
  • Thread-safe buffering — Atomic swaps during flush to prevent data loss under concurrent recording
  • Graceful shutdown — Properly disables collectors and cleans up resources

For NestJS applications, use PrometheusModule from @pawells/nestjs-metrics instead of initializing directly — the module manages the exporter lifecycle and registers the HTTP endpoint automatically.

Requirements

  • Node.js >=22 (see package.json for exact version)
  • TypeScript ~6.0.0 (as a dev dependency in your project)
  • @pawells/metrics — the vendor-neutral foundation library (direct dependency, installed automatically)
  • prom-client ^15.1.3 (managed by this package)

Installation

yarn add @pawells/prometheus @pawells/metrics

This package is ESM-only ("type": "module"). See exports in package.json for entry points.

Quick Start

Pull Mode (Default)

Initialize the Prometheus manager and fetch metrics from a scrape endpoint:

import { PrometheusManager } from '@pawells/prometheus';
import http from 'http';

// Initialize Prometheus exporter
PrometheusManager.Initialize();

// Create an HTTP server with a /metrics endpoint.
// Bind to 127.0.0.1 (loopback) so only the local Prometheus scraper can reach it.
// Never expose /metrics on 0.0.0.0 without a separate authentication layer.
http.createServer(async (req, res) => {
	if (req.url === '/metrics') {
		try {
			const metrics = await PrometheusManager.GetMetrics();
			res.writeHead(200, { 'Content-Type': 'text/plain; version=0.0.4; charset=utf-8' });
			res.end(metrics);
		}
		catch (error) {
			res.writeHead(500);
			res.end('Failed to generate metrics');
		}
	}
}).listen(9090, '127.0.0.1');

console.log('Prometheus metrics available at http://127.0.0.1:9090/metrics');
NestJS Integration

For NestJS applications, import PrometheusModule from @pawells/nestjs-metrics:

import { Module } from '@nestjs/common';
import { PrometheusModule } from '@pawells/nestjs-metrics/prometheus';

@Module({
	imports: [PrometheusModule],
})
export class AppModule {}

// Endpoint automatically registered at GET /metrics
Recording Metrics

Use Instrumentation from @pawells/metrics to declare and record metrics:

import { Instrumentation } from '@pawells/metrics';

// Register a metric descriptor (must happen before recording)
Instrumentation.RegisterDescriptor({
	name: 'requests_total',
	type: 'counter',
	description: 'Total requests received',
	labelNames: ['method', 'route']
});

// Record a value (forwarded to all registered exporters including Prometheus)
Instrumentation.RecordMetric({
	descriptorName: 'requests_total',
	labels: { method: 'GET', route: '/health' },
	value: 1
});

Configuration

Configuration is managed via environment variables with the PROMETHEUS_ prefix. All variables use @pawells/config v3 with optional Zod validation. Secrets (marked SECRET below) are automatically redacted in logs.

Variable Type Default Description
PROMETHEUS_PULL_ENABLED boolean true Enable Prometheus pull mode (HTTP /metrics endpoint). When enabled, metrics are returned on-demand.
PROMETHEUS_PUSH_ENABLED boolean false Enable Prometheus push mode (Pushgateway). When enabled, metrics are periodically pushed to the gateway.
PROMETHEUS_PUSHGATEWAY_URL string (URL) '' URL of the Prometheus Pushgateway (e.g., http://localhost:9091). Required if PUSH_ENABLED=true. Must use http or https scheme.
PROMETHEUS_PUSHGATEWAY_JOB_NAME string os.hostname() Job label to use when pushing metrics to the Pushgateway. Defaults to the machine hostname.
PROMETHEUS_PUSHGATEWAY_INSTANCE string optional Instance label to use when pushing metrics (defaults to hostname if not set). Useful to distinguish multiple instances of the same job.
PROMETHEUS_PUSHGATEWAY_INTERVAL_MS number 5000 Interval (milliseconds) between pushes to the gateway. Minimum: 100ms.
PROMETHEUS_PUSHGATEWAY_USERNAME string optional Username for Basic authentication with the Pushgateway. SECRET — masked in logs.
PROMETHEUS_PUSHGATEWAY_PASSWORD string optional Password for Basic authentication with the Pushgateway. SECRET — masked in logs. Both username and password required for Basic auth.
PROMETHEUS_PUSHGATEWAY_TOKEN string optional Bearer token for Pushgateway authentication. SECRET — masked in logs. Takes precedence over Basic auth if both are set.
PROMETHEUS_PUSHGATEWAY_HEADERS string (JSON) '{}' Custom HTTP headers to include when pushing metrics. Must be a valid JSON object with string values. Headers are sent with every push request.

Cross-field validation:

  • If PROMETHEUS_PUSH_ENABLED=true, then PROMETHEUS_PUSHGATEWAY_URL must be set, otherwise initialization fails.
  • If PUSHGATEWAY_URL is set but PUSH_ENABLED=false, push mode is disabled.

Authorization priority (highest to lowest):

  1. Custom Authorization header in PUSHGATEWAY_HEADERS
  2. Bearer token (PUSHGATEWAY_TOKEN)
  3. Basic auth (username + password)

Example: Pull-only mode (default)

PROMETHEUS_PULL_ENABLED=true
PROMETHEUS_PUSH_ENABLED=false

Example: Push-mode with Basic auth

Caution: Basic auth encodes credentials as base64, not encryption. Use https:// for PUSHGATEWAY_URL in production to prevent cleartext credential exposure in transit.

PROMETHEUS_PULL_ENABLED=true
PROMETHEUS_PUSH_ENABLED=true
PROMETHEUS_PUSHGATEWAY_URL=http://pushgateway.local:9091
PROMETHEUS_PUSHGATEWAY_JOB_NAME=myapp
PROMETHEUS_PUSHGATEWAY_INTERVAL_MS=10000
PROMETHEUS_PUSHGATEWAY_USERNAME=admin
PROMETHEUS_PUSHGATEWAY_PASSWORD=secret123

Example: Push-mode with Bearer token

PROMETHEUS_PUSH_ENABLED=true
PROMETHEUS_PUSHGATEWAY_URL=http://pushgateway.local:9091
PROMETHEUS_PUSHGATEWAY_JOB_NAME=myapp
PROMETHEUS_PUSHGATEWAY_TOKEN=my-bearer-token-here
PROMETHEUS_PUSHGATEWAY_HEADERS='{"X-Custom-Header":"value"}'

API Reference

PrometheusManager

Static manager class that owns the Prometheus exporter lifecycle. All methods and properties are static. Called automatically by PrometheusModule.onModuleInit() in NestJS applications.

Static Methods:

  • Initialize(): void

    • Initializes the exporter and registers it with Instrumentation.
    • If PUSH_ENABLED=true, starts a periodic push interval.
    • Idempotent — subsequent calls are no-ops.
  • async Shutdown(): Promise<void>

    • Clears the periodic push interval (if active), deregisters the exporter from Instrumentation, awaits the exporter's own Shutdown(), clears the cached exporter reference, and marks the manager as uninitialized.
    • Idempotent — subsequent calls are no-ops.
  • GetMetrics(): Promise<string>

    • Flushes all pending metric values and returns them in Prometheus text format (version 0.0.4).
    • Throws: PrometheusError (PROMETHEUS_NOT_INITIALIZED) if the manager has not been initialized, PrometheusError (PROMETHEUS_EXPORTER_MISSING) if initialized but the internal exporter reference is unexpectedly missing, or PrometheusError (PROMETHEUS_METRICS_GENERATION_FAILED) if the underlying metrics generation fails.
    • Returns: Promise resolving to metrics string.
  • BuildAuthorizationHeaders(headersJson: string, username?: string, password?: string, token?: string): string

    • Builds authorization headers with proper priority ordering (token > basic auth > custom header).
    • Used internally to build the authorization headers for push requests.
    • Parameters:
      • headersJson — Raw headers JSON string from configuration
      • username — Optional username for Basic authentication
      • password — Optional password for Basic authentication
      • token — Optional bearer token
    • Returns: Processed headers JSON string with Authorization header injected (if applicable).

Static Properties:

  • Initialized: boolean (getter)
    • Returns true if Initialize() has been called and Shutdown() has not been called.
PrometheusExporter

Framework-agnostic exporter class that implements MetricsExporter from @pawells/metrics. Implements the buffering and flush model described below.

Help text: descriptor.description is used as the prom-client help string. When descriptor.unit is set it is appended in the form "<description> (unit: <unit>)".

Exemplar handling: value.exemplar is accepted but not emitted. prom-client exemplars require the OpenMetrics content type and use a different data model; the field is silently ignored.

Error contract: OnDescriptorRegistered and OnMetricRecorded throw PrometheusError on real failures rather than logging and dropping. The Instrumentation registry isolates each exporter and aggregates failures into MetricsExporterError.

Buffering model:

  • Metric values are buffered in memory as they are recorded (via OnMetricRecorded)
  • Pending values are flushed into prom-client instruments when GetMetrics() is called (atomic swap pattern)
  • Each metric has a max pending buffer of 1000 values; oldest entries are culled if exceeded
  • For updown counters, running totals are maintained across scrapes (persistent state); the 1000-entry buffer cap applies only to the Pending map — GaugeValues (running totals) are capped at 10,000 unique label combinations per metric, with the oldest label combination evicted and a warning logged on overflow

Type mapping:

Descriptor Type prom-client Instrument Behavior
counter Counter Monotonically increasing; values added via inc(labels, value)
gauge Gauge Point-in-time value; set via set(labels, value)
updown_counter Gauge Can increase or decrease; running total persisted across scrapes via internal map
histogram Histogram Distribution with buckets; values added via observe(labels, value)
summary Summary Server-side quantile distribution; values added via observe(labels, value); percentiles from descriptor.quantiles (prom-client defaults apply when absent)

Key Methods:

  • OnDescriptorRegistered(descriptor: TMetricDescriptor): void

    • Called by Instrumentation when a metric descriptor is registered.
    • Pre-creates the corresponding prom-client instrument (Counter, Gauge, Histogram, or Summary).
    • Initializes an empty pending buffer for the metric.
    • Re-registering an already-known descriptor is a no-op.
    • Throws PrometheusError if the name is empty, the type is unsupported, or prom-client instrument construction fails.
  • OnMetricRecorded(value: TMetricValue): void

    • Called by Instrumentation for every recorded metric value.
    • Appends the value to the pending buffer (max 1000 per metric).
    • Oldest entries are culled if the buffer is exceeded.
    • value.exemplar, when present, is accepted but not forwarded to prom-client (see Exemplar handling above).
    • Throws PrometheusError if the metric's descriptor was not previously registered with this exporter.
  • GetMetrics(): Promise<string>

    • Flushes all pending values into prom-client instruments.
    • Returns metrics in Prometheus text format (version 0.0.4).
    • Includes Node.js default metrics and all custom metrics.
  • Flush(): Promise<void>

    • No-op. Prometheus is pull-based: buffered values are materialised into prom-client instruments when the /metrics endpoint is scraped (or pushed by PrometheusManager when a Pushgateway is configured). This method exists to satisfy the MetricsExporter.Flush lifecycle contract and resolves immediately.
  • Shutdown(): Promise<void>

    • Disables all registered collectors, clears the registry, and releases internal state.
    • Called during application shutdown to prevent hanging (especially for event loop histogram).

Properties:

  • readonly Name = 'prometheus'
    • Stable identifier used by the Instrumentation registry for exporter deduplication and diagnostic tagging.
PrometheusError

Error class thrown when a Prometheus exporter operation fails. Extends BaseError from @pawells/typescript-common, aligning with the other exporter packages (@pawells/open-telemetry, @pawells/pyroscope).

export class PrometheusError extends BaseError<IPrometheusErrorMetadata> {
	static readonly Code: Readonly<Record<string, string>>; // frozen enum of every code thrown by this package
	get code(): string; // getter, not a stored field — delegates to BaseError.Code
	constructor(message: string, options?: { code?: string; cause?: Error });
}

Properties:

  • code — Machine-readable error code (a getter, not a plain field); delegates to BaseError.Code, defaulting to PrometheusError.Code.PROMETHEUS_ERROR when no explicit code was supplied
  • cause — Original error that caused this error, if any (propagated through the native Error cause chain)

Example:

try {
	const metrics = await PrometheusManager.GetMetrics();
}
catch (error) {
	if (error instanceof PrometheusError) {
		console.error(`Prometheus error: ${error.message} (code: ${error.code})`);
	}
}
Configuration Schema

Configuration values are accessed at runtime via the exported PrometheusConfig object. Use PrometheusConfig.Get(key) to read validated, environment-variable-backed values:

import { PrometheusConfig } from '@pawells/prometheus';

const url = PrometheusConfig.Get('PUSHGATEWAY_URL');
const enabled = PrometheusConfig.Get('PUSH_ENABLED');

See the Configuration section above for the complete list of variables, types, defaults, and descriptions.

License

MIT — Phillip Aaron Wells. See LICENSE for details.

Keywords