diff --git a/lib/common/prom.decorators.ts b/lib/common/prom.decorators.ts index 940d265..9b4cda8 100644 --- a/lib/common/prom.decorators.ts +++ b/lib/common/prom.decorators.ts @@ -1,10 +1,46 @@ import { Inject } from '@nestjs/common'; -import { getMetricToken } from './prom.utils'; +import { getMetricToken, findOrCreateCounter } from './prom.utils'; +import * as client from 'prom-client'; export const InjectCounterMetric = (name: string) => Inject(getMetricToken(`Counter`, name)); export const InjectGaugeMetric = (name: string) => Inject(getMetricToken(`Gauge`, name)); export const InjectHistogramMetric = (name: string) => Inject(getMetricToken(`Histogram`, name)); export const InjectSummaryMetric = (name: string) => Inject(getMetricToken(`Summary`, name)); -// export const InjectRegistry = (name?: string) => -// Inject(name ? getRegistryName(name) : ''); \ No newline at end of file +/** + * Create and increment a counter when the method is called + * + * @param param0 + */ +export const PromMethodCounter = () => { + return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => { + const className = target.constructor.name; + const counterMetric = findOrCreateCounter({ + name: `${className}_${propertyKey.toString()}_method_counter`, + help: `${className}#${propertyKey.toString()} called counter`, + }); + const methodFunc = descriptor.value; + descriptor.value = function (...args) { + counterMetric.inc(1); + return methodFunc.apply(this, args); + } + }; +} + +/** + * Create and increment a counter when a new instance is created + * + * @param ctor + */ +export const PromInstanceCounter = (ctor: T) => { + const counterMetric = findOrCreateCounter({ + name: `${ctor.name}_instance_counter`, + help: `${ctor.name} object instances counter`, + }); + return class extends ctor { + constructor(...args) { + counterMetric.inc(1); + super(...args); + } + } +}; \ No newline at end of file diff --git a/lib/common/prom.utils.ts b/lib/common/prom.utils.ts index c2482d3..cc3236b 100644 --- a/lib/common/prom.utils.ts +++ b/lib/common/prom.utils.ts @@ -1,4 +1,6 @@ +import * as client from 'prom-client'; + export function getMetricToken(type: string, name: string) { return `${name}${type}`; } @@ -10,3 +12,87 @@ export function getRegistryName(name: string) { export function getOptionsName(name: string) { return `${name}PromOptions`; } + +export const findOrCreateMetric = ({ + name, + type, + help, +}: { + name: string; + type: string; + help?: string; +}): client.Metric => { + + let metric: client.Metric = client.register.getSingleMetric(name); + if (!metric) { + return new client.Counter({ + name: name, + help: help || `${name} ${type}`, + }); + } + + if (metric instanceof client.Counter === false) { + return new client.Counter({ + name: getMetricToken(type, name), + help: help || `${name} ${type}`, + }); + } + + return metric; +} + +export const findOrCreateCounter = ({ + name, + help, +}: { + name: string, + help?: string, +}): client.Counter => { + return findOrCreateMetric({ + name, + help, + type: `Counter`, + }) as client.Counter; +} + +export const findOrCreateGauge = ({ + name, + help, +}: { + name: string, + help?: string, +}): client.Gauge => { + return findOrCreateMetric({ + name, + help, + type: `Gauge`, + }) as client.Gauge; +} + +export const findOrCreateHistogram = ({ + name, + help, +}: { + name: string, + help?: string, +}): client.Histogram => { + return findOrCreateMetric({ + name, + help, + type: `Histogram`, + }) as client.Histogram; +} + +export const findOrCreateSummary = ({ + name, + help, +}: { + name: string, + help?: string, +}): client.Summary => { + return findOrCreateMetric({ + name, + help, + type: `Summary`, + }) as client.Summary; +} diff --git a/lib/prom.constants.ts b/lib/prom.constants.ts index 10fe522..5c28948 100644 --- a/lib/prom.constants.ts +++ b/lib/prom.constants.ts @@ -2,3 +2,5 @@ export const DEFAULT_PROM_REGISTRY = 'PromRegistry'; export const DEFAULT_PROM_OPTIONS = 'PromOptions'; export const PROM_REGISTRY_NAME = 'PromRegistryName'; + +export const PROM_COUNTER_METRIC_AUTO_INC = 'PROM_COUNTER_METRIC_AUTO_INC'; diff --git a/lib/prom.module.ts b/lib/prom.module.ts index 1f84d08..9758082 100644 --- a/lib/prom.module.ts +++ b/lib/prom.module.ts @@ -6,6 +6,7 @@ import * as client from 'prom-client'; import { PromController } from './prom.controller'; import { InboundMiddleware } from './middleware/inbound.middleware'; import { DEFAULT_PROM_OPTIONS } from './prom.constants'; +import { PromService } from './prom.service'; @Module({}) export class PromModule { @@ -24,8 +25,12 @@ export class PromModule { module: PromModule, imports: [PromCoreModule.forRoot(options)], controllers: [], - exports: [], - providers: [], + exports: [ + PromService, + ], + providers: [ + PromService, + ], }; // default push default controller diff --git a/lib/prom.service.ts b/lib/prom.service.ts new file mode 100644 index 0000000..fa5f836 --- /dev/null +++ b/lib/prom.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { findOrCreateCounter, findOrCreateGauge, findOrCreateHistogram } from './common/prom.utils'; + +@Injectable() +export class PromService { + getCounterMetric(name: string) { + return findOrCreateCounter({ name }); + } + + getGaugeMetric(name: string) { + return findOrCreateGauge({ name }); + } + + getHistogramMetric(name: string) { + return findOrCreateHistogram({ name }); + } + + getSummaryMetric(name: string) { + return findOrCreateHistogram({ name }); + } +} diff --git a/package-lock.json b/package-lock.json index 132d6e7..06e84c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@digikare/nestjs-prom", - "version": "0.0.1", + "version": "0.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/tsconfig.json b/tsconfig.json index bc13615..c40afb9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,4 +20,4 @@ "node_modules", "**/*.spec.ts" ] -} \ No newline at end of file +}