import {Aggregator, Registry} from 'prom-client';
import {Tags} from '@btilford/ts-base-core';
import * as os from 'os';
export type PromOptions = {
registry?: Registry[];
aggregator?: Aggregator;
help: string;
tags?: Tags;
/**
* `prom-client` requires configuring the tags that will be used before calling collection methods.
* You can provide names for tags that can't be set until the method invocation here.
*/
additionalTagNames?: string[];
prefix?: string;
suffix?: string;
}
function cleanMetricName(name: string): string {
const result = name.replace(/\./g, ':') // prometheus namespaces on `:` instead of `.`
.replace(/((?!:)\W)/g, '_') // convert anything left thats not a word to `_`
.replace(/(__+|::+)/g, '_') // convert >= double `__` or `::` to single
.replace(/(^[:_]|[:_]$)/g, ''); // trim leading trailing word separators
// use the regex from their site to verify
console.assert(result.match(/[:A-Z_a-z][\w:]*/), 'Invalid metric name! %s', result);
return result;
}
export class PromMetric {
readonly tags: Tags;
protected readonly options: PromOptions;
protected readonly supportedLabels: Set<string>;
readonly name: string;
constructor(
name: string,
tags: Tags,
options: PromOptions) {
this.options = { ...options };
this.tags = {
pid: `${process.pid}`,
NODE_ENV: process.env.NODE_ENV || 'not set',
host: os.hostname(),
os: os.type(),
platform: os.platform(),
...options.tags,
...tags
};
this.name = cleanMetricName([options.prefix, name, options.suffix].filter(part => part).join(':'));
this.supportedLabels = new Set([...(this.options.additionalTagNames || []), ...Object.keys(this.tags)])
}
filterTags(tags: Tags = {}): Tags {
const result: Tags = {};
for (const entry of Object.entries(tags)) {
if (this.supportedLabels.has(entry[0])) {
result[entry[0]] = entry[1];
}
}
return result;
}
}
|