xcally-nest-library 0.0.1
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/.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
|
+
}
|