From 95ec6a52d485a6e53f8b714cc9734645a04c7230 Mon Sep 17 00:00:00 2001 From: Thomas Kortyka Date: Mon, 26 Aug 2024 17:07:45 +0200 Subject: [PATCH] feat: add health, metrics and version --- .../src/app/app.controller.spec.ts | 22 ---------- .../cronjob-service/src/app/app.controller.ts | 13 ------ apps/cronjob-service/src/app/app.module.ts | 16 ++++--- .../src/app/app.service.spec.ts | 21 ---------- apps/cronjob-service/src/app/app.service.ts | 8 ---- .../src/app/health/health.controller.ts | 39 +++++++++++++++++ .../src/app/health/health.module.ts | 42 +++++++++++++++++++ .../src/app/metrics/metrics.module.ts | 17 ++++++++ .../src/app/version/version.controller.ts | 14 +++++++ .../src/app/version/version.module.ts | 19 +++++++++ .../src/app/version/version.service.ts | 10 +++++ package-lock.json | 8 ++-- package.json | 2 +- 13 files changed, 156 insertions(+), 75 deletions(-) delete mode 100644 apps/cronjob-service/src/app/app.controller.spec.ts delete mode 100644 apps/cronjob-service/src/app/app.controller.ts delete mode 100644 apps/cronjob-service/src/app/app.service.spec.ts delete mode 100644 apps/cronjob-service/src/app/app.service.ts create mode 100644 apps/cronjob-service/src/app/health/health.controller.ts create mode 100644 apps/cronjob-service/src/app/health/health.module.ts create mode 100644 apps/cronjob-service/src/app/metrics/metrics.module.ts create mode 100644 apps/cronjob-service/src/app/version/version.controller.ts create mode 100644 apps/cronjob-service/src/app/version/version.module.ts create mode 100644 apps/cronjob-service/src/app/version/version.service.ts diff --git a/apps/cronjob-service/src/app/app.controller.spec.ts b/apps/cronjob-service/src/app/app.controller.spec.ts deleted file mode 100644 index 431e71c6..00000000 --- a/apps/cronjob-service/src/app/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; - -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let app: TestingModule; - - beforeAll(async () => { - app = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - }); - - describe('getData', () => { - it('should return "Hello API"', () => { - const appController = app.get(AppController); - expect(appController.getData()).toEqual({ message: 'Hello API' }); - }); - }); -}); diff --git a/apps/cronjob-service/src/app/app.controller.ts b/apps/cronjob-service/src/app/app.controller.ts deleted file mode 100644 index 3752bda9..00000000 --- a/apps/cronjob-service/src/app/app.controller.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; - -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getData() { - return this.appService.getData(); - } -} diff --git a/apps/cronjob-service/src/app/app.module.ts b/apps/cronjob-service/src/app/app.module.ts index 078b178f..b5ea29c3 100644 --- a/apps/cronjob-service/src/app/app.module.ts +++ b/apps/cronjob-service/src/app/app.module.ts @@ -1,11 +1,15 @@ import { Module } from '@nestjs/common'; - -import { AppController } from './app.controller'; -import { AppService } from './app.service'; +import { ConfigModule } from '@nestjs/config'; +import { HealthModule } from './health/health.module'; +import { MetricsModule } from './metrics/metrics.module'; +import { VersionModule } from './version/version.module'; @Module({ - imports: [], - controllers: [AppController], - providers: [AppService], + imports: [ + ConfigModule.forRoot({ isGlobal: true }), + HealthModule, + MetricsModule, + VersionModule, + ], }) export class AppModule {} diff --git a/apps/cronjob-service/src/app/app.service.spec.ts b/apps/cronjob-service/src/app/app.service.spec.ts deleted file mode 100644 index 741d180f..00000000 --- a/apps/cronjob-service/src/app/app.service.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Test } from '@nestjs/testing'; - -import { AppService } from './app.service'; - -describe('AppService', () => { - let service: AppService; - - beforeAll(async () => { - const app = await Test.createTestingModule({ - providers: [AppService], - }).compile(); - - service = app.get(AppService); - }); - - describe('getData', () => { - it('should return "Hello API"', () => { - expect(service.getData()).toEqual({ message: 'Hello API' }); - }); - }); -}); diff --git a/apps/cronjob-service/src/app/app.service.ts b/apps/cronjob-service/src/app/app.service.ts deleted file mode 100644 index 33933b02..00000000 --- a/apps/cronjob-service/src/app/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AppService { - getData(): { message: string } { - return { message: 'Hello API' }; - } -} diff --git a/apps/cronjob-service/src/app/health/health.controller.ts b/apps/cronjob-service/src/app/health/health.controller.ts new file mode 100644 index 00000000..fac10a35 --- /dev/null +++ b/apps/cronjob-service/src/app/health/health.controller.ts @@ -0,0 +1,39 @@ +import { Controller, Get, Inject } from '@nestjs/common'; +import { + HealthCheck, + HealthCheckService, + MemoryHealthIndicator, + MicroserviceHealthIndicator, +} from '@nestjs/terminus'; +import { Transport } from '@nestjs/microservices'; +import { azkaban_cronjob } from '@toxictoast/azkaban-broker-rabbitmq'; + +@Controller('health') +export class HealthController { + constructor( + @Inject('MEMORY_HEAP_TRESHOLD') private readonly heapTreshold: number, + @Inject('MEMORY_RSS_TRESHOLD') private readonly rssTreshold: number, + @Inject('BROKER_CONNECTION_STRING') + private readonly brokerConnectionString: string, + private readonly service: HealthCheckService, + private readonly memory: MemoryHealthIndicator, + private readonly microservices: MicroserviceHealthIndicator, + ) {} + + @Get() + @HealthCheck() + check() { + return this.service.check([ + () => this.memory.checkHeap('memory_heap', this.heapTreshold), + () => this.memory.checkRSS('memory_rss', this.rssTreshold), + () => + this.microservices.pingCheck('rabbitmq', { + transport: Transport.RMQ, + options: { + urls: [this.brokerConnectionString], + queue: azkaban_cronjob, + }, + }), + ]); + } +} diff --git a/apps/cronjob-service/src/app/health/health.module.ts b/apps/cronjob-service/src/app/health/health.module.ts new file mode 100644 index 00000000..f5abd9fe --- /dev/null +++ b/apps/cronjob-service/src/app/health/health.module.ts @@ -0,0 +1,42 @@ +import { Module } from '@nestjs/common'; +import { TerminusModule } from '@nestjs/terminus'; +import { HealthController } from './health.controller'; +import { ConfigService } from '@nestjs/config'; + +@Module({ + imports: [ + TerminusModule.forRoot({ + errorLogStyle: 'pretty', + }), + ], + controllers: [HealthController], + providers: [ + { + provide: 'MEMORY_HEAP_TRESHOLD', + useFactory: (config: ConfigService) => { + return config.get('MEMORY_HEAP_TRESHOLD', 0); + }, + inject: [ConfigService], + }, + { + provide: 'MEMORY_RSS_TRESHOLD', + useFactory: (config: ConfigService) => { + return config.get('MEMORY_RSS_TRESHOLD', 0); + }, + inject: [ConfigService], + }, + { + provide: 'BROKER_CONNECTION_STRING', + useFactory: (config: ConfigService) => { + const username = config.get('BROKER_USERNAME', 'guest'); + const password = config.get('BROKER_PASSWORD', 'guest'); + const hostname = config.get('BROKER_HOST', 'localhost'); + const port = config.get('BROKER_PORT', 5672); + // + return `amqp://${username}:${password}@${hostname}:${port}`; + }, + inject: [ConfigService], + }, + ], +}) +export class HealthModule {} diff --git a/apps/cronjob-service/src/app/metrics/metrics.module.ts b/apps/cronjob-service/src/app/metrics/metrics.module.ts new file mode 100644 index 00000000..bc37b173 --- /dev/null +++ b/apps/cronjob-service/src/app/metrics/metrics.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { PrometheusModule } from '@willsoto/nestjs-prometheus'; + +@Module({ + imports: [ + PrometheusModule.register({ + defaultMetrics: { + enabled: true, + }, + defaultLabels: { + app: 'cronjob-service', + }, + path: '/metrics', + }), + ], +}) +export class MetricsModule {} diff --git a/apps/cronjob-service/src/app/version/version.controller.ts b/apps/cronjob-service/src/app/version/version.controller.ts new file mode 100644 index 00000000..192ea0b4 --- /dev/null +++ b/apps/cronjob-service/src/app/version/version.controller.ts @@ -0,0 +1,14 @@ +import { Controller } from '@nestjs/common'; +import { VersionService } from './version.service'; +import { MessagePattern } from '@nestjs/microservices'; +import { CronjobTopics } from '@toxictoast/azkaban-broker-rabbitmq'; + +@Controller('version') +export class VersionController { + constructor(private readonly service: VersionService) {} + + @MessagePattern(CronjobTopics.VERSION) + async getVersion() { + return this.service.getVersion(); + } +} diff --git a/apps/cronjob-service/src/app/version/version.module.ts b/apps/cronjob-service/src/app/version/version.module.ts new file mode 100644 index 00000000..30f663fb --- /dev/null +++ b/apps/cronjob-service/src/app/version/version.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { VersionController } from './version.controller'; +import { VersionService } from './version.service'; +import { ConfigService } from '@nestjs/config'; + +@Module({ + controllers: [VersionController], + providers: [ + VersionService, + { + provide: 'APP_VERSION', + useFactory: (config: ConfigService) => { + return config.get('APP_VERSION', 'local'); + }, + inject: [ConfigService], + }, + ], +}) +export class VersionModule {} diff --git a/apps/cronjob-service/src/app/version/version.service.ts b/apps/cronjob-service/src/app/version/version.service.ts new file mode 100644 index 00000000..2c576747 --- /dev/null +++ b/apps/cronjob-service/src/app/version/version.service.ts @@ -0,0 +1,10 @@ +import { Inject, Injectable } from '@nestjs/common'; + +@Injectable() +export class VersionService { + constructor(@Inject('APP_VERSION') private readonly appVersion: string) {} + + getVersion(): string { + return this.appVersion; + } +} diff --git a/package-lock.json b/package-lock.json index 58f3d78f..d11ceb7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "@toxictoast/azkaban-base-domain": "^0.0.2", "@toxictoast/azkaban-base-helpers": "^0.0.13", "@toxictoast/azkaban-base-types": "^0.0.1", - "@toxictoast/azkaban-broker-rabbitmq": "^0.0.26", + "@toxictoast/azkaban-broker-rabbitmq": "^0.0.27", "@toxictoast/azkaban-sdk": "^0.0.14", "@willsoto/nestjs-prometheus": "^6.0.1", "amqp-connection-manager": "^4.1.14", @@ -7210,9 +7210,9 @@ "integrity": "sha512-PkXYD4ldTtps2WElLiHVFNIR7dsWlzeGWGsFa3I8BZenhLYJ0q8FI/F9oa0XXHPF3xWtF+XgjZDMKRm9ibwBMw==" }, "node_modules/@toxictoast/azkaban-broker-rabbitmq": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@toxictoast/azkaban-broker-rabbitmq/-/azkaban-broker-rabbitmq-0.0.26.tgz", - "integrity": "sha512-X/QuhYrW2QRjO8svDsQwRy6ZDdEftpnUKv1JmsW4vuUWTDbkzruwcf7JHrZ7YntpJkE+wy6EfFrem05kRdX7wA==", + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/@toxictoast/azkaban-broker-rabbitmq/-/azkaban-broker-rabbitmq-0.0.27.tgz", + "integrity": "sha512-eMdysCrupo43Wqy7kDMDV5dCATK0LAYnzJ2pZXbZtK+CcHwcbdiFLgLwWRCUvlLk1+EhsfWbPXSqmm73PdMleg==", "license": "ISC", "dependencies": { "@nestjs/common": "^10.3.8", diff --git a/package.json b/package.json index f5dbaa9e..c0a22c21 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@toxictoast/azkaban-base-domain": "^0.0.2", "@toxictoast/azkaban-base-helpers": "^0.0.13", "@toxictoast/azkaban-base-types": "^0.0.1", - "@toxictoast/azkaban-broker-rabbitmq": "^0.0.26", + "@toxictoast/azkaban-broker-rabbitmq": "^0.0.27", "@toxictoast/azkaban-sdk": "^0.0.14", "@willsoto/nestjs-prometheus": "^6.0.1", "amqp-connection-manager": "^4.1.14",