@seta-labs/nestjs-health 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/check.util.d.ts +6 -0
- package/dist/check.util.js +36 -0
- package/dist/check.util.js.map +1 -0
- package/dist/dto/event-loop-delay.dto.d.ts +6 -0
- package/dist/dto/event-loop-delay.dto.js +41 -0
- package/dist/dto/event-loop-delay.dto.js.map +1 -0
- package/dist/dto/health-check-response.dto.d.ts +8 -0
- package/dist/dto/health-check-response.dto.js +43 -0
- package/dist/dto/health-check-response.dto.js.map +1 -0
- package/dist/dto/health-check-result.dto.d.ts +8 -0
- package/dist/dto/health-check-result.dto.js +52 -0
- package/dist/dto/health-check-result.dto.js.map +1 -0
- package/dist/dto/health-report-response.dto.d.ts +15 -0
- package/dist/dto/health-report-response.dto.js +66 -0
- package/dist/dto/health-report-response.dto.js.map +1 -0
- package/dist/dto/memory-usage.dto.d.ts +11 -0
- package/dist/dto/memory-usage.dto.js +72 -0
- package/dist/dto/memory-usage.dto.js.map +1 -0
- package/dist/enums/health-status.enum.d.ts +10 -0
- package/dist/enums/health-status.enum.js +15 -0
- package/dist/enums/health-status.enum.js.map +1 -0
- package/dist/health.constants.d.ts +4 -0
- package/dist/health.constants.js +8 -0
- package/dist/health.constants.js.map +1 -0
- package/dist/health.controller.d.ts +6 -0
- package/dist/health.controller.js +87 -0
- package/dist/health.controller.js.map +1 -0
- package/dist/health.mapper.d.ts +7 -0
- package/dist/health.mapper.js +58 -0
- package/dist/health.mapper.js.map +1 -0
- package/dist/health.module.d.ts +10 -0
- package/dist/health.module.js +44 -0
- package/dist/health.module.js.map +1 -0
- package/dist/health.service.d.ts +33 -0
- package/dist/health.service.js +133 -0
- package/dist/health.service.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/memory.util.d.ts +11 -0
- package/dist/memory.util.js +51 -0
- package/dist/memory.util.js.map +1 -0
- package/dist/types/health-indicator.type.d.ts +21 -0
- package/dist/types/health-indicator.type.js +3 -0
- package/dist/types/health-indicator.type.js.map +1 -0
- package/dist/types/health-options.type.d.ts +29 -0
- package/dist/types/health-options.type.js +3 -0
- package/dist/types/health-options.type.js.map +1 -0
- package/dist/types/health-report-input.type.d.ts +14 -0
- package/dist/types/health-report-input.type.js +3 -0
- package/dist/types/health-report-input.type.js.map +1 -0
- package/dist/types/memory-snapshot.type.d.ts +14 -0
- package/dist/types/memory-snapshot.type.js +3 -0
- package/dist/types/memory-snapshot.type.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.controller.js","sourceRoot":"","sources":["../src/health.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AA+BA,wDA2CC;AA1ED,2CASwB;AACxB,6CAKyB;AAEzB,+EAAyE;AACzE,iFAA2E;AAC3E,mEAA0D;AAC1D,qDAAiD;AAOjD;;;GAGG;AACH,SAAgB,sBAAsB,CAAC,kBAAqC,EAAE;IAC1E,MAAM,KAAK,GAAG,IAAA,wBAAe,EAAC,GAAG,eAAe,CAAC,CAAC;IAElD,IAEM,gBAAgB,GAFtB,MAEM,gBAAgB;QACkC;QAApD,YAAoD,MAAqB;YAArB,WAAM,GAAN,MAAM,CAAe;QAAG,CAAC;QAO7E,IAAI;YACA,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;QAQK,AAAN,KAAK,CAAC,KAAK,CACqB,GAAmB;YAE/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,MAAM,KAAK,iCAAY,CAAC,IAAI;gBAAE,GAAG,CAAC,MAAM,CAAC,mBAAU,CAAC,mBAAmB,CAAC,CAAC;YAClF,OAAO,IAAI,CAAC;QAChB,CAAC;QAQD,MAAM;YACF,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAChC,CAAC;KACJ,CAAA;IA3BG;QALC,IAAA,YAAG,GAAE;QACL,KAAK;QACL,IAAA,eAAM,EAAC,eAAe,EAAE,UAAU,CAAC;QACnC,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;QACpF,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,kDAAsB,EAAE,CAAC;;;wCACxC,kDAAsB;gDAE7B;IAQK;QANL,IAAA,YAAG,EAAC,OAAO,CAAC;QACZ,KAAK;QACL,IAAA,eAAM,EAAC,eAAe,EAAE,UAAU,CAAC;QACnC,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;QACjF,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,kDAAsB,EAAE,CAAC;QAC/C,IAAA,uCAA6B,EAAC,EAAE,IAAI,EAAE,kDAAsB,EAAE,CAAC;QAE3D,WAAA,IAAA,YAAG,EAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;;;;iDAK9B;IAQD;QANC,IAAA,YAAG,EAAC,QAAQ,CAAC;QACb,IAAA,eAAM,EAAC,eAAe,EAAE,UAAU,CAAC;QACnC,IAAA,sBAAY,EAAC;YACV,OAAO,EAAE,mEAAmE;SAC/E,CAAC;QACD,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,oDAAuB,EAAE,CAAC;;;;kDAGhD;IAlCC,gBAAgB;QAFrB,IAAA,iBAAO,EAAC,QAAQ,CAAC;QACjB,IAAA,mBAAU,EAAC,QAAQ,CAAC;QAEJ,WAAA,IAAA,eAAM,EAAC,8BAAa,CAAC,CAAA;yCAA0B,8BAAa;OADvE,gBAAgB,CAmCrB;IAED,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HealthCheckResponseDto } from './dto/health-check-response.dto';
|
|
2
|
+
import type { HealthReportResponseDto } from './dto/health-report-response.dto';
|
|
3
|
+
import { HealthStatus } from './enums/health-status.enum';
|
|
4
|
+
import type { CheckResult } from './types/health-indicator.type';
|
|
5
|
+
import type { HealthReportInput } from './types/health-report-input.type';
|
|
6
|
+
export declare function toHealthCheckResponseDto(status: HealthStatus, service: string, checks?: CheckResult[]): HealthCheckResponseDto;
|
|
7
|
+
export declare function toHealthReportResponseDto(input: HealthReportInput): HealthReportResponseDto;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHealthCheckResponseDto = toHealthCheckResponseDto;
|
|
4
|
+
exports.toHealthReportResponseDto = toHealthReportResponseDto;
|
|
5
|
+
const health_status_enum_1 = require("./enums/health-status.enum");
|
|
6
|
+
const memory_util_1 = require("./memory.util");
|
|
7
|
+
function toHealthCheckResponseDto(status, service, checks) {
|
|
8
|
+
return {
|
|
9
|
+
status,
|
|
10
|
+
service,
|
|
11
|
+
timestamp: new Date().toISOString(),
|
|
12
|
+
...(checks && { checks: checks.map((check) => toHealthCheckResultDto(check, false)) }),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function toHealthReportResponseDto(input) {
|
|
16
|
+
return {
|
|
17
|
+
status: input.status,
|
|
18
|
+
service: input.service,
|
|
19
|
+
...(input.version && { version: input.version }),
|
|
20
|
+
node: process.version,
|
|
21
|
+
uptimeSec: input.uptimeSec,
|
|
22
|
+
timestamp: new Date().toISOString(),
|
|
23
|
+
memory: toMemoryUsageDto(input.memory),
|
|
24
|
+
eventLoop: input.eventLoop,
|
|
25
|
+
checks: input.checks.map((check) => toHealthCheckResultDto(check, true)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** `withError` only for the status report: the probes are public. */
|
|
29
|
+
function toHealthCheckResultDto(check, withError) {
|
|
30
|
+
return {
|
|
31
|
+
name: check.name,
|
|
32
|
+
status: check.ok ? health_status_enum_1.HealthStatus.Ok : health_status_enum_1.HealthStatus.Down,
|
|
33
|
+
critical: check.critical,
|
|
34
|
+
durationMs: check.durationMs,
|
|
35
|
+
...(withError && !check.ok && { error: errorMessage(check.err) }),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function toMemoryUsageDto(memory) {
|
|
39
|
+
return {
|
|
40
|
+
rssMiB: (0, memory_util_1.toMiB)(memory.rss),
|
|
41
|
+
heapUsedMiB: (0, memory_util_1.toMiB)(memory.heapUsed),
|
|
42
|
+
heapTotalMiB: (0, memory_util_1.toMiB)(memory.heapTotal),
|
|
43
|
+
externalMiB: (0, memory_util_1.toMiB)(memory.external),
|
|
44
|
+
arrayBuffersMiB: (0, memory_util_1.toMiB)(memory.arrayBuffers),
|
|
45
|
+
heapLimitMiB: (0, memory_util_1.toMiB)(memory.heapLimit),
|
|
46
|
+
containerLimitMiB: memory.containerLimit === null ? null : (0, memory_util_1.toMiB)(memory.containerLimit),
|
|
47
|
+
heapRatio: round(memory.heapRatio, 3),
|
|
48
|
+
rssRatio: memory.rssRatio === null ? null : round(memory.rssRatio, 3),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function errorMessage(err) {
|
|
52
|
+
return err instanceof Error ? err.message : String(err);
|
|
53
|
+
}
|
|
54
|
+
function round(value, digits) {
|
|
55
|
+
const factor = 10 ** digits;
|
|
56
|
+
return Math.round(value * factor) / factor;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=health.mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.mapper.js","sourceRoot":"","sources":["../src/health.mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
+
import type { HealthModuleOptions } from './types/health-options.type';
|
|
3
|
+
/**
|
|
4
|
+
* `GET /health` (liveness), `GET /health/ready` (readiness, 503 when down),
|
|
5
|
+
* `GET /health/status` (diagnostics). Import once in the root module; call
|
|
6
|
+
* `app.enableShutdownHooks()` so readiness fails as soon as shutdown starts.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HealthModule {
|
|
9
|
+
static forRoot(options: HealthModuleOptions): DynamicModule;
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var HealthModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.HealthModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const health_controller_1 = require("./health.controller");
|
|
13
|
+
const health_constants_1 = require("./health.constants");
|
|
14
|
+
const health_service_1 = require("./health.service");
|
|
15
|
+
/**
|
|
16
|
+
* `GET /health` (liveness), `GET /health/ready` (readiness, 503 when down),
|
|
17
|
+
* `GET /health/status` (diagnostics). Import once in the root module; call
|
|
18
|
+
* `app.enableShutdownHooks()` so readiness fails as soon as shutdown starts.
|
|
19
|
+
*/
|
|
20
|
+
let HealthModule = HealthModule_1 = class HealthModule {
|
|
21
|
+
static forRoot(options) {
|
|
22
|
+
const indicators = options.indicators ?? [];
|
|
23
|
+
return {
|
|
24
|
+
module: HealthModule_1,
|
|
25
|
+
imports: options.imports ?? [],
|
|
26
|
+
controllers: [(0, health_controller_1.createHealthController)(options.probeDecorators)],
|
|
27
|
+
providers: [
|
|
28
|
+
{ provide: health_constants_1.HEALTH_OPTIONS, useValue: options },
|
|
29
|
+
{
|
|
30
|
+
provide: health_constants_1.HEALTH_INDICATORS,
|
|
31
|
+
useFactory: (...resolved) => resolved,
|
|
32
|
+
inject: indicators,
|
|
33
|
+
},
|
|
34
|
+
health_service_1.HealthService,
|
|
35
|
+
],
|
|
36
|
+
exports: [health_service_1.HealthService],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.HealthModule = HealthModule;
|
|
41
|
+
exports.HealthModule = HealthModule = HealthModule_1 = __decorate([
|
|
42
|
+
(0, common_1.Module)({})
|
|
43
|
+
], HealthModule);
|
|
44
|
+
//# sourceMappingURL=health.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.module.js","sourceRoot":"","sources":["../src/health.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA4D;AAE5D,2DAA6D;AAC7D,yDAAuE;AACvE,qDAAiD;AAIjD;;;;GAIG;AAEI,IAAM,YAAY,oBAAlB,MAAM,YAAY;IACrB,MAAM,CAAC,OAAO,CAAC,OAA4B;QACvC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAC5C,OAAO;YACH,MAAM,EAAE,cAAY;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,WAAW,EAAE,CAAC,IAAA,0CAAsB,EAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAC9D,SAAS,EAAE;gBACP,EAAE,OAAO,EAAE,iCAAc,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAC9C;oBACI,OAAO,EAAE,oCAAiB;oBAC1B,UAAU,EAAE,CAAC,GAAG,QAA2B,EAAqB,EAAE,CAAC,QAAQ;oBAC3E,MAAM,EAAE,UAAU;iBACrB;gBACD,8BAAa;aAChB;YACD,OAAO,EAAE,CAAC,8BAAa,CAAC;SAC3B,CAAC;IACN,CAAC;CACJ,CAAA;AAnBY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,YAAY,CAmBxB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type OnModuleDestroy, type OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import type { HealthCheckResponseDto } from './dto/health-check-response.dto';
|
|
3
|
+
import type { HealthReportResponseDto } from './dto/health-report-response.dto';
|
|
4
|
+
import type { HealthIndicator } from './types/health-indicator.type';
|
|
5
|
+
import type { HealthModuleOptions } from './types/health-options.type';
|
|
6
|
+
/**
|
|
7
|
+
* Runs the checks behind the three health endpoints. Liveness checks nothing:
|
|
8
|
+
* a failing dependency must not make the kubelet restart every pod. Readiness
|
|
9
|
+
* runs the memory check and the indicators, and fails from the first destroy
|
|
10
|
+
* hook of the shutdown, before the HTTP server closes.
|
|
11
|
+
*/
|
|
12
|
+
export declare class HealthService implements OnModuleInit, OnModuleDestroy {
|
|
13
|
+
private readonly options;
|
|
14
|
+
private readonly logger;
|
|
15
|
+
private readonly indicators;
|
|
16
|
+
private readonly timeoutMs;
|
|
17
|
+
private readonly loop;
|
|
18
|
+
private loopSince;
|
|
19
|
+
private shuttingDown;
|
|
20
|
+
private lastReady;
|
|
21
|
+
constructor(options: HealthModuleOptions, indicators: HealthIndicator[]);
|
|
22
|
+
onModuleInit(): void;
|
|
23
|
+
onModuleDestroy(): void;
|
|
24
|
+
live(): HealthCheckResponseDto;
|
|
25
|
+
ready(): Promise<HealthCheckResponseDto>;
|
|
26
|
+
report(): Promise<HealthReportResponseDto>;
|
|
27
|
+
private runChecks;
|
|
28
|
+
private memoryIndicator;
|
|
29
|
+
/** Percentiles since the previous report, then a fresh window. */
|
|
30
|
+
private drainEventLoop;
|
|
31
|
+
/** One line per readiness change, not one per probe. */
|
|
32
|
+
private logTransition;
|
|
33
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var HealthService_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.HealthService = void 0;
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
19
|
+
const check_util_1 = require("./check.util");
|
|
20
|
+
const health_status_enum_1 = require("./enums/health-status.enum");
|
|
21
|
+
const health_constants_1 = require("./health.constants");
|
|
22
|
+
const health_mapper_1 = require("./health.mapper");
|
|
23
|
+
const memory_util_1 = require("./memory.util");
|
|
24
|
+
const NS_PER_MS = 1e6;
|
|
25
|
+
/**
|
|
26
|
+
* Runs the checks behind the three health endpoints. Liveness checks nothing:
|
|
27
|
+
* a failing dependency must not make the kubelet restart every pod. Readiness
|
|
28
|
+
* runs the memory check and the indicators, and fails from the first destroy
|
|
29
|
+
* hook of the shutdown, before the HTTP server closes.
|
|
30
|
+
*/
|
|
31
|
+
let HealthService = HealthService_1 = class HealthService {
|
|
32
|
+
options;
|
|
33
|
+
logger = new common_1.Logger(HealthService_1.name);
|
|
34
|
+
indicators;
|
|
35
|
+
timeoutMs;
|
|
36
|
+
loop = (0, node_perf_hooks_1.monitorEventLoopDelay)({ resolution: 20 });
|
|
37
|
+
loopSince = Date.now();
|
|
38
|
+
shuttingDown = false;
|
|
39
|
+
lastReady = health_status_enum_1.HealthStatus.Ok;
|
|
40
|
+
constructor(options, indicators) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.indicators =
|
|
43
|
+
options.memory === false ? indicators : [this.memoryIndicator(), ...indicators];
|
|
44
|
+
this.timeoutMs = options.checkTimeoutMs ?? health_constants_1.DEFAULT_CHECK_TIMEOUT_MS;
|
|
45
|
+
}
|
|
46
|
+
onModuleInit() {
|
|
47
|
+
this.loop.enable();
|
|
48
|
+
}
|
|
49
|
+
onModuleDestroy() {
|
|
50
|
+
this.shuttingDown = true;
|
|
51
|
+
this.loop.disable();
|
|
52
|
+
}
|
|
53
|
+
live() {
|
|
54
|
+
return (0, health_mapper_1.toHealthCheckResponseDto)(health_status_enum_1.HealthStatus.Ok, this.options.service);
|
|
55
|
+
}
|
|
56
|
+
async ready() {
|
|
57
|
+
if (this.shuttingDown) {
|
|
58
|
+
return (0, health_mapper_1.toHealthCheckResponseDto)(health_status_enum_1.HealthStatus.Down, this.options.service, [
|
|
59
|
+
{ name: 'shutdown', critical: true, ok: false, durationMs: 0 },
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
const checks = await this.runChecks();
|
|
63
|
+
const status = (0, check_util_1.overallStatus)(checks);
|
|
64
|
+
this.logTransition(status, checks);
|
|
65
|
+
return (0, health_mapper_1.toHealthCheckResponseDto)(status, this.options.service, checks);
|
|
66
|
+
}
|
|
67
|
+
async report() {
|
|
68
|
+
const checks = await this.runChecks();
|
|
69
|
+
return (0, health_mapper_1.toHealthReportResponseDto)({
|
|
70
|
+
status: this.shuttingDown ? health_status_enum_1.HealthStatus.Down : (0, check_util_1.overallStatus)(checks),
|
|
71
|
+
service: this.options.service,
|
|
72
|
+
version: this.options.version,
|
|
73
|
+
uptimeSec: Math.round(process.uptime()),
|
|
74
|
+
memory: (0, memory_util_1.readMemory)(),
|
|
75
|
+
eventLoop: this.drainEventLoop(),
|
|
76
|
+
checks,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
runChecks() {
|
|
80
|
+
return Promise.all(this.indicators.map((indicator) => (0, check_util_1.runCheck)(indicator, this.timeoutMs)));
|
|
81
|
+
}
|
|
82
|
+
memoryIndicator() {
|
|
83
|
+
const memory = this.options.memory || {};
|
|
84
|
+
const thresholds = {
|
|
85
|
+
heapRatio: memory.heapRatio ?? health_constants_1.DEFAULT_MEMORY_RATIO,
|
|
86
|
+
rssRatio: memory.rssRatio ?? health_constants_1.DEFAULT_MEMORY_RATIO,
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
name: 'memory',
|
|
90
|
+
check: () => {
|
|
91
|
+
const failure = (0, memory_util_1.memoryFailure)((0, memory_util_1.readMemory)(), thresholds);
|
|
92
|
+
return failure ? Promise.reject(new Error(failure)) : Promise.resolve();
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Percentiles since the previous report, then a fresh window. */
|
|
97
|
+
drainEventLoop() {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const delay = {
|
|
100
|
+
p50Ms: toMs(this.loop.percentile(50)),
|
|
101
|
+
p99Ms: toMs(this.loop.percentile(99)),
|
|
102
|
+
maxMs: toMs(this.loop.max),
|
|
103
|
+
windowSec: Math.round((now - this.loopSince) / 1000),
|
|
104
|
+
};
|
|
105
|
+
this.loop.reset();
|
|
106
|
+
this.loopSince = now;
|
|
107
|
+
return delay;
|
|
108
|
+
}
|
|
109
|
+
/** One line per readiness change, not one per probe. */
|
|
110
|
+
logTransition(status, checks) {
|
|
111
|
+
if (status === this.lastReady)
|
|
112
|
+
return;
|
|
113
|
+
this.lastReady = status;
|
|
114
|
+
if (status === health_status_enum_1.HealthStatus.Ok) {
|
|
115
|
+
this.logger.log({ service: this.options.service }, 'readiness recovered');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
for (const check of checks.filter((result) => !result.ok)) {
|
|
119
|
+
this.logger.warn({ err: check.err, check: check.name, critical: check.critical, status }, 'health check failing');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
exports.HealthService = HealthService;
|
|
124
|
+
exports.HealthService = HealthService = HealthService_1 = __decorate([
|
|
125
|
+
(0, common_1.Injectable)(),
|
|
126
|
+
__param(0, (0, common_1.Inject)(health_constants_1.HEALTH_OPTIONS)),
|
|
127
|
+
__param(1, (0, common_1.Inject)(health_constants_1.HEALTH_INDICATORS)),
|
|
128
|
+
__metadata("design:paramtypes", [Object, Array])
|
|
129
|
+
], HealthService);
|
|
130
|
+
function toMs(ns) {
|
|
131
|
+
return Math.round((ns / NS_PER_MS) * 10) / 10;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=health.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.service.js","sourceRoot":"","sources":["../src/health.service.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { EventLoopDelayDto } from './dto/event-loop-delay.dto';
|
|
2
|
+
export { HealthCheckResponseDto } from './dto/health-check-response.dto';
|
|
3
|
+
export { HealthCheckResultDto } from './dto/health-check-result.dto';
|
|
4
|
+
export { HealthReportResponseDto } from './dto/health-report-response.dto';
|
|
5
|
+
export { MemoryUsageDto } from './dto/memory-usage.dto';
|
|
6
|
+
export { HealthStatus } from './enums/health-status.enum';
|
|
7
|
+
export { HealthModule } from './health.module';
|
|
8
|
+
export { HealthService } from './health.service';
|
|
9
|
+
export type { HealthIndicator } from './types/health-indicator.type';
|
|
10
|
+
export type { HealthModuleOptions, MemoryThresholds } from './types/health-options.type';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HealthService = exports.HealthModule = exports.HealthStatus = exports.MemoryUsageDto = exports.HealthReportResponseDto = exports.HealthCheckResultDto = exports.HealthCheckResponseDto = exports.EventLoopDelayDto = void 0;
|
|
4
|
+
var event_loop_delay_dto_1 = require("./dto/event-loop-delay.dto");
|
|
5
|
+
Object.defineProperty(exports, "EventLoopDelayDto", { enumerable: true, get: function () { return event_loop_delay_dto_1.EventLoopDelayDto; } });
|
|
6
|
+
var health_check_response_dto_1 = require("./dto/health-check-response.dto");
|
|
7
|
+
Object.defineProperty(exports, "HealthCheckResponseDto", { enumerable: true, get: function () { return health_check_response_dto_1.HealthCheckResponseDto; } });
|
|
8
|
+
var health_check_result_dto_1 = require("./dto/health-check-result.dto");
|
|
9
|
+
Object.defineProperty(exports, "HealthCheckResultDto", { enumerable: true, get: function () { return health_check_result_dto_1.HealthCheckResultDto; } });
|
|
10
|
+
var health_report_response_dto_1 = require("./dto/health-report-response.dto");
|
|
11
|
+
Object.defineProperty(exports, "HealthReportResponseDto", { enumerable: true, get: function () { return health_report_response_dto_1.HealthReportResponseDto; } });
|
|
12
|
+
var memory_usage_dto_1 = require("./dto/memory-usage.dto");
|
|
13
|
+
Object.defineProperty(exports, "MemoryUsageDto", { enumerable: true, get: function () { return memory_usage_dto_1.MemoryUsageDto; } });
|
|
14
|
+
var health_status_enum_1 = require("./enums/health-status.enum");
|
|
15
|
+
Object.defineProperty(exports, "HealthStatus", { enumerable: true, get: function () { return health_status_enum_1.HealthStatus; } });
|
|
16
|
+
var health_module_1 = require("./health.module");
|
|
17
|
+
Object.defineProperty(exports, "HealthModule", { enumerable: true, get: function () { return health_module_1.HealthModule; } });
|
|
18
|
+
var health_service_1 = require("./health.service");
|
|
19
|
+
Object.defineProperty(exports, "HealthService", { enumerable: true, get: function () { return health_service_1.HealthService; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mEAA+D;AAAtD,yHAAA,iBAAiB,OAAA;AAC1B,6EAAyE;AAAhE,mIAAA,sBAAsB,OAAA;AAC/B,yEAAqE;AAA5D,+HAAA,oBAAoB,OAAA;AAC7B,+EAA2E;AAAlE,qIAAA,uBAAuB,OAAA;AAChC,2DAAwD;AAA/C,kHAAA,cAAc,OAAA;AACvB,iEAA0D;AAAjD,kHAAA,YAAY,OAAA;AACrB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AACrB,mDAAiD;AAAxC,+GAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MemoryThresholds } from './types/health-options.type';
|
|
2
|
+
import type { MemorySnapshot } from './types/memory-snapshot.type';
|
|
3
|
+
/**
|
|
4
|
+
* The two ways a Node process dies of memory are distinct: the V8 heap hits
|
|
5
|
+
* `heap_size_limit` ("heap out of memory"), or the resident set hits the
|
|
6
|
+
* cgroup limit (OOMKill), driven by buffers and native memory outside the heap.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readMemory(): MemorySnapshot;
|
|
9
|
+
/** The reason readiness must fail, or `undefined` under both thresholds. */
|
|
10
|
+
export declare function memoryFailure(memory: MemorySnapshot, thresholds: Required<MemoryThresholds>): string | undefined;
|
|
11
|
+
export declare function toMiB(bytes: number): number;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readMemory = readMemory;
|
|
4
|
+
exports.memoryFailure = memoryFailure;
|
|
5
|
+
exports.toMiB = toMiB;
|
|
6
|
+
const node_os_1 = require("node:os");
|
|
7
|
+
const node_v8_1 = require("node:v8");
|
|
8
|
+
const MIB = 1024 * 1024;
|
|
9
|
+
/**
|
|
10
|
+
* The two ways a Node process dies of memory are distinct: the V8 heap hits
|
|
11
|
+
* `heap_size_limit` ("heap out of memory"), or the resident set hits the
|
|
12
|
+
* cgroup limit (OOMKill), driven by buffers and native memory outside the heap.
|
|
13
|
+
*/
|
|
14
|
+
function readMemory() {
|
|
15
|
+
const usage = process.memoryUsage();
|
|
16
|
+
const heapLimit = (0, node_v8_1.getHeapStatistics)().heap_size_limit;
|
|
17
|
+
const containerLimit = constrainedMemory();
|
|
18
|
+
return {
|
|
19
|
+
rss: usage.rss,
|
|
20
|
+
heapUsed: usage.heapUsed,
|
|
21
|
+
heapTotal: usage.heapTotal,
|
|
22
|
+
external: usage.external,
|
|
23
|
+
arrayBuffers: usage.arrayBuffers,
|
|
24
|
+
heapLimit,
|
|
25
|
+
containerLimit,
|
|
26
|
+
heapRatio: usage.heapUsed / heapLimit,
|
|
27
|
+
rssRatio: containerLimit === null ? null : usage.rss / containerLimit,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** The reason readiness must fail, or `undefined` under both thresholds. */
|
|
31
|
+
function memoryFailure(memory, thresholds) {
|
|
32
|
+
if (memory.heapRatio > thresholds.heapRatio) {
|
|
33
|
+
return `heap at ${percent(memory.heapRatio)} of the V8 limit`;
|
|
34
|
+
}
|
|
35
|
+
if (memory.rssRatio !== null && memory.rssRatio > thresholds.rssRatio) {
|
|
36
|
+
return `rss at ${percent(memory.rssRatio)} of the container limit`;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
function toMiB(bytes) {
|
|
41
|
+
return Math.round((bytes / MIB) * 10) / 10;
|
|
42
|
+
}
|
|
43
|
+
/** `0` (Node 22) or a value above the host memory means no cgroup limit. */
|
|
44
|
+
function constrainedMemory() {
|
|
45
|
+
const limit = process.constrainedMemory();
|
|
46
|
+
return limit > 0 && limit < (0, node_os_1.totalmem)() ? limit : null;
|
|
47
|
+
}
|
|
48
|
+
function percent(ratio) {
|
|
49
|
+
return `${Math.round(ratio * 100)}%`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=memory.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.util.js","sourceRoot":"","sources":["../src/memory.util.ts"],"names":[],"mappings":";;AAaA,gCAeC;AAGD,sCAWC;AAED,sBAEC;AA9CD,qCAAmC;AACnC,qCAA4C;AAK5C,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAExB;;;;GAIG;AACH,SAAgB,UAAU;IACtB,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,IAAA,2BAAiB,GAAE,CAAC,eAAe,CAAC;IACtD,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;IAC3C,OAAO;QACH,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,SAAS;QACT,cAAc;QACd,SAAS,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS;QACrC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,cAAc;KACxE,CAAC;AACN,CAAC;AAED,4EAA4E;AAC5E,SAAgB,aAAa,CACzB,MAAsB,EACtB,UAAsC;IAEtC,IAAI,MAAM,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;QAC1C,OAAO,WAAW,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAClE,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;QACpE,OAAO,UAAU,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IACvE,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,SAAgB,KAAK,CAAC,KAAa;IAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AAC/C,CAAC;AAED,4EAA4E;AAC5E,SAAS,iBAAiB;IACtB,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAC1C,OAAO,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,IAAA,kBAAQ,GAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC1B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A dependency probe. Implement it as a provider of the module that owns the
|
|
3
|
+
* dependency (`DatabaseHealthIndicator` in the Prisma module), export it, and
|
|
4
|
+
* list the module in `HealthModule.forRoot({ imports, indicators })`.
|
|
5
|
+
*/
|
|
6
|
+
export interface HealthIndicator {
|
|
7
|
+
/** Key of the check in the report: `database`, `redis`. */
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/** A failing critical check takes the pod out of the load balancer. Default `true`. */
|
|
10
|
+
readonly critical?: boolean;
|
|
11
|
+
/** Resolves when the dependency answers, rejects otherwise. */
|
|
12
|
+
check(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** Outcome of one indicator; `err` stays server-side (log, status report). */
|
|
15
|
+
export interface CheckResult {
|
|
16
|
+
name: string;
|
|
17
|
+
critical: boolean;
|
|
18
|
+
ok: boolean;
|
|
19
|
+
durationMs: number;
|
|
20
|
+
err?: unknown;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-indicator.type.js","sourceRoot":"","sources":["../../src/types/health-indicator.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ModuleMetadata, Type } from '@nestjs/common';
|
|
2
|
+
import type { HealthIndicator } from './health-indicator.type';
|
|
3
|
+
/** Readiness fails above these fractions of the limit, `0 < ratio <= 1`. */
|
|
4
|
+
export interface MemoryThresholds {
|
|
5
|
+
/** `heapUsed / heap_size_limit`: above it V8 is close to "heap out of memory". Default `0.9`. */
|
|
6
|
+
heapRatio?: number;
|
|
7
|
+
/** `rss / container limit`: above it the kernel is close to an OOMKill. Default `0.9`. */
|
|
8
|
+
rssRatio?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface HealthModuleOptions {
|
|
11
|
+
/** Service name echoed in every response. */
|
|
12
|
+
service: string;
|
|
13
|
+
/** Release shown by `/health/status`, e.g. the image tag. */
|
|
14
|
+
version?: string;
|
|
15
|
+
/** Modules that export the indicators. */
|
|
16
|
+
imports?: ModuleMetadata['imports'];
|
|
17
|
+
/** Dependency probes run by `/health/ready` and `/health/status`. */
|
|
18
|
+
indicators?: Type<HealthIndicator>[];
|
|
19
|
+
/** Memory pressure thresholds; `false` drops the memory check. */
|
|
20
|
+
memory?: MemoryThresholds | false;
|
|
21
|
+
/** Per-check timeout, below the probe `timeoutSeconds` (k8s default 1 s). Default `750`. */
|
|
22
|
+
checkTimeoutMs?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Decorators applied to the two probes (`/health`, `/health/ready`), e.g.
|
|
25
|
+
* the app's `Public()` when a global guard protects every route.
|
|
26
|
+
* `/health/status` stays behind the app's guards.
|
|
27
|
+
*/
|
|
28
|
+
probeDecorators?: MethodDecorator[];
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-options.type.js","sourceRoot":"","sources":["../../src/types/health-options.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EventLoopDelayDto } from '../dto/event-loop-delay.dto';
|
|
2
|
+
import type { HealthStatus } from '../enums/health-status.enum';
|
|
3
|
+
import type { CheckResult } from './health-indicator.type';
|
|
4
|
+
import type { MemorySnapshot } from './memory-snapshot.type';
|
|
5
|
+
/** What the service assembles for the status report. */
|
|
6
|
+
export interface HealthReportInput {
|
|
7
|
+
status: HealthStatus;
|
|
8
|
+
service: string;
|
|
9
|
+
version?: string;
|
|
10
|
+
uptimeSec: number;
|
|
11
|
+
memory: MemorySnapshot;
|
|
12
|
+
eventLoop: EventLoopDelayDto;
|
|
13
|
+
checks: CheckResult[];
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-report-input.type.js","sourceRoot":"","sources":["../../src/types/health-report-input.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Process memory in bytes, with the two limits it is measured against. */
|
|
2
|
+
export interface MemorySnapshot {
|
|
3
|
+
rss: number;
|
|
4
|
+
heapUsed: number;
|
|
5
|
+
heapTotal: number;
|
|
6
|
+
external: number;
|
|
7
|
+
arrayBuffers: number;
|
|
8
|
+
/** V8 `heap_size_limit`: Node sizes it from the container limit (about half on small pods). */
|
|
9
|
+
heapLimit: number;
|
|
10
|
+
/** cgroup memory limit, `null` when the process is not constrained (local dev). */
|
|
11
|
+
containerLimit: number | null;
|
|
12
|
+
heapRatio: number;
|
|
13
|
+
rssRatio: number | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-snapshot.type.js","sourceRoot":"","sources":["../../src/types/memory-snapshot.type.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seta-labs/nestjs-health",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Health endpoints for NestJS APIs: dependency-free liveness, readiness on memory pressure against the V8 heap and container limits plus the app's critical dependencies, a diagnostic status report and a readiness flip on shutdown",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Seta-Labs/node-common.git",
|
|
9
|
+
"directory": "packages/nestjs-health"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@nestjs/common": "^11.0.0",
|
|
26
|
+
"@nestjs/core": "^11.0.0",
|
|
27
|
+
"@nestjs/swagger": "^11.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@nestjs/common": "^11.1.27",
|
|
31
|
+
"@nestjs/core": "^11.1.27",
|
|
32
|
+
"@nestjs/platform-express": "^11.1.27",
|
|
33
|
+
"@nestjs/swagger": "^11.4.7",
|
|
34
|
+
"@types/node": "^22.0.0",
|
|
35
|
+
"reflect-metadata": "^0.2.2",
|
|
36
|
+
"rxjs": "^7.8.1",
|
|
37
|
+
"typescript": "^5.9.3",
|
|
38
|
+
"vitest": "^4.1.9"
|
|
39
|
+
}
|
|
40
|
+
}
|