xcally-nest-library 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.js +22 -0
- package/.nvmrc +1 -0
- package/.prettierrc +5 -0
- package/.vscode/settings.json +6 -0
- package/README.md +73 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/src/config/env.validation.d.ts +21 -0
- package/dist/src/config/env.validation.js +80 -0
- package/dist/src/config/env.validation.js.map +1 -0
- package/dist/src/db/typeorm/generic.repository.d.ts +18 -0
- package/dist/src/db/typeorm/generic.repository.js +43 -0
- package/dist/src/db/typeorm/generic.repository.js.map +1 -0
- package/dist/src/decorators/utils.decorators.d.ts +2 -0
- package/dist/src/decorators/utils.decorators.js +15 -0
- package/dist/src/decorators/utils.decorators.js.map +1 -0
- package/dist/src/interceptors/log-request.interceptor.d.ts +8 -0
- package/dist/src/interceptors/log-request.interceptor.js +42 -0
- package/dist/src/interceptors/log-request.interceptor.js.map +1 -0
- package/dist/src/interceptors/serialize.interceptor.d.ts +12 -0
- package/dist/src/interceptors/serialize.interceptor.js +24 -0
- package/dist/src/interceptors/serialize.interceptor.js.map +1 -0
- package/dist/src/modules/logger/winston.decorator.d.ts +1 -0
- package/dist/src/modules/logger/winston.decorator.js +17 -0
- package/dist/src/modules/logger/winston.decorator.js.map +1 -0
- package/dist/src/modules/logger/winston.interface.d.ts +21 -0
- package/dist/src/modules/logger/winston.interface.js +3 -0
- package/dist/src/modules/logger/winston.interface.js.map +1 -0
- package/dist/src/modules/logger/winston.module.d.ts +4 -0
- package/dist/src/modules/logger/winston.module.js +95 -0
- package/dist/src/modules/logger/winston.module.js.map +1 -0
- package/dist/src/modules/logger/winston.service.d.ts +23 -0
- package/dist/src/modules/logger/winston.service.js +113 -0
- package/dist/src/modules/logger/winston.service.js.map +1 -0
- package/dist/src/modules/tracer/tracer.middleware.d.ts +24 -0
- package/dist/src/modules/tracer/tracer.middleware.js +41 -0
- package/dist/src/modules/tracer/tracer.middleware.js.map +1 -0
- package/dist/src/modules/tracer/tracer.module.d.ts +7 -0
- package/dist/src/modules/tracer/tracer.module.js +36 -0
- package/dist/src/modules/tracer/tracer.module.js.map +1 -0
- package/dist/src/types/auth.d.ts +8 -0
- package/dist/src/types/auth.js +9 -0
- package/dist/src/types/auth.js.map +1 -0
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/index.js +18 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/index.ts +9 -0
- package/nest-cli.json +8 -0
- package/package.json +89 -0
- package/src/config/env.validation.ts +59 -0
- package/src/db/typeorm/generic.repository.ts +43 -0
- package/src/decorators/utils.decorators.ts +28 -0
- package/src/interceptors/log-request.interceptor.ts +30 -0
- package/src/interceptors/serialize.interceptor.ts +27 -0
- package/src/modules/logger/winston.decorator.ts +14 -0
- package/src/modules/logger/winston.interface.ts +22 -0
- package/src/modules/logger/winston.module.ts +105 -0
- package/src/modules/logger/winston.service.ts +60 -0
- package/src/modules/tracer/tracer.middleware.ts +40 -0
- package/src/modules/tracer/tracer.module.ts +18 -0
- package/src/types/auth.ts +9 -0
- package/src/types/index.ts +1 -0
- package/test/jest-e2e.json +9 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +21 -0
package/index.ts
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
export * from './src/types';
|
2
|
+
export * from './src/interceptors/log-request.interceptor';
|
3
|
+
export * from './src/interceptors/serialize.interceptor';
|
4
|
+
export * from './src/modules/logger/winston.service';
|
5
|
+
export * from './src/modules/logger/winston.module';
|
6
|
+
export * from './src/modules/tracer/tracer.module';
|
7
|
+
export * from './src/db/typeorm/generic.repository';
|
8
|
+
export * from './src/decorators/utils.decorators';
|
9
|
+
export * from './src/config/env.validation';
|
package/nest-cli.json
ADDED
package/package.json
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
{
|
2
|
+
"name": "xcally-nest-library",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"types": "dist/index.d.ts",
|
7
|
+
"author": "",
|
8
|
+
"license": "UNLICENSED",
|
9
|
+
"scripts": {
|
10
|
+
"prepublish": "pnpm build",
|
11
|
+
"build": "nest build",
|
12
|
+
"build:watch": "rimraf dist && tsc-watch -b -v",
|
13
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
14
|
+
"prettier:check": "prettier --check ./**/*.{ts,js,json,*rc}",
|
15
|
+
"prettier:write": "prettier --write ./**/*.{ts,js,json,*rc}",
|
16
|
+
"start": "nest start",
|
17
|
+
"start:dev": "nest start --watch",
|
18
|
+
"start:debug": "nest start --debug --watch",
|
19
|
+
"start:prod": "node dist/main",
|
20
|
+
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
21
|
+
"test": "jest",
|
22
|
+
"test:watch": "jest --watch",
|
23
|
+
"test:cov": "jest --coverage",
|
24
|
+
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
25
|
+
"test:e2e": "jest --config ./test/jest-e2e.json",
|
26
|
+
"link:lib": "pnpm link --global",
|
27
|
+
"unlink:lib": "pnpm unlink xcally-nest-libs"
|
28
|
+
},
|
29
|
+
"dependencies": {
|
30
|
+
"@nestjs/axios": "^3.0.2",
|
31
|
+
"@nestjs/common": "^10.3.7",
|
32
|
+
"@nestjs/config": "^3.2.2",
|
33
|
+
"@nestjs/core": "^10.3.7",
|
34
|
+
"@nestjs/microservices": "^10.3.7",
|
35
|
+
"@nestjs/platform-express": "^10.3.7",
|
36
|
+
"axios": "^1.6.8",
|
37
|
+
"class-transformer": "^0.5.1",
|
38
|
+
"class-validator": "^0.14.1",
|
39
|
+
"datadog-winston": "^1.6.0",
|
40
|
+
"nest-winston": "^1.9.4",
|
41
|
+
"nestjs-grpc-exceptions": "^0.2.2",
|
42
|
+
"reflect-metadata": "^0.2.2",
|
43
|
+
"rxjs": "^7.8.1",
|
44
|
+
"typeorm": "^0.3.20",
|
45
|
+
"winston": "^3.13.0",
|
46
|
+
"winston-elasticsearch": "^0.18.0"
|
47
|
+
},
|
48
|
+
"devDependencies": {
|
49
|
+
"@nestjs/cli": "^10.3.2",
|
50
|
+
"@nestjs/schematics": "^10.1.1",
|
51
|
+
"@nestjs/testing": "^10.3.7",
|
52
|
+
"@types/express": "^4.17.21",
|
53
|
+
"@types/jest": "^29.5.12",
|
54
|
+
"@types/node": "^20.12.7",
|
55
|
+
"@types/supertest": "^6.0.2",
|
56
|
+
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
57
|
+
"@typescript-eslint/parser": "^7.6.0",
|
58
|
+
"eslint": "^8.57.0",
|
59
|
+
"eslint-config-prettier": "^9.1.0",
|
60
|
+
"eslint-plugin-prettier": "^5.1.3",
|
61
|
+
"jest": "^29.7.0",
|
62
|
+
"prettier": "^3.2.5",
|
63
|
+
"source-map-support": "^0.5.21",
|
64
|
+
"supertest": "^6.3.4",
|
65
|
+
"ts-jest": "^29.1.2",
|
66
|
+
"ts-loader": "^9.5.1",
|
67
|
+
"ts-node": "^10.9.2",
|
68
|
+
"tsc-watch": "^6.2.0",
|
69
|
+
"tsconfig-paths": "^4.2.0",
|
70
|
+
"typescript": "^5.4.5"
|
71
|
+
},
|
72
|
+
"jest": {
|
73
|
+
"moduleFileExtensions": [
|
74
|
+
"js",
|
75
|
+
"json",
|
76
|
+
"ts"
|
77
|
+
],
|
78
|
+
"rootDir": "src",
|
79
|
+
"testRegex": ".*\\.spec\\.ts$",
|
80
|
+
"transform": {
|
81
|
+
"^.+\\.(t|j)s$": "ts-jest"
|
82
|
+
},
|
83
|
+
"collectCoverageFrom": [
|
84
|
+
"**/*.(t|j)s"
|
85
|
+
],
|
86
|
+
"coverageDirectory": "../coverage",
|
87
|
+
"testEnvironment": "node"
|
88
|
+
}
|
89
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import { plainToClass } from 'class-transformer';
|
2
|
+
import { IsEnum, IsNumber, IsBoolean, IsString, validateSync, NotContains } from 'class-validator';
|
3
|
+
|
4
|
+
enum EnvironmentType {
|
5
|
+
Dev = 'development',
|
6
|
+
Prod = 'production',
|
7
|
+
Test = 'test',
|
8
|
+
Staging = 'staging',
|
9
|
+
}
|
10
|
+
|
11
|
+
class EnvironmentVariables {
|
12
|
+
@IsEnum(EnvironmentType)
|
13
|
+
NODE_ENV: EnvironmentType;
|
14
|
+
|
15
|
+
@IsString()
|
16
|
+
MYSQL_HOST: string;
|
17
|
+
|
18
|
+
@IsNumber()
|
19
|
+
MYSQL_PORT: number;
|
20
|
+
|
21
|
+
@IsString()
|
22
|
+
@NotContains(' ')
|
23
|
+
MYSQL_DATABASE: string;
|
24
|
+
|
25
|
+
@IsString()
|
26
|
+
MYSQL_USERNAME: string;
|
27
|
+
|
28
|
+
@IsString()
|
29
|
+
MYSQL_PASSWORD: string;
|
30
|
+
|
31
|
+
@IsBoolean()
|
32
|
+
MYSQL_SYNCHRONIZE: boolean;
|
33
|
+
|
34
|
+
@IsNumber()
|
35
|
+
HTTP_TIMEOUT: number;
|
36
|
+
|
37
|
+
@IsNumber()
|
38
|
+
HTTP_MAX_REDIRECTS: number;
|
39
|
+
|
40
|
+
@IsString()
|
41
|
+
BASE_MOTION_URL: string;
|
42
|
+
|
43
|
+
@IsString()
|
44
|
+
ACTOR_SERVICE_URL: string;
|
45
|
+
}
|
46
|
+
|
47
|
+
export function validate(configuration: Record<string, unknown>) {
|
48
|
+
const finalConfig = plainToClass(EnvironmentVariables, configuration, {
|
49
|
+
enableImplicitConversion: true,
|
50
|
+
});
|
51
|
+
|
52
|
+
const errors = validateSync(finalConfig, { skipMissingProperties: true });
|
53
|
+
|
54
|
+
if (errors.length > 0) {
|
55
|
+
throw new Error(errors.toString());
|
56
|
+
}
|
57
|
+
|
58
|
+
return finalConfig;
|
59
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { Repository, EntityTarget, EntityManager, Equal, FindOptionsWhere, ObjectLiteral, DeepPartial } from 'typeorm';
|
2
|
+
import { Injectable } from '@nestjs/common';
|
3
|
+
|
4
|
+
interface IId {
|
5
|
+
id: number;
|
6
|
+
}
|
7
|
+
|
8
|
+
interface IGenericRepository<E> {
|
9
|
+
save<T extends DeepPartial<E>>(entity: T): Promise<E>;
|
10
|
+
update<T extends DeepPartial<E> & IId>(entity: T): Promise<E>;
|
11
|
+
count(options: FindOptionsWhere<E>): Promise<number>;
|
12
|
+
}
|
13
|
+
|
14
|
+
@Injectable()
|
15
|
+
export class GenericRepository<E extends ObjectLiteral> implements IGenericRepository<E> {
|
16
|
+
protected repository: Repository<E>;
|
17
|
+
|
18
|
+
constructor(
|
19
|
+
readonly entityManager: EntityManager,
|
20
|
+
entity: EntityTarget<E>,
|
21
|
+
) {
|
22
|
+
this.repository = entityManager.getRepository(entity);
|
23
|
+
}
|
24
|
+
save<T extends DeepPartial<E>>(entity: T): Promise<E> {
|
25
|
+
const res = this.repository.create({ ...entity });
|
26
|
+
return this.repository.save(res);
|
27
|
+
}
|
28
|
+
|
29
|
+
async update<T extends DeepPartial<E> & IId>(dto: T): Promise<E> {
|
30
|
+
const entity = await this.repository.findOneBy({
|
31
|
+
id: Equal(dto.id),
|
32
|
+
} as unknown as FindOptionsWhere<E>);
|
33
|
+
if (!entity) {
|
34
|
+
throw new Error('Entity not found');
|
35
|
+
}
|
36
|
+
const entityUpdated = await this.repository.save({ id: entity.id, ...dto });
|
37
|
+
return Object.assign(entity, entityUpdated);
|
38
|
+
}
|
39
|
+
|
40
|
+
count(options: FindOptionsWhere<E>): Promise<number> {
|
41
|
+
return this.repository.count(options);
|
42
|
+
}
|
43
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Creates a parameter decorator that retrieves the metadata from the execution context.
|
5
|
+
*
|
6
|
+
* @param data - Additional data passed to the decorator (unused)
|
7
|
+
* @param ctx - The execution context
|
8
|
+
* @returns The metadata from the execution context
|
9
|
+
*/
|
10
|
+
export const GetMeta = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
|
11
|
+
const context = ctx.switchToRpc();
|
12
|
+
return context.getContext();
|
13
|
+
});
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Creates a parameter decorator that retrieves the trace ID from the execution context.
|
17
|
+
* The 'traceId' key should be used when setting this value in the metadata.
|
18
|
+
*
|
19
|
+
* @param data - Additional data passed to the decorator (unused)
|
20
|
+
* @param ctx - The execution context
|
21
|
+
* @returns The trace ID
|
22
|
+
*/
|
23
|
+
export const GetTraceId = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
|
24
|
+
const context = ctx.switchToRpc();
|
25
|
+
const metadata = context.getContext();
|
26
|
+
const traceId = metadata.get('traceId')[0];
|
27
|
+
return traceId; // 'traceId' should match the key used when setting this value in metadata
|
28
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
2
|
+
import { Observable } from 'rxjs';
|
3
|
+
import { tap } from 'rxjs/operators';
|
4
|
+
import { WinstonLoggerService } from '../modules/logger/winston.service';
|
5
|
+
// import { CustomLoggerService } from '../modules/logger/winston.service';
|
6
|
+
@Injectable()
|
7
|
+
export class LogRequestInterceptor implements NestInterceptor {
|
8
|
+
constructor(private readonly logger: WinstonLoggerService) {}
|
9
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
10
|
+
const req = context.switchToHttp().getRequest();
|
11
|
+
this.logger.info('Before Middleware...', {
|
12
|
+
traceId: req?.local?.traceId,
|
13
|
+
className: 'LogRequestInterceptor',
|
14
|
+
});
|
15
|
+
const now = Date.now();
|
16
|
+
|
17
|
+
if (req) {
|
18
|
+
const method = req.method;
|
19
|
+
const url = req.url;
|
20
|
+
return next.handle().pipe(
|
21
|
+
tap(() =>
|
22
|
+
this.logger.info(`After Middleware... ${method} ${url} ${Date.now() - now}ms`, {
|
23
|
+
traceId: req?.local?.traceId,
|
24
|
+
className: 'LogRequestInterceptor',
|
25
|
+
}),
|
26
|
+
),
|
27
|
+
);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { UseInterceptors, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
2
|
+
import { Observable } from 'rxjs';
|
3
|
+
import { map } from 'rxjs/operators';
|
4
|
+
import { plainToInstance } from 'class-transformer';
|
5
|
+
|
6
|
+
interface ClassConstructor {
|
7
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
8
|
+
new (...args: any[]): {};
|
9
|
+
}
|
10
|
+
|
11
|
+
export function Serialize(dto: ClassConstructor) {
|
12
|
+
return UseInterceptors(new SerializeInterceptor(dto));
|
13
|
+
}
|
14
|
+
|
15
|
+
export class SerializeInterceptor implements NestInterceptor {
|
16
|
+
constructor(private dto: any) {}
|
17
|
+
|
18
|
+
intercept(context: ExecutionContext, handler: CallHandler): Observable<any> {
|
19
|
+
return handler.handle().pipe(
|
20
|
+
map((data: any) => {
|
21
|
+
return plainToInstance(this.dto, data, {
|
22
|
+
excludeExtraneousValues: true,
|
23
|
+
});
|
24
|
+
}),
|
25
|
+
);
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
export const TraceLog = () => {
|
2
|
+
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
3
|
+
const originalMethod = descriptor.value;
|
4
|
+
|
5
|
+
descriptor.value = function (message: string, meta: any) {
|
6
|
+
const traceId = meta?.traceId || 'unknown-trace-id';
|
7
|
+
const className = meta.className;
|
8
|
+
meta = { ...meta, traceId, className };
|
9
|
+
originalMethod.call(this, message, meta);
|
10
|
+
};
|
11
|
+
|
12
|
+
return descriptor;
|
13
|
+
};
|
14
|
+
};
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { ElasticsearchTransportOptions } from 'winston-elasticsearch';
|
2
|
+
import { DatadogTransportOptions } from 'datadog-winston';
|
3
|
+
export interface IWinstonLogger {
|
4
|
+
info(message: string, meta?: Record<string, any>): void;
|
5
|
+
error(message: string, meta?: Record<string, any>): void;
|
6
|
+
warn(message: string, meta?: Record<string, any>): void;
|
7
|
+
help(message: string, meta?: Record<string, any>): void;
|
8
|
+
data(message: string, meta?: Record<string, any>): void;
|
9
|
+
debug(message: string, meta?: Record<string, any>): void;
|
10
|
+
prompt(message: string, meta?: Record<string, any>): void;
|
11
|
+
http(message: string, meta?: Record<string, any>): void;
|
12
|
+
verbose(message: string, meta?: Record<string, any>): void;
|
13
|
+
input(message: string, meta?: Record<string, any>): void;
|
14
|
+
silly(message: string, meta?: Record<string, any>): void;
|
15
|
+
}
|
16
|
+
|
17
|
+
export interface WinstonLoggerModuleOptions {
|
18
|
+
transportFile?: boolean;
|
19
|
+
transportConsole?: boolean;
|
20
|
+
transportElasticsearch?: ElasticsearchTransportOptions;
|
21
|
+
transportDatadog?: DatadogTransportOptions;
|
22
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
// winston.module.ts
|
2
|
+
import { Module, DynamicModule, Global } from '@nestjs/common';
|
3
|
+
import { createLogger, transports, format, Logger } from 'winston';
|
4
|
+
// import * as Elasticsearch from 'winston-elasticsearch';
|
5
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
6
|
+
import { WinstonLoggerService } from './winston.service';
|
7
|
+
import { ElasticsearchTransport } from 'winston-elasticsearch';
|
8
|
+
import DatadogWinston = require('datadog-winston');
|
9
|
+
|
10
|
+
const myFormat = format.printf(({ level = 'info', message, timestamp, err, ...metadata }) => {
|
11
|
+
return `${timestamp} [${level}]: ${message} ${err ? err.stack : ''} ${JSON.stringify(metadata)} `;
|
12
|
+
});
|
13
|
+
|
14
|
+
@Global()
|
15
|
+
@Module({})
|
16
|
+
export class WinstonModule {
|
17
|
+
static forRoot(): DynamicModule {
|
18
|
+
const winstonProvider = {
|
19
|
+
provide: 'WINSTON_LOGGER',
|
20
|
+
useFactory: (configService: ConfigService): Logger => {
|
21
|
+
const transportType = configService.getOrThrow<string>('LOG_TRANSPORTS');
|
22
|
+
// Split the string by commas and check if at least one entry exists.
|
23
|
+
const transportsArray = transportType.split(',');
|
24
|
+
if (transportsArray.length === 0 || transportsArray.every((item) => item.trim() === '')) {
|
25
|
+
throw new Error('LOG_TRANSPORTS must contain at least one transport method.');
|
26
|
+
}
|
27
|
+
const selectedTransport = [];
|
28
|
+
for (const iterator of transportsArray) {
|
29
|
+
if (iterator === 'elasticsearch') {
|
30
|
+
selectedTransport.push(
|
31
|
+
new ElasticsearchTransport({
|
32
|
+
clientOpts: {
|
33
|
+
node: configService.getOrThrow<string>('ELASTICSEARCH_sURL'),
|
34
|
+
},
|
35
|
+
}),
|
36
|
+
);
|
37
|
+
}
|
38
|
+
if (iterator === 'datadog') {
|
39
|
+
selectedTransport.push(
|
40
|
+
new DatadogWinston({
|
41
|
+
apiKey: configService.getOrThrow<string>('DATADOG_APIKEY'),
|
42
|
+
hostname: configService.getOrThrow<string>('DATADOG_HOSTNAME'),
|
43
|
+
service: configService.getOrThrow<string>('DATADOG_SERVICE'), // 'service',
|
44
|
+
ddsource: configService.getOrThrow<string>('DATADOG_DDSOURCE'), // 'nodejs',
|
45
|
+
ddtags: configService.getOrThrow<string>('DATADOG_DDTAGS'), //'foo:bar,boo:baz',
|
46
|
+
intakeRegion: configService.getOrThrow<string>('DATADOG_INTAKEREGION'), // 'eu'
|
47
|
+
}),
|
48
|
+
);
|
49
|
+
}
|
50
|
+
if (iterator === 'file') {
|
51
|
+
selectedTransport.push(
|
52
|
+
new transports.File({
|
53
|
+
filename: 'logs/error.log',
|
54
|
+
level: 'error',
|
55
|
+
format: format.combine(format.timestamp(), format.json()),
|
56
|
+
}),
|
57
|
+
new transports.File({
|
58
|
+
filename: 'logs/combined.log',
|
59
|
+
format: format.combine(format.timestamp(), format.json(), myFormat),
|
60
|
+
}),
|
61
|
+
);
|
62
|
+
}
|
63
|
+
if (iterator === 'console') {
|
64
|
+
selectedTransport.push(
|
65
|
+
new transports.Console({
|
66
|
+
format: format.combine(
|
67
|
+
format.colorize(),
|
68
|
+
format.splat(),
|
69
|
+
format.errors({ stack: true }),
|
70
|
+
format.json(),
|
71
|
+
format.timestamp({
|
72
|
+
format: 'YYYY-MM-DD HH:mm:ss',
|
73
|
+
}),
|
74
|
+
myFormat,
|
75
|
+
),
|
76
|
+
}),
|
77
|
+
);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
return createLogger({
|
82
|
+
level: configService.get<string>('LOG_LEVEL') || 'info',
|
83
|
+
defaultMeta: {
|
84
|
+
service: configService.getOrThrow<string>('NAME_SERVICE'),
|
85
|
+
},
|
86
|
+
transports: selectedTransport,
|
87
|
+
});
|
88
|
+
},
|
89
|
+
inject: [ConfigService],
|
90
|
+
};
|
91
|
+
|
92
|
+
const loggerServiceProvider = {
|
93
|
+
provide: WinstonLoggerService,
|
94
|
+
useFactory: (logger: Logger) => new WinstonLoggerService(logger),
|
95
|
+
inject: ['WINSTON_LOGGER'],
|
96
|
+
};
|
97
|
+
|
98
|
+
return {
|
99
|
+
module: WinstonModule,
|
100
|
+
imports: [ConfigModule],
|
101
|
+
providers: [winstonProvider, loggerServiceProvider],
|
102
|
+
exports: [winstonProvider, loggerServiceProvider],
|
103
|
+
};
|
104
|
+
}
|
105
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
// winston-logger.service.ts
|
2
|
+
import { Logger } from 'winston';
|
3
|
+
import { IWinstonLogger } from './winston.interface';
|
4
|
+
import { TraceLog } from './winston.decorator';
|
5
|
+
|
6
|
+
export interface Meta {
|
7
|
+
traceId: string; // Ensures traceId is always present
|
8
|
+
className?: string;
|
9
|
+
methodName?: string;
|
10
|
+
[key: string]: any; // Allows for any number of additional properties
|
11
|
+
}
|
12
|
+
export class WinstonLoggerService implements IWinstonLogger {
|
13
|
+
constructor(private readonly logger: Logger) {}
|
14
|
+
|
15
|
+
@TraceLog()
|
16
|
+
info(message: string, meta: Meta): void {
|
17
|
+
this.logger.info(message, meta);
|
18
|
+
}
|
19
|
+
@TraceLog()
|
20
|
+
warn(message: string, meta: Meta): void {
|
21
|
+
this.logger.warn(message, meta);
|
22
|
+
}
|
23
|
+
@TraceLog()
|
24
|
+
help(message: string, meta: Meta): void {
|
25
|
+
this.logger.help(message, meta);
|
26
|
+
}
|
27
|
+
@TraceLog()
|
28
|
+
data(message: string, meta: Meta): void {
|
29
|
+
this.logger.data(message, meta);
|
30
|
+
}
|
31
|
+
@TraceLog()
|
32
|
+
debug(message: string, meta: Meta): void {
|
33
|
+
this.logger.debug(message, meta);
|
34
|
+
}
|
35
|
+
prompt(message: string, meta: Meta): void {
|
36
|
+
this.logger.prompt(message, meta);
|
37
|
+
}
|
38
|
+
@TraceLog()
|
39
|
+
http(message: string, meta: Meta): void {
|
40
|
+
this.logger.http(message, meta);
|
41
|
+
}
|
42
|
+
@TraceLog()
|
43
|
+
verbose(message: string, meta: Meta): void {
|
44
|
+
this.logger.verbose(message, meta);
|
45
|
+
}
|
46
|
+
@TraceLog()
|
47
|
+
input(message: string, meta: Meta): void {
|
48
|
+
this.logger.input(message, meta);
|
49
|
+
}
|
50
|
+
@TraceLog()
|
51
|
+
silly(message: string, meta: Meta): void {
|
52
|
+
this.logger.silly(message, meta);
|
53
|
+
}
|
54
|
+
@TraceLog()
|
55
|
+
error(message: string, meta: Meta): void {
|
56
|
+
this.logger.error(message, meta);
|
57
|
+
}
|
58
|
+
|
59
|
+
// Add other methods for different logging levels as needed
|
60
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { Inject, Injectable, NestMiddleware } from '@nestjs/common';
|
2
|
+
import { randomUUID } from 'crypto';
|
3
|
+
import { NextFunction, Request, Response } from 'express';
|
4
|
+
export const X_REQUEST_ID_HEADER = 'X-Request-ID';
|
5
|
+
export const X_RESPONSE_ID_HEADER = 'X-Response-ID';
|
6
|
+
import { ConfigurableModuleBuilder } from '@nestjs/common';
|
7
|
+
import { RouteInfo } from '@nestjs/common/interfaces';
|
8
|
+
|
9
|
+
interface CtxLocal {
|
10
|
+
local: CtxLocalTraceID;
|
11
|
+
}
|
12
|
+
interface CtxLocalTraceID {
|
13
|
+
traceId: string;
|
14
|
+
}
|
15
|
+
type CtxLocalRequest = Request & CtxLocal;
|
16
|
+
|
17
|
+
export interface TracingModuleOptions {
|
18
|
+
routes: (string | RouteInfo)[];
|
19
|
+
excludedRoutes?: (string | RouteInfo)[];
|
20
|
+
onRequest?(uuid: string, next: NextFunction): void;
|
21
|
+
}
|
22
|
+
|
23
|
+
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, ASYNC_OPTIONS_TYPE } =
|
24
|
+
new ConfigurableModuleBuilder<TracingModuleOptions>().build();
|
25
|
+
|
26
|
+
@Injectable()
|
27
|
+
export class TracerMiddleware implements NestMiddleware {
|
28
|
+
constructor(
|
29
|
+
@Inject(MODULE_OPTIONS_TOKEN)
|
30
|
+
protected readonly options: typeof OPTIONS_TYPE,
|
31
|
+
) {}
|
32
|
+
|
33
|
+
async use(req: CtxLocalRequest & CtxLocal, res: Response, next: NextFunction) {
|
34
|
+
const { onRequest = (_, next) => next() } = this.options;
|
35
|
+
const uuid = req.header(X_REQUEST_ID_HEADER) ?? randomUUID();
|
36
|
+
req.local = { traceId: uuid };
|
37
|
+
res.setHeader(X_RESPONSE_ID_HEADER, uuid);
|
38
|
+
onRequest(uuid, next);
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Inject, MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
2
|
+
import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, TracerMiddleware } from './tracer.middleware';
|
3
|
+
|
4
|
+
@Module({})
|
5
|
+
export class TracerModule extends ConfigurableModuleClass implements NestModule {
|
6
|
+
constructor(
|
7
|
+
@Inject(MODULE_OPTIONS_TOKEN)
|
8
|
+
protected readonly options: typeof OPTIONS_TYPE,
|
9
|
+
) {
|
10
|
+
super();
|
11
|
+
}
|
12
|
+
|
13
|
+
configure(consumer: MiddlewareConsumer) {
|
14
|
+
const config = consumer.apply(TracerMiddleware);
|
15
|
+
if (this.options.excludedRoutes) config.exclude(...this.options.excludedRoutes);
|
16
|
+
config.forRoutes(...this.options.routes);
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './auth';
|
package/tsconfig.json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"module": "commonjs",
|
4
|
+
"declaration": true,
|
5
|
+
"removeComments": true,
|
6
|
+
"emitDecoratorMetadata": true,
|
7
|
+
"experimentalDecorators": true,
|
8
|
+
"allowSyntheticDefaultImports": true,
|
9
|
+
"target": "ES2021",
|
10
|
+
"sourceMap": true,
|
11
|
+
"outDir": "./dist",
|
12
|
+
"baseUrl": "./",
|
13
|
+
"incremental": true,
|
14
|
+
"skipLibCheck": true,
|
15
|
+
"strictNullChecks": false,
|
16
|
+
"noImplicitAny": false,
|
17
|
+
"strictBindCallApply": false,
|
18
|
+
"forceConsistentCasingInFileNames": false,
|
19
|
+
"noFallthroughCasesInSwitch": false
|
20
|
+
}
|
21
|
+
}
|