Skip to content

Commit

Permalink
feat: support prom-client v15
Browse files Browse the repository at this point in the history
BREAKING CHANGE: dropped support for clients less than v15

Signed-off-by: Will Soto <willsoto@users.noreply.github.com>
  • Loading branch information
willsoto committed Oct 10, 2023
1 parent 3aace98 commit cd62351
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 47 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"prettier": "3.0.3",
"prettier-plugin-organize-imports": "3.2.3",
"prettier-plugin-packagejson": "2.4.6",
"prom-client": "14.2.0",
"prom-client": "15.0.0",
"reflect-metadata": "0.1.13",
"rimraf": "5.0.5",
"rxjs": "7.8.1",
Expand All @@ -130,7 +130,7 @@
},
"peerDependencies": {
"@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0",
"prom-client": "^13.0.0 || ^14.0.0"
"prom-client": "^15.0.0"
},
"packageManager": "pnpm@8.9.0",
"volta": {
Expand Down
16 changes: 11 additions & 5 deletions pnpm-lock.yaml

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

40 changes: 25 additions & 15 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Type } from "@nestjs/common";
import { ModuleMetadata } from "@nestjs/common/interfaces";
import * as client from "prom-client";
import { PrometheusContentType, RegistryContentType } from "prom-client";

/**
* Configuration for the defaultMetrics collected by `prom-client`.
*
* @public
*/
export interface PrometheusDefaultMetrics {
export interface PrometheusDefaultMetrics<
T extends RegistryContentType = PrometheusContentType,
> {
/**
* Whether or not default metrics are collected.
*
Expand All @@ -17,15 +20,17 @@ export interface PrometheusDefaultMetrics {
/**
* {@link https://github.com/siimon/prom-client#default-metrics | Default Metrics}
*/
config?: client.DefaultMetricsCollectorConfiguration;
config?: client.DefaultMetricsCollectorConfiguration<T>;
}

/**
* Options for the Prometheus Module.
*
* @public
*/
export interface PrometheusOptions {
export interface PrometheusOptions<
T extends RegistryContentType = PrometheusContentType,
> {
/**
* Make the module global when set to true
* */
Expand Down Expand Up @@ -68,7 +73,7 @@ export interface PrometheusOptions {
*/
path?: string;
/** {@inheritDoc PrometheusDefaultMetrics} */
defaultMetrics?: PrometheusDefaultMetrics;
defaultMetrics?: PrometheusDefaultMetrics<T>;
/**
* Will be passed into `setDefaultLabels`
*
Expand All @@ -84,34 +89,39 @@ export interface PrometheusOptions {
};
}

export type PrometheusOptionsWithDefaults = Required<
Omit<PrometheusOptions, "pushgateway" | "customMetricPrefix">
>;
export type PrometheusOptionsWithDefaults<
T extends RegistryContentType = PrometheusContentType,
> = Required<Omit<PrometheusOptions<T>, "pushgateway" | "customMetricPrefix">>;

/**
* @internal
*/
export interface PrometheusOptionsFactory {
createPrometheusOptions(): Promise<PrometheusOptions> | PrometheusOptions;
export interface PrometheusOptionsFactory<
T extends RegistryContentType = PrometheusContentType,
> {
createPrometheusOptions():
| Promise<PrometheusOptions<T>>
| PrometheusOptions<T>;
}

/**
* Options for configuring a dynamic provider
*
* @public
*/
export interface PrometheusAsyncOptions
extends Pick<ModuleMetadata, "imports"> {
export interface PrometheusAsyncOptions<
T extends RegistryContentType = PrometheusContentType,
> extends Pick<ModuleMetadata, "imports"> {
global?: boolean;

useExisting?: Type<PrometheusOptionsFactory>;
useClass?: Type<PrometheusOptionsFactory>;
useExisting?: Type<PrometheusOptionsFactory<T>>;
useClass?: Type<PrometheusOptionsFactory<T>>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inject?: any[];

/** {@inheritDoc PrometheusOptions.controller} */
controller?: PrometheusOptions["controller"];
controller?: PrometheusOptions<T>["controller"];
useFactory?(
...args: unknown[]
): Promise<PrometheusOptions> | PrometheusOptions;
): Promise<PrometheusOptions<T>> | PrometheusOptions<T>;
}
5 changes: 4 additions & 1 deletion src/metrics/counter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PrometheusContentType, RegistryContentType } from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";
Expand All @@ -12,7 +13,9 @@ export function makeCounterProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(config?: PrometheusOptions): client.Metric<string> {
useFactory<T extends RegistryContentType = PrometheusContentType>(
config?: PrometheusOptions<T>,
): client.Metric<string> {
return getOrCreateMetric("Counter", options, config);
},
inject: [
Expand Down
5 changes: 4 additions & 1 deletion src/metrics/gauge.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PrometheusContentType, RegistryContentType } from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";
Expand All @@ -12,7 +13,9 @@ export function makeGaugeProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(config?: PrometheusOptions): client.Metric<string> {
useFactory<T extends RegistryContentType = PrometheusContentType>(
config?: PrometheusOptions<T>,
): client.Metric<string> {
return getOrCreateMetric("Gauge", options, config);
},
inject: [
Expand Down
5 changes: 4 additions & 1 deletion src/metrics/histogram.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PrometheusContentType, RegistryContentType } from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";
Expand All @@ -12,7 +13,9 @@ export function makeHistogramProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(config?: PrometheusOptions): client.Metric<string> {
useFactory<T extends RegistryContentType = PrometheusContentType>(
config?: PrometheusOptions<T>,
): client.Metric<string> {
return getOrCreateMetric("Histogram", options, config);
},
inject: [
Expand Down
5 changes: 4 additions & 1 deletion src/metrics/summary.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Provider } from "@nestjs/common";
import * as client from "prom-client";
import { PrometheusContentType, RegistryContentType } from "prom-client";
import { PROMETHEUS_OPTIONS } from "../constants";
import { PrometheusOptions } from "../interfaces";
import { getOrCreateMetric, getToken } from "./utils";
Expand All @@ -12,7 +13,9 @@ export function makeSummaryProvider(
): Provider {
return {
provide: getToken(options.name),
useFactory(config?: PrometheusOptions): client.Metric<string> {
useFactory<T extends RegistryContentType = PrometheusContentType>(
config?: PrometheusOptions<T>,
): client.Metric<string> {
return getOrCreateMetric("Summary", options, config);
},
inject: [
Expand Down
7 changes: 5 additions & 2 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 { PrometheusContentType, RegistryContentType } from "prom-client";
import { PrometheusOptions } from "../interfaces";

/**
Expand All @@ -18,10 +19,12 @@ export type Options =
/**
* @internal
*/
export function getOrCreateMetric(
export function getOrCreateMetric<
T extends RegistryContentType = PrometheusContentType,
>(
type: Metrics,
options: Options,
prometheusOptions?: PrometheusOptions,
prometheusOptions?: PrometheusOptions<T>,
): client.Metric<string> {
const opts: Options = {
...options,
Expand Down
43 changes: 26 additions & 17 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Provider,
} from "@nestjs/common";
import * as promClient from "prom-client";
import { RegistryContentType } from "prom-client";
import { PROMETHEUS_OPTIONS, PROM_CLIENT } from "./constants";
import { PrometheusController } from "./controller";
import {
Expand All @@ -21,7 +22,9 @@ import {
*/
@Module({})
export class PrometheusModule {
public static register(options?: PrometheusOptions): DynamicModule {
public static register<T extends RegistryContentType>(
options?: PrometheusOptions<T>,
): DynamicModule {
const opts = PrometheusModule.makeDefaultOptions(options);

PrometheusModule.configureServer(opts);
Expand Down Expand Up @@ -53,7 +56,9 @@ export class PrometheusModule {
};
}

public static registerAsync(options: PrometheusAsyncOptions): DynamicModule {
public static registerAsync<T extends RegistryContentType>(
options: PrometheusAsyncOptions<T>,
): DynamicModule {
const providers = this.createAsyncProviders(options);
const controller = options.controller ?? PrometheusController;

Expand All @@ -67,7 +72,9 @@ export class PrometheusModule {
{
provide: PROM_CLIENT,
inject: [PROMETHEUS_OPTIONS],
useFactory(userOptions: PrometheusOptions) {
useFactory<T extends RegistryContentType>(
userOptions: PrometheusOptions<T>,
) {
const opts = PrometheusModule.makeDefaultOptions(userOptions);

PrometheusModule.configureServer(opts);
Expand All @@ -80,8 +87,8 @@ export class PrometheusModule {
};
}

public static createAsyncProviders(
options: PrometheusAsyncOptions,
public static createAsyncProviders<T extends RegistryContentType>(
options: PrometheusAsyncOptions<T>,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [
Expand All @@ -104,8 +111,8 @@ export class PrometheusModule {
];
}

public static createAsyncOptionsProvider(
options: PrometheusAsyncOptions,
public static createAsyncOptionsProvider<T extends RegistryContentType>(
options: PrometheusAsyncOptions<T>,
): Provider {
if (options.useFactory) {
return {
Expand All @@ -126,16 +133,18 @@ export class PrometheusModule {

return {
provide: PROMETHEUS_OPTIONS,
async useFactory(
optionsFactory: PrometheusOptionsFactory,
): Promise<PrometheusOptions> {
async useFactory<T extends RegistryContentType>(
optionsFactory: PrometheusOptionsFactory<T>,
): Promise<PrometheusOptions<T>> {
return optionsFactory.createPrometheusOptions();
},
inject: [inject],
};
}

private static configureServer(options: PrometheusOptionsWithDefaults): void {
private static configureServer<T extends RegistryContentType>(
options: PrometheusOptionsWithDefaults<T>,
): void {
if (options.defaultMetrics.enabled) {
promClient.collectDefaultMetrics(options.defaultMetrics.config);
}
Expand All @@ -147,19 +156,19 @@ export class PrometheusModule {
Reflect.defineMetadata("path", options.path, options.controller);
}

private static configurePushgateway(
private static configurePushgateway<T extends RegistryContentType>(
url: string,
options?: unknown,
registry?: promClient.Registry,
): promClient.Pushgateway {
): promClient.Pushgateway<T> {
return new promClient.Pushgateway(url, options, registry);
}

private static createPushgatewayProvider(): FactoryProvider {
return {
provide: promClient.Pushgateway,
inject: [PROMETHEUS_OPTIONS],
useFactory(options: PrometheusOptions) {
useFactory<T extends RegistryContentType>(options: PrometheusOptions<T>) {
if (options?.pushgateway !== undefined) {
const {
url,
Expand All @@ -179,9 +188,9 @@ export class PrometheusModule {
};
}

private static makeDefaultOptions(
options?: PrometheusOptions,
): PrometheusOptionsWithDefaults {
private static makeDefaultOptions<T extends RegistryContentType>(
options?: PrometheusOptions<T>,
): PrometheusOptionsWithDefaults<T> {
return {
global: false,
path: "/metrics",
Expand Down
6 changes: 4 additions & 2 deletions test/push-gateway.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable, Module } from "@nestjs/common";
import { Test } from "@nestjs/testing";
import { expect } from "chai";
import { Pushgateway, register } from "prom-client";
import { PrometheusContentType, Pushgateway, register } from "prom-client";
import {
PrometheusModule,
PrometheusOptions,
Expand All @@ -23,7 +23,9 @@ describe("Pushgateway", function () {

@Injectable()
class MockService {
constructor(public readonly pushgateway: Pushgateway) {}
constructor(
public readonly pushgateway: Pushgateway<PrometheusContentType>,
) {}
}

@Module({
Expand Down

0 comments on commit cd62351

Please sign in to comment.