Skip to content

Commit

Permalink
feat: Add PromService and some decorators
Browse files Browse the repository at this point in the history
Add PromService to be able to create metric on the fly
Add new decorators to auto generate metric for class instance and method
call
  • Loading branch information
spike008t committed Aug 21, 2019
1 parent e09c81a commit 563f2b2
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 7 deletions.
42 changes: 39 additions & 3 deletions lib/common/prom.decorators.ts
Original file line number Diff line number Diff line change
@@ -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) : '');
/**
* Create and increment a counter when the method is called
*
* @param param0
*/
export const PromMethodCounter = () => {
return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<Function>) => {
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 = <T extends { new(...args: any[]): {} }>(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);
}
}
};
86 changes: 86 additions & 0 deletions lib/common/prom.utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import * as client from 'prom-client';

export function getMetricToken(type: string, name: string) {
return `${name}${type}`;
}
Expand All @@ -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;
}
2 changes: 2 additions & 0 deletions lib/prom.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
9 changes: 7 additions & 2 deletions lib/prom.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -24,8 +25,12 @@ export class PromModule {
module: PromModule,
imports: [PromCoreModule.forRoot(options)],
controllers: [],
exports: [],
providers: [],
exports: [
PromService,
],
providers: [
PromService,
],
};

// default push default controller
Expand Down
21 changes: 21 additions & 0 deletions lib/prom.service.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
"node_modules",
"**/*.spec.ts"
]
}
}

0 comments on commit 563f2b2

Please sign in to comment.