Skip to content

Commit

Permalink
feat: add prefix to all injected metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
MoshePeret authored and willsoto committed Sep 10, 2023
1 parent 9cd6add commit 2375e42
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 16 deletions.
11 changes: 10 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export interface PrometheusDefaultMetrics {
* @public
*/
export interface PrometheusOptions {
/**
* Similar to `defaultMetrics.prefix`, this will be applied to each custom
* metric created using the various providers. Will suffix the given prefix
* with `_`.
*
* For example, given a metric name of "my_metric" and a prefix of "app"
* would create a metric name of `app_my_metric`
* */
customMetricPrefix?: string;
/**
* A custom controller to be used instead of the default one. Only needs to be
* provided if you need to do any kind of customization on the route, eg Swagger.
Expand Down Expand Up @@ -72,7 +81,7 @@ export interface PrometheusOptions {
}

export type PrometheusOptionsWithDefaults = Required<
Omit<PrometheusOptions, "pushgateway">
Omit<PrometheusOptions, "pushgateway" | "customMetricPrefix">
>;

/**
Expand Down
7 changes: 5 additions & 2 deletions src/metrics/counter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";

/**
Expand All @@ -10,8 +12,9 @@ export function makeCounterProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(): client.Metric<string> {
return getOrCreateMetric("Counter", options);
useFactory(config?: PrometheusOptions): client.Metric<string> {
return getOrCreateMetric("Counter", options, config);
},
inject: [PROMETHEUS_OPTIONS],
};
}
7 changes: 5 additions & 2 deletions src/metrics/gauge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";

/**
Expand All @@ -10,8 +12,9 @@ export function makeGaugeProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(): client.Metric<string> {
return getOrCreateMetric("Gauge", options);
useFactory(config?: PrometheusOptions): client.Metric<string> {
return getOrCreateMetric("Gauge", options, config);
},
inject: [PROMETHEUS_OPTIONS],
};
}
7 changes: 5 additions & 2 deletions src/metrics/histogram.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";

/**
Expand All @@ -10,8 +12,9 @@ export function makeHistogramProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(): client.Metric<string> {
return getOrCreateMetric("Histogram", options);
useFactory(config?: PrometheusOptions): client.Metric<string> {
return getOrCreateMetric("Histogram", options, config);
},
inject: [PROMETHEUS_OPTIONS],
};
}
7 changes: 5 additions & 2 deletions src/metrics/summary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";

/**
Expand All @@ -10,8 +12,9 @@ export function makeSummaryProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(): client.Metric<string> {
return getOrCreateMetric("Summary", options);
useFactory(config?: PrometheusOptions): client.Metric<string> {
return getOrCreateMetric("Summary", options, config);
},
inject: [PROMETHEUS_OPTIONS],
};
}
18 changes: 13 additions & 5 deletions src/metrics/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as client from "prom-client";
import { PrometheusOptions } from "../interfaces";

/**
* @internal
Expand All @@ -20,24 +21,31 @@ export type Options =
export function getOrCreateMetric(
type: Metrics,
options: Options,
prometheusOptions?: PrometheusOptions,
): client.Metric<string> {
const existingMetric = client.register.getSingleMetric(options.name);
const opts: Options = {
...options,
name: prometheusOptions?.customMetricPrefix
? prometheusOptions.customMetricPrefix.concat("_", options.name)
: options.name,
};

const existingMetric = client.register.getSingleMetric(opts.name);
if (existingMetric) {
return existingMetric;
}

switch (type) {
case "Gauge":
return new client.Gauge(options as client.GaugeConfiguration<string>);
return new client.Gauge(opts as client.GaugeConfiguration<string>);
case "Counter":
return new client.Counter(options as client.CounterConfiguration<string>);
return new client.Counter(opts as client.CounterConfiguration<string>);
case "Histogram":
return new client.Histogram(
options as client.HistogramConfiguration<string>,
opts as client.HistogramConfiguration<string>,
);
case "Summary":
return new client.Summary(options as client.SummaryConfiguration<string>);
return new client.Summary(opts as client.SummaryConfiguration<string>);
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Unknown type: ${type}`);
Expand Down
7 changes: 6 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ export class PrometheusModule {

PrometheusModule.configureServer(opts);

const providers: Provider[] = [];
const providers: Provider[] = [
{
provide: PROMETHEUS_OPTIONS,
useValue: options,
},
];
if (options?.pushgateway !== undefined) {
const { url, options: gatewayOptions, registry } = options.pushgateway;
providers.push({
Expand Down
13 changes: 12 additions & 1 deletion test/metrics/counter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { Injectable } from "@nestjs/common";
import { Test, TestingModule } from "@nestjs/testing";
import { expect } from "chai";
import * as client from "prom-client";
import { Counter, register } from "prom-client";
import {
Counter,
MetricObjectWithValues,
MetricValue,
register,
} from "prom-client";
import {
InjectMetric,
PrometheusModule,
Expand Down Expand Up @@ -56,4 +61,10 @@ describe("Counter", function () {

expect(service.counter).to.be.instanceOf(Counter);
});

it(`name has no prefix besides "controller_counter"`, async function () {
const metricValues: MetricObjectWithValues<MetricValue<string>> =
await metric.get();
expect(metricValues.name).to.equal("controller_counter");
});
});
15 changes: 15 additions & 0 deletions test/metrics/gauge.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Test, TestingModule } from "@nestjs/testing";
import { expect } from "chai";
import * as client from "prom-client";
import { MetricObjectWithValues, MetricValue } from "prom-client";
import { getToken, makeGaugeProvider } from "../../src";
import { PROMETHEUS_OPTIONS } from "../../src/constants";

describe("Gauge", function () {
let testingModule: TestingModule;
Expand All @@ -10,6 +12,12 @@ describe("Gauge", function () {
beforeEach(async function () {
testingModule = await Test.createTestingModule({
providers: [
{
provide: PROMETHEUS_OPTIONS,
useValue: {
customMetricPrefix: "app",
},
},
makeGaugeProvider({
name: "controller_gauge",
help: "controller_gauge_help",
Expand All @@ -35,4 +43,11 @@ describe("Gauge", function () {
it("has the appropriate methods (dec)", function () {
expect(metric.dec).to.be.a("function");
});

it("name has the prefix of APP", async function () {
const metricValues: MetricObjectWithValues<MetricValue<string>> =
await metric.get();

expect(metricValues.name).to.eq("app_controller_gauge");
});
});
15 changes: 15 additions & 0 deletions test/metrics/histogram.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Test, TestingModule } from "@nestjs/testing";
import { expect } from "chai";
import * as client from "prom-client";
import { MetricObjectWithValues, MetricValue } from "prom-client";
import { getToken, makeHistogramProvider } from "../../src";
import { PROMETHEUS_OPTIONS } from "../../src/constants";

describe("Histogram", function () {
let testingModule: TestingModule;
Expand All @@ -14,6 +16,12 @@ describe("Histogram", function () {
name: "controller_histogram",
help: "controller_histogram_help",
}),
{
provide: PROMETHEUS_OPTIONS,
useValue: {
customMetricPrefix: "app",
},
},
],
}).compile();

Expand All @@ -31,4 +39,11 @@ describe("Histogram", function () {
it("has the appropriate methods (observe)", function () {
expect(metric.observe).to.be.a("function");
});

it("name has the prefix of APP", async function () {
const metricValues: MetricObjectWithValues<MetricValue<string>> =
await metric.get();

expect(metricValues.name).to.eq("app_controller_histogram");
});
});
15 changes: 15 additions & 0 deletions test/metrics/summary.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Test, TestingModule } from "@nestjs/testing";
import { expect } from "chai";
import * as client from "prom-client";
import { MetricObjectWithValues, MetricValue } from "prom-client";
import { getToken, makeSummaryProvider } from "../../src";
import { PROMETHEUS_OPTIONS } from "../../src/constants";

describe("Summary", function () {
let testingModule: TestingModule;
Expand All @@ -14,6 +16,12 @@ describe("Summary", function () {
name: "controller_summary",
help: "controller_summary_help",
}),
{
provide: PROMETHEUS_OPTIONS,
useValue: {
customMetricPrefix: "app",
},
},
],
}).compile();

Expand All @@ -31,4 +39,11 @@ describe("Summary", function () {
it("has the appropriate methods (observe)", function () {
expect(metric.observe).to.be.a("function");
});

it("name has the prefix of APP", async function () {
const metricValues: MetricObjectWithValues<MetricValue<string>> =
await metric.get();

expect(metricValues.name).to.eq("app_controller_summary");
});
});

0 comments on commit 2375e42

Please sign in to comment.