Skip to content

Commit

Permalink
feat(pushgateway): add support for Pushgateway
Browse files Browse the repository at this point in the history
Closes #1628

Signed-off-by: Will Soto <willsoto@users.noreply.github.com>
  • Loading branch information
willsoto committed Jan 8, 2023
1 parent 65d2544 commit f797636
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 6 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Histogram](#histogram)
- [Summary](#summary)
- [Providing a custom controller](#providing-a-custom-controller)
- [Pushgateway](#pushgateway)

<!-- tocstop -->

Expand Down Expand Up @@ -215,3 +216,37 @@ import { MyCustomController } from "./my-custom-controller";
})
export class AppModule {}
```

## Pushgateway

In order to enable Pushgateway for injection, provide the configuration under the `pushgateway` key.

```typescript
import { Module } from "@nestjs/common";
import { PrometheusModule } from "@willsoto/nestjs-prometheus";

@Module({
imports: [
PrometheusModule.register({
pushgateway: {
url: "http://127.0.0.1:9091",
},
}),
],
})
export class AppModule {}
```

```typescript
import { Injectable, Inject } from "@nestjs/common";
import * as client from "prom-client";
import {
PrometheusModule,
PROMETHEUS_PUSH_GATEWAY,
} from "@willsoto/nestjs-prometheus";

@Injectable()
export class Service {
constructor(private readonly pushgateway: client.Pushgateway) {}
}
```
9 changes: 9 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,17 @@ export interface PrometheusOptions {
// Using this type to match what prom-client specifies.
// eslint-disable-next-line @typescript-eslint/ban-types
defaultLabels?: Object;
pushgateway?: {
url: string;
options?: unknown;
registry?: client.Registry;
};
}

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

/**
* @internal
*/
Expand Down
65 changes: 61 additions & 4 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { DynamicModule, Module, Provider } from "@nestjs/common";
import {
DynamicModule,
FactoryProvider,
Module,
Provider,
} from "@nestjs/common";
import * as promClient from "prom-client";
import { PROMETHEUS_OPTIONS } from "./constants";
import { PrometheusController } from "./controller";
import {
PrometheusAsyncOptions,
PrometheusOptions,
PrometheusOptionsFactory,
PrometheusOptionsWithDefaults,
} from "./interfaces";

/**
Expand All @@ -20,8 +26,23 @@ export class PrometheusModule {

PrometheusModule.configureServer(opts);

const providers: Provider[] = [];
if (options?.pushgateway !== undefined) {
const { url, options: gatewayOptions, registry } = options.pushgateway;

providers.push({
provide: promClient.Pushgateway,
useValue: PrometheusModule.configurePushgateway(
url,
gatewayOptions,
registry,
),
});
}

return {
module: PrometheusModule,
providers,
controllers: [opts.controller],
};
}
Expand All @@ -42,7 +63,10 @@ export class PrometheusModule {
options: PrometheusAsyncOptions,
): Provider[] {
if (options.useExisting) {
return [this.createAsyncOptionsProvider(options)];
return [
this.createAsyncOptionsProvider(options),
PrometheusModule.createPushgatewayProvider(),
];
} else if (!options.useClass) {
throw new Error(
"Invalid configuration. Must provide useClass or useExisting",
Expand All @@ -55,6 +79,7 @@ export class PrometheusModule {
provide: options.useClass,
useClass: options.useClass,
},
PrometheusModule.createPushgatewayProvider(),
];
}

Expand Down Expand Up @@ -97,7 +122,7 @@ export class PrometheusModule {
};
}

private static configureServer(options: Required<PrometheusOptions>): void {
private static configureServer(options: PrometheusOptionsWithDefaults): void {
if (options.defaultMetrics.enabled) {
promClient.collectDefaultMetrics(options.defaultMetrics.config);
}
Expand All @@ -109,9 +134,41 @@ export class PrometheusModule {
Reflect.defineMetadata("path", options.path, options.controller);
}

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

private static createPushgatewayProvider(): FactoryProvider {
return {
provide: promClient.Pushgateway,
inject: [PROMETHEUS_OPTIONS],
useFactory(options: PrometheusOptions) {
if (options?.pushgateway !== undefined) {
const {
url,
options: gatewayOptions,
registry,
} = options.pushgateway;

return PrometheusModule.configurePushgateway(
url,
gatewayOptions,
registry,
);
}

return null;
},
};
}

private static makeDefaultOptions(
options?: PrometheusOptions,
): Required<PrometheusOptions> {
): PrometheusOptionsWithDefaults {
return {
path: "/metrics",
defaultMetrics: {
Expand Down
4 changes: 2 additions & 2 deletions test/module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("PrometheusModule", function () {
}
});

describe("#forRoot", function () {
describe("#register", function () {
describe("with all defaults", function () {
beforeEach(async function () {
({ agent, app } = await createPrometheusModule());
Expand Down Expand Up @@ -70,7 +70,7 @@ describe("PrometheusModule", function () {
});
});

describe("#forRootAsync", function () {
describe("#registerAsync", function () {
@Injectable()
class OptionsService implements PrometheusOptionsFactory {
createPrometheusOptions() {
Expand Down
54 changes: 54 additions & 0 deletions test/push-gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Injectable, Module } from "@nestjs/common";
import { expect } from "chai";
import { Pushgateway, register } from "prom-client";
import { PrometheusOptions, PrometheusOptionsFactory } from "../src";
import { createAsyncPrometheusModule, createPrometheusModule } from "./utils";

describe("Pushgateway", function () {
@Injectable()
class OptionsService implements PrometheusOptionsFactory {
createPrometheusOptions(): PrometheusOptions | Promise<PrometheusOptions> {
return {
pushgateway: {
url: "http://127.0.0.1:9091",
},
};
}
}

@Module({
providers: [OptionsService],
exports: [OptionsService],
})
class OptionsModule {}

afterEach(function () {
register.clear();
});

it("should register a pushgateway if options are provided (sync)", async function () {
const { testingModule, app } = await createPrometheusModule({
pushgateway: {
url: "http://127.0.0.1:9091",
},
});
const gateway = testingModule.get(Pushgateway);

expect(gateway).to.be.instanceOf(Pushgateway);

await app.close();
});

it("should register a pushgateway if options are provided (async)", async function () {
const { testingModule, app } = await createAsyncPrometheusModule({
imports: [OptionsModule],
useExisting: OptionsService,
inject: [OptionsService],
});
const gateway = testingModule.get(Pushgateway);

expect(gateway).to.be.instanceOf(Pushgateway);

await app.close();
});
});

0 comments on commit f797636

Please sign in to comment.