UNPKG

@sourceregistry/node-prometheus

Version:

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

307 lines (306 loc) 8.7 kB
class n { /** * Creates a new Metric instance. * * @param type - The Prometheus metric type. * @param name - The raw metric name (will be cleaned). * @param description - Optional description for # HELP. * @param labels - Optional default labels. */ constructor(t, e, r, s) { this.type = t, this.name = n.cleanText(e) || "", this.description = r, this.labels = s; } /** * Converts a label object to a Prometheus label string (e.g., `{foo="bar"}`). * * @param labels - The label key-value pairs. * @returns The formatted label string. */ static labelString(t) { return !t || Object.keys(t).length === 0 ? "" : `{${Object.entries(t).map(([e, r]) => `${e}="${String(r)}"`).join(",")}}`; } /** * Sanitizes a metric name by removing or replacing invalid characters. * * Prometheus metric names must match [a-zA-Z_:][a-zA-Z0-9_:]* * This removes hyphens, parens; replaces slashes and spaces with underscores. * * @param input - The raw metric name. * @returns The cleaned metric name. */ static cleanText(t) { return t?.replaceAll("-", "").replaceAll("/", "_").replaceAll(" ", "_").replaceAll("(", "").replaceAll(")", ""); } /** * Concatenates multiple metrics into a single exposition string. * * @param format - used to set the metric type * @param metrics - The metrics to serialize. * @returns A Promise resolving to the combined string. */ static async concat(t = "prometheus", ...e) { let s = (await Promise.all(e.map((u) => u.stringify()))).join(` `); return t === "openmetrics" && (s = s.trimEnd() + ` # EOF`), s; } /** * Generates the common header lines (# HELP, # TYPE) for this metric. * Called by subclasses before serializing their specific values. * * @returns The header string. */ generateHeader() { let t = ""; return this.name && this.description && (t += `# HELP ${this.name} ${this.description} `), this.type !== "untyped" && (t += `# TYPE ${this.name} ${this.type} `), t; } } class a extends n { /** * Creates a new ValueMetric. * * @param type - The metric type. * @param config - Configuration object. * @param config.name - Metric name. * @param config.description - Optional description. * @param config.labels - Optional default labels. * @param config.reader - Async or sync function returning values. */ constructor(t, e) { super(t, e.name, e.description, e.labels), this._reader = async () => (await e.reader()).map((s) => { if (typeof s == "number") return [s, {}, Date.now()]; if (Array.isArray(s)) return s.length === 2 ? typeof s[1] == "number" ? [s[0], {}, s[1]] : [s[0], s[1], Date.now()] : s; throw new Error(`Unexpected value format: ${JSON.stringify(s)}`); }); } /** * Serializes the current values with labels and timestamps. * * @returns Promise resolving to value lines. */ async valueString() { return (await this._reader()).map( ([e, r, s]) => `${this.name}${n.labelString({ ...this.labels, ...r })} ${e} ${s} ` ).join(""); } /** * @inheritdoc */ async stringify() { return `${super.generateHeader()}${await this.valueString()} `; } } class l extends a { /** * Creates a new Gauge. * * @param config - Configuration object. */ constructor(t) { super("gauge", t); } } class c extends a { /** * Creates a new Counter. * * @param config - Configuration object. */ constructor(t) { super("counter", t); } /** * Increments the counter by a given value (convenience alias). * Note: Since Counter uses a reader, this is just documentation — actual increment * must be handled in the reader function or external state. * * @param delta - Amount to increment by (default: 1). */ inc(t = 1) { console.warn( `Counter.inc() called, but Counter uses reader function. Increment must be handled externally. Delta: ${t}` ); } } class h extends n { /** * Creates a new Histogram. * * @param config - Configuration object. * @param config.name - Metric name. * @param config.description - Optional description. * @param config.buckets - Optional bucket thresholds (default: []). +Inf always added. * @param config.labels - Optional default labels. */ constructor(t) { super("histogram", t.name, t.description, t.labels), this._bucketCounts = {}, this._sum = 0, this._count = 0; const e = (t.buckets ?? []).sort((r, s) => r - s); this._buckets = [...e, 1 / 0], this.reset(); } /** * Resets all bucket counts, sum, and total count to zero. */ reset() { this._buckets.forEach((t) => this._bucketCounts[t] = 0), this._sum = 0, this._count = 0; } /** * Observes a value, updating buckets, sum, and count. * * @param value - The observed value. */ observe(t) { if (typeof t != "number" || isNaN(t)) throw new Error(`Invalid histogram value: ${t}`); this.push(t); } /** * Alias for observe(). * * @param value - The observed value. */ push(t) { this._sum += t, this._count++; for (const e of this._buckets) t <= e && this._bucketCounts[e]++; } /** * Serializes bucket, sum, and count lines. * * @returns The serialized string. */ bucketString() { return `${this._buckets.map((e) => { const r = e === 1 / 0 ? "+Inf" : e; return `${this.name}_bucket${n.labelString({ ...this.labels, le: r })} ${this._bucketCounts[e]}`; }).join(` `)} ${this.name}_sum ${this._sum} ${this.name}_count ${this._count}`; } /** * @inheritdoc */ async stringify() { return `${super.generateHeader()}${this.bucketString()} `; } } class o extends n { /** * Creates a new Summary. * * @param config - Configuration object. * @param config.name - Metric name. * @param config.description - Optional description. * @param config.quantiles - Array of φ-quantiles (0 < φ < 1). * @param config.calculate - Function to calculate quantile estimate given value and φ. */ constructor(t) { if (super("summary", t.name, t.description), this._sum = 0, this._count = 0, !t.quantiles.length) throw new Error("Summary must have at least one quantile"); this._quantiles = new Map(t.quantiles.map((e) => [e, 0])), this._calculate = t.calculate; } /** * Observes a value, updating quantiles, sum, and count. * * @param value - The observed value. */ observe(t) { if (typeof t != "number" || isNaN(t)) throw new Error(`Invalid summary value: ${t}`); this.push(t); } /** * Alias for observe(). * * @param value - The observed value. */ push(t) { this._sum += t, this._count++, this._quantiles.forEach((e, r) => { const s = this._calculate(t, r); this._quantiles.set(r, s); }); } /** * Serializes quantile, sum, and count lines. * * @returns The serialized string. */ summaryString() { return `${[...this._quantiles.entries()].sort(([e], [r]) => e - r).map( ([e, r]) => `${this.name}${n.labelString({ ...this.labels, quantile: e })} ${r}` ).join(` `)} ${this.name}_sum ${this._sum} ${this.name}_count ${this._count}`; } /** * @inheritdoc */ async stringify() { return `${super.generateHeader()}${this.summaryString()} `; } } class m extends n { // [value, timestamp] /** * Creates a new Untyped metric. * * @param config - Configuration object. * @param config.name - Metric name. * @param config.description - Optional description. * @param config.labels - Optional default labels. * @param config.value - Initial value or [value, timestamp] tuple. */ constructor(t) { super("untyped", t.name, t.description, t.labels), this._value = Array.isArray(t.value) ? t.value : [t.value ?? 0, Date.now()]; } /** * Sets the current value and timestamp. * * @param value - New value or [value, timestamp] tuple. */ set(t) { this._value = Array.isArray(t) ? t : [t, Date.now()]; } /** * Gets the current value and timestamp. * * @returns [value, timestamp] tuple. */ get() { return this._value; } /** * Serializes the current value. * * @returns The serialized string. */ valueString() { return `${this.name}${n.labelString(this.labels)} ${this._value[0]} ${this._value[1]} `; } /** * @inheritdoc */ async stringify() { return `${super.generateHeader()}${this.valueString()} `; } } export { c as Counter, l as Gauge, h as Histogram, n as Metric, o as Summary, m as Untyped }; //# sourceMappingURL=index.es.js.map