Skip to content

Commit e88c1e2

Browse files
committed
feat: add custom logger
resolves #2068
1 parent 1246e2b commit e88c1e2

13 files changed

+142
-38
lines changed

e2e/helper/bootstrap-testing-module.ts

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ function createHealthController(func: TestingHealthFunc) {
5353
private readonly typeorm: TypeOrmHealthIndicator,
5454
private readonly mikroOrm: MikroOrmHealthIndicator,
5555
) {}
56-
// @ts-ignore
5756
@Get('health')
5857
health() {
5958
return func({

lib/health-check/health-check.service.spec.ts

+31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { HealthCheckService } from './health-check.service';
33
import { HealthCheckExecutor } from './health-check-executor.service';
44
import { ERROR_LOGGER } from './error-logger/error-logger.provider';
55
import { ErrorLogger } from './error-logger/error-logger.interface';
6+
import { TERMINUS_LOGGER } from './logger/logger.provider';
7+
import { ConsoleLogger } from '@nestjs/common';
68

79
const healthCheckExecutorMock: Partial<HealthCheckExecutor> = {
810
execute: jest.fn(),
@@ -12,9 +14,19 @@ const errorLoggerMock: ErrorLogger = {
1214
getErrorMessage: jest.fn(),
1315
};
1416

17+
const loggerMock: Partial<ConsoleLogger> = {
18+
setContext: jest.fn(),
19+
log: jest.fn(),
20+
error: jest.fn(),
21+
warn: jest.fn(),
22+
debug: jest.fn(),
23+
};
24+
1525
describe('HealthCheckService', () => {
1626
let healthCheckExecutor: HealthCheckExecutor;
1727
let healthCheckService: HealthCheckService;
28+
let logger: ConsoleLogger;
29+
let errorLogger: ErrorLogger;
1830

1931
beforeEach(async () => {
2032
const module = Test.createTestingModule({
@@ -28,12 +40,18 @@ describe('HealthCheckService', () => {
2840
provide: ERROR_LOGGER,
2941
useValue: errorLoggerMock,
3042
},
43+
{
44+
provide: TERMINUS_LOGGER,
45+
useValue: loggerMock,
46+
},
3147
],
3248
});
3349
const context = await module.compile();
3450

3551
healthCheckService = context.get(HealthCheckService);
3652
healthCheckExecutor = context.get(HealthCheckExecutor);
53+
logger = context.get(TERMINUS_LOGGER);
54+
errorLogger = context.get(ERROR_LOGGER);
3755
});
3856

3957
it('should return the result', async () => {
@@ -55,4 +73,17 @@ describe('HealthCheckService', () => {
5573
expect((error as any).status).toBe(503);
5674
}
5775
});
76+
77+
it('should print an error message', async () => {
78+
(healthCheckExecutor.execute as jest.Mock).mockReturnValue({
79+
status: 'error',
80+
});
81+
(errorLogger.getErrorMessage as jest.Mock).mockReturnValue('error message');
82+
83+
try {
84+
await healthCheckService.check([() => Promise.resolve({})]);
85+
} catch (error) {
86+
expect(logger.error).toHaveBeenCalledWith('error message');
87+
}
88+
});
5889
});

lib/health-check/health-check.service.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import {
22
Injectable,
33
ServiceUnavailableException,
4-
Logger,
54
Inject,
5+
ConsoleLogger,
66
} from '@nestjs/common';
77
import { HealthIndicatorFunction } from '../health-indicator';
88
import { ErrorLogger } from './error-logger/error-logger.interface';
99
import { ERROR_LOGGER } from './error-logger/error-logger.provider';
1010
import { HealthCheckExecutor } from './health-check-executor.service';
1111
import { HealthCheckResult } from './health-check-result.interface';
12+
import { TERMINUS_LOGGER } from './logger/logger.provider';
1213

1314
/**
1415
* Handles Health Checks which can be used in
@@ -20,9 +21,11 @@ export class HealthCheckService {
2021
private readonly healthCheckExecutor: HealthCheckExecutor,
2122
@Inject(ERROR_LOGGER)
2223
private readonly errorLogger: ErrorLogger,
23-
) {}
24-
25-
private readonly logger = new Logger(HealthCheckService.name);
24+
@Inject(TERMINUS_LOGGER)
25+
private readonly logger: ConsoleLogger,
26+
) {
27+
this.logger.setContext(HealthCheckService.name);
28+
}
2629

2730
/**
2831
* Checks the given health indicators
@@ -45,7 +48,7 @@ export class HealthCheckService {
4548
return result;
4649
}
4750

48-
if (result.error) {
51+
if (result.status === 'error') {
4952
const msg = this.errorLogger.getErrorMessage(
5053
'Health Check has failed!',
5154
result.details,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { Injectable, Scope, ConsoleLogger } from '@nestjs/common';
2+
3+
@Injectable({ scope: Scope.TRANSIENT })
4+
export class DefaultTerminusLogger extends ConsoleLogger {}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { LoggerService, Provider, Type } from '@nestjs/common';
2+
import { DefaultTerminusLogger } from './default-logger.service';
3+
4+
export const TERMINUS_LOGGER = 'TERMINUS_LOGGER';
5+
6+
export function getLoggerProvider(
7+
logger?: Type<LoggerService> | boolean,
8+
): Provider<LoggerService> {
9+
if (logger === true || logger === undefined) {
10+
return {
11+
provide: TERMINUS_LOGGER,
12+
useClass: DefaultTerminusLogger,
13+
};
14+
}
15+
16+
if (logger === false) {
17+
return {
18+
provide: TERMINUS_LOGGER,
19+
useValue: {
20+
// eslint-disable-next-line @typescript-eslint/no-empty-function
21+
log: () => {},
22+
// eslint-disable-next-line @typescript-eslint/no-empty-function
23+
error: () => {},
24+
// eslint-disable-next-line @typescript-eslint/no-empty-function
25+
warn: () => {},
26+
},
27+
};
28+
}
29+
30+
return {
31+
provide: TERMINUS_LOGGER,
32+
useClass: logger,
33+
};
34+
}

lib/health-indicator/http/http.health.spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { HttpModule, HttpService } from '@nestjs/axios';
33
import { HttpHealthIndicator } from './http.health';
44
import { checkPackages } from '../../utils/checkPackage.util';
55
import { of } from 'rxjs';
6+
import { TERMINUS_LOGGER } from '../../health-check/logger/logger.provider';
67
jest.mock('../../utils/checkPackage.util');
78

89
// == MOCKS ==
@@ -33,6 +34,13 @@ describe('Http Response Health Indicator', () => {
3334
provide: nestJSAxiosMock.HttpService as any,
3435
useValue: httpServiceMock,
3536
},
37+
{
38+
provide: TERMINUS_LOGGER,
39+
useValue: {
40+
error: jest.fn(),
41+
setContext: jest.fn(),
42+
},
43+
},
3644
],
3745
}).compile();
3846

lib/health-indicator/http/http.health.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
import { Injectable, Scope } from '@nestjs/common';
1+
import {
2+
ConsoleLogger,
3+
Inject,
4+
Injectable,
5+
Logger,
6+
Scope,
7+
} from '@nestjs/common';
28
import { HealthIndicator, HealthIndicatorResult } from '..';
39
import { HealthCheckError } from '../../health-check/health-check.error';
410
import { lastValueFrom, Observable } from 'rxjs';
511
import { ModuleRef } from '@nestjs/core';
612
import { checkPackages, isAxiosError } from '../../utils';
713
import type * as NestJSAxios from '@nestjs/axios';
814
import { AxiosRequestConfig, AxiosResponse } from './axios.interfaces';
9-
import { Logger } from '@nestjs/common/services/logger.service';
1015
import { URL } from 'url';
1116
import { AxiosError } from '../../errors/axios.error';
12-
13-
const logger = new Logger('HttpHealthIndicator');
17+
import { TERMINUS_LOGGER } from '../../health-check/logger/logger.provider';
1418

1519
interface HttpClientLike {
1620
request<T = any>(config: any): Observable<AxiosResponse<T>>;
@@ -32,8 +36,13 @@ export class HttpHealthIndicator extends HealthIndicator {
3236
* Initializes the health indicator
3337
* @param httpService The HttpService provided by Nest
3438
*/
35-
constructor(private readonly moduleRef: ModuleRef) {
39+
constructor(
40+
private readonly moduleRef: ModuleRef,
41+
@Inject(TERMINUS_LOGGER)
42+
private readonly logger: ConsoleLogger,
43+
) {
3644
super();
45+
this.logger.setContext(HttpHealthIndicator.name);
3746
this.checkDependantPackages();
3847
}
3948

@@ -53,7 +62,7 @@ export class HttpHealthIndicator extends HealthIndicator {
5362
strict: false,
5463
});
5564
} catch (err) {
56-
logger.error(
65+
this.logger.error(
5766
'It seems like "HttpService" is not available in the current context. Are you sure you imported the HttpModule from the @nestjs/axios package?',
5867
);
5968
throw new Error(

lib/terminus-options.interface.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { LoggerService, Type } from '@nestjs/common';
2+
13
export type ErrorLogStyle = 'pretty' | 'json';
24

35
export interface TerminusModuleOptions {
4-
errorLogStyle: ErrorLogStyle;
6+
errorLogStyle?: ErrorLogStyle;
7+
logger?: Type<LoggerService> | boolean;
58
}

lib/terminus.module.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { HealthCheckExecutor } from './health-check/health-check-executor.servic
66
import { ERROR_LOGGERS } from './health-check/error-logger/error-loggers.provider';
77
import { getErrorLoggerProvider } from './health-check/error-logger/error-logger.provider';
88
import { TerminusModuleOptions } from './terminus-options.interface';
9+
import { getLoggerProvider } from './health-check/logger/logger.provider';
910

1011
const providers = [
1112
...ERROR_LOGGERS,
@@ -24,16 +25,20 @@ const exports_ = [HealthCheckService, ...HEALTH_INDICATORS];
2425
* @publicApi
2526
*/
2627
@Module({
27-
providers: [...providers, getErrorLoggerProvider()],
28+
providers: [...providers, getErrorLoggerProvider(), getLoggerProvider()],
2829
exports: exports_,
2930
})
3031
export class TerminusModule {
31-
static forRoot(
32-
options: TerminusModuleOptions = { errorLogStyle: 'json' },
33-
): DynamicModule {
32+
static forRoot(options: TerminusModuleOptions = {}): DynamicModule {
33+
const { errorLogStyle = 'json', logger = true } = options;
34+
3435
return {
3536
module: TerminusModule,
36-
providers: [...providers, getErrorLoggerProvider(options.errorLogStyle)],
37+
providers: [
38+
...providers,
39+
getErrorLoggerProvider(errorLogStyle),
40+
getLoggerProvider(logger),
41+
],
3742
exports: exports_,
3843
};
3944
}

tools/gulp/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { join } from 'path';
33

44
// All paths are related to the base dir
55
export const rootFolder = join(__dirname, '../../');
6-
export const tsconfig = join(rootFolder, 'tsconfig.json');
6+
export const tsconfig = join(rootFolder, 'tsconfig.build.json');
77
export const libPath = join(rootFolder, 'lib');
88
export const samplePath = join(rootFolder, 'sample');
99
export const distPath = join(rootFolder, 'dist');

tsconfig.base.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"baseUrl": ".",
4+
"module": "commonjs",
5+
"strict": true,
6+
"declaration": true,
7+
"noImplicitAny": true,
8+
"removeComments": false,
9+
"noLib": false,
10+
"emitDecoratorMetadata": true,
11+
"experimentalDecorators": true,
12+
"target": "es6",
13+
"sourceMap": true,
14+
"outDir": "./dist",
15+
"rootDir": "./lib",
16+
"alwaysStrict": true,
17+
"skipLibCheck": true
18+
},
19+
}

tsconfig.build.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": "./tsconfig.base.json",
3+
"include": ["lib/**/*"],
4+
"exclude": ["node_modules", "**/*.spec.ts", "sample/**/*.ts"]
5+
}

tsconfig.json

+3-19
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
{
2-
"compilerOptions": {
3-
"baseUrl": ".",
4-
"module": "commonjs",
5-
"strict": true,
6-
"declaration": true,
7-
"noImplicitAny": true,
8-
"removeComments": false,
9-
"noLib": false,
10-
"emitDecoratorMetadata": true,
11-
"experimentalDecorators": true,
12-
"target": "es6",
13-
"sourceMap": true,
14-
"outDir": "./dist",
15-
"rootDir": "./lib",
16-
"alwaysStrict": true,
17-
"skipLibCheck": true
18-
},
19-
"include": ["lib/**/*"],
20-
"exclude": ["node_modules", "**/*.spec.ts", "sample/**/*.ts", "e2e_old"]
2+
"extends": "./tsconfig.base.json",
3+
"include": ["lib/**/*", "e2e/**/*", "tools/**/*"],
4+
"exclude": ["node_modules", "sample/**/*.ts"]
215
}

0 commit comments

Comments
 (0)