@rsdk/core 5.7.0-next.3 → 6.0.0-next.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.
Files changed (261) hide show
  1. package/dist/config/index.d.ts +0 -1
  2. package/dist/config/index.js +0 -1
  3. package/dist/config/index.js.map +1 -1
  4. package/dist/config/metadata/decorators/declare-property.decorator.d.ts +2 -1
  5. package/dist/config/metadata/decorators/declare-property.decorator.js.map +1 -1
  6. package/dist/config/metadata/decorators/property.decorator.d.ts +2 -1
  7. package/dist/config/metadata/decorators/property.decorator.js.map +1 -1
  8. package/dist/config/sources/implementations/json-file.source.js +1 -2
  9. package/dist/config/sources/implementations/json-file.source.js.map +1 -1
  10. package/dist/config/sources/implementations/relodable-json-file.source.js +1 -2
  11. package/dist/config/sources/implementations/relodable-json-file.source.js.map +1 -1
  12. package/dist/config/types.d.ts +1 -20
  13. package/dist/config/types.js.map +1 -1
  14. package/dist/exceptions.handling/default.formatter.d.ts +6 -0
  15. package/dist/exceptions.handling/default.formatter.js +12 -0
  16. package/dist/exceptions.handling/default.formatter.js.map +1 -0
  17. package/dist/exceptions.handling/default.sender.d.ts +8 -0
  18. package/dist/exceptions.handling/default.sender.js +13 -0
  19. package/dist/exceptions.handling/default.sender.js.map +1 -0
  20. package/dist/exceptions.handling/global-exceptions.config.js +1 -1
  21. package/dist/exceptions.handling/global-exceptions.config.js.map +1 -1
  22. package/dist/exceptions.handling/global-exceptions.filter.d.ts +8 -11
  23. package/dist/exceptions.handling/global-exceptions.filter.js +38 -56
  24. package/dist/exceptions.handling/global-exceptions.filter.js.map +1 -1
  25. package/dist/exceptions.handling/global-exceptions.interceptor.d.ts +18 -0
  26. package/dist/exceptions.handling/global-exceptions.interceptor.js +57 -0
  27. package/dist/exceptions.handling/global-exceptions.interceptor.js.map +1 -0
  28. package/dist/exceptions.handling/global-exceptions.module.d.ts +2 -23
  29. package/dist/exceptions.handling/global-exceptions.module.js +38 -3
  30. package/dist/exceptions.handling/global-exceptions.module.js.map +1 -1
  31. package/dist/exceptions.handling/types.d.ts +2 -2
  32. package/dist/index.d.ts +31 -2
  33. package/dist/index.js +34 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/logging/constants.d.ts +1 -0
  36. package/dist/logging/constants.js +5 -0
  37. package/dist/logging/constants.js.map +1 -0
  38. package/dist/logging/decorators/inject-logger.decorator.js +2 -2
  39. package/dist/logging/decorators/inject-logger.decorator.js.map +1 -1
  40. package/dist/logging/formatters/default.formatter.d.ts +7 -0
  41. package/dist/logging/formatters/default.formatter.js +17 -0
  42. package/dist/logging/formatters/default.formatter.js.map +1 -0
  43. package/dist/logging/formatters/index.d.ts +2 -0
  44. package/dist/{config/parsers/array → logging/formatters}/index.js +2 -1
  45. package/dist/logging/formatters/index.js.map +1 -0
  46. package/dist/logging/formatters/interfaces.d.ts +23 -0
  47. package/dist/logging/formatters/interfaces.js +3 -0
  48. package/dist/logging/formatters/interfaces.js.map +1 -0
  49. package/dist/logging/index.d.ts +3 -2
  50. package/dist/logging/index.js +6 -4
  51. package/dist/logging/index.js.map +1 -1
  52. package/dist/logging/logging-core.module.d.ts +10 -0
  53. package/dist/logging/{logger-initializing.module.js → logging-core.module.js} +46 -10
  54. package/dist/logging/logging-core.module.js.map +1 -0
  55. package/dist/logging/{logging.module.d.ts → logging-instances.module.d.ts} +1 -1
  56. package/dist/logging/{logging.module.js → logging-instances.module.js} +8 -9
  57. package/dist/logging/logging-instances.module.js.map +1 -0
  58. package/dist/logging/logging.config.d.ts +3 -0
  59. package/dist/logging/logging.config.js +29 -4
  60. package/dist/logging/logging.config.js.map +1 -1
  61. package/dist/logging/logging.interceptor.d.ts +37 -0
  62. package/dist/logging/logging.interceptor.js +124 -0
  63. package/dist/logging/logging.interceptor.js.map +1 -0
  64. package/dist/metrics/metrics.config.js +3 -2
  65. package/dist/metrics/metrics.config.js.map +1 -1
  66. package/dist/platform.context.d.ts +1 -1
  67. package/dist/platform.context.js +3 -2
  68. package/dist/platform.context.js.map +1 -1
  69. package/dist/platform.module.js +19 -6
  70. package/dist/platform.module.js.map +1 -1
  71. package/dist/tracing/constants.d.ts +4 -5
  72. package/dist/tracing/constants.js +9 -7
  73. package/dist/tracing/constants.js.map +1 -1
  74. package/dist/tracing/index.d.ts +1 -0
  75. package/dist/tracing/index.js +1 -0
  76. package/dist/tracing/index.js.map +1 -1
  77. package/dist/tracing/request-id/index.d.ts +4 -0
  78. package/dist/{config/parsers/enum → tracing/request-id}/index.js +4 -1
  79. package/dist/tracing/request-id/index.js.map +1 -0
  80. package/dist/tracing/request-id/metadata.helpers.d.ts +13 -0
  81. package/dist/tracing/request-id/metadata.helpers.js +23 -0
  82. package/dist/tracing/request-id/metadata.helpers.js.map +1 -0
  83. package/dist/tracing/request-id/request-id-gen.decorator.d.ts +8 -0
  84. package/dist/tracing/request-id/request-id-gen.decorator.js +17 -0
  85. package/dist/tracing/request-id/request-id-gen.decorator.js.map +1 -0
  86. package/dist/tracing/request-id/types.d.ts +2 -0
  87. package/dist/tracing/request-id/types.js +3 -0
  88. package/dist/tracing/request-id/types.js.map +1 -0
  89. package/dist/tracing/request-id/with-prefix.fn.d.ts +30 -0
  90. package/dist/tracing/request-id/with-prefix.fn.js +36 -0
  91. package/dist/tracing/request-id/with-prefix.fn.js.map +1 -0
  92. package/dist/tracing/tracing.config.d.ts +5 -0
  93. package/dist/tracing/tracing.config.js +42 -0
  94. package/dist/tracing/tracing.config.js.map +1 -0
  95. package/dist/tracing/tracing.interceptor.d.ts +7 -9
  96. package/dist/tracing/tracing.interceptor.js +30 -28
  97. package/dist/tracing/tracing.interceptor.js.map +1 -1
  98. package/dist/tracing/tracing.module.d.ts +13 -2
  99. package/dist/tracing/tracing.module.js +60 -17
  100. package/dist/tracing/tracing.module.js.map +1 -1
  101. package/dist/transport/protocol.detector.d.ts +1 -1
  102. package/dist/transport/protocol.detector.js +17 -2
  103. package/dist/transport/protocol.detector.js.map +1 -1
  104. package/dist/transport/transport.module.d.ts +0 -1
  105. package/dist/transport/transport.module.js +0 -20
  106. package/dist/transport/transport.module.js.map +1 -1
  107. package/dist/types/plugins.d.ts +2 -15
  108. package/dist/types/plugins.js.map +1 -1
  109. package/dist/types/transports.d.ts +23 -13
  110. package/dist/types/transports.js.map +1 -1
  111. package/dist/unhandled-rejection.handler.js +8 -13
  112. package/dist/unhandled-rejection.handler.js.map +1 -1
  113. package/package.json +3 -2
  114. package/src/config/index.ts +0 -1
  115. package/src/config/metadata/decorators/declare-property.decorator.ts +3 -1
  116. package/src/config/metadata/decorators/property.decorator.ts +3 -5
  117. package/src/config/sources/implementations/json-file.source.ts +1 -2
  118. package/src/config/sources/implementations/relodable-json-file.source.ts +1 -2
  119. package/src/config/types.ts +2 -23
  120. package/src/exceptions.handling/default.formatter.ts +11 -0
  121. package/src/exceptions.handling/default.sender.ts +15 -0
  122. package/src/exceptions.handling/global-exceptions.config.ts +2 -2
  123. package/src/exceptions.handling/global-exceptions.filter.ts +45 -77
  124. package/src/exceptions.handling/global-exceptions.interceptor.ts +57 -0
  125. package/src/exceptions.handling/global-exceptions.module.ts +46 -33
  126. package/src/exceptions.handling/types.ts +2 -2
  127. package/src/index.ts +53 -2
  128. package/src/logging/constants.ts +1 -0
  129. package/src/logging/decorators/inject-logger.decorator.ts +2 -2
  130. package/src/logging/formatters/default.formatter.ts +17 -0
  131. package/src/logging/formatters/index.ts +2 -0
  132. package/src/logging/formatters/interfaces.ts +34 -0
  133. package/src/logging/index.ts +3 -2
  134. package/src/logging/logging-core.module.ts +102 -0
  135. package/src/logging/{logging.module.ts → logging-instances.module.ts} +2 -4
  136. package/src/logging/logging.config.ts +22 -6
  137. package/src/logging/logging.interceptor.ts +138 -0
  138. package/src/metrics/metrics.config.ts +3 -1
  139. package/src/platform.context.ts +5 -2
  140. package/src/platform.module.ts +28 -7
  141. package/src/tracing/constants.ts +8 -7
  142. package/src/tracing/index.ts +1 -0
  143. package/src/tracing/request-id/index.ts +4 -0
  144. package/src/tracing/request-id/metadata.helpers.ts +24 -0
  145. package/src/tracing/request-id/request-id-gen.decorator.ts +16 -0
  146. package/src/tracing/request-id/types.ts +6 -0
  147. package/src/tracing/request-id/with-prefix.fn.ts +44 -0
  148. package/src/tracing/tracing.config.ts +23 -0
  149. package/src/tracing/tracing.interceptor.ts +36 -31
  150. package/src/tracing/tracing.module.ts +68 -19
  151. package/src/transport/protocol.detector.ts +24 -4
  152. package/src/transport/transport.module.ts +0 -25
  153. package/src/types/plugins.ts +11 -22
  154. package/src/types/transports.ts +32 -16
  155. package/src/unhandled-rejection.handler.ts +10 -18
  156. package/test/logging.interceptor.spec.ts +292 -0
  157. package/dist/config/parsers/array/array.parser.d.ts +0 -11
  158. package/dist/config/parsers/array/array.parser.js +0 -33
  159. package/dist/config/parsers/array/array.parser.js.map +0 -1
  160. package/dist/config/parsers/array/index.d.ts +0 -1
  161. package/dist/config/parsers/array/index.js.map +0 -1
  162. package/dist/config/parsers/boolean/bool.parser.d.ts +0 -6
  163. package/dist/config/parsers/boolean/bool.parser.js +0 -20
  164. package/dist/config/parsers/boolean/bool.parser.js.map +0 -1
  165. package/dist/config/parsers/boolean/index.d.ts +0 -1
  166. package/dist/config/parsers/boolean/index.js +0 -18
  167. package/dist/config/parsers/boolean/index.js.map +0 -1
  168. package/dist/config/parsers/enum/enum.parser.d.ts +0 -9
  169. package/dist/config/parsers/enum/enum.parser.js +0 -24
  170. package/dist/config/parsers/enum/enum.parser.js.map +0 -1
  171. package/dist/config/parsers/enum/index.d.ts +0 -1
  172. package/dist/config/parsers/enum/index.js.map +0 -1
  173. package/dist/config/parsers/index.d.ts +0 -11
  174. package/dist/config/parsers/index.js +0 -28
  175. package/dist/config/parsers/index.js.map +0 -1
  176. package/dist/config/parsers/int/index.d.ts +0 -1
  177. package/dist/config/parsers/int/index.js +0 -18
  178. package/dist/config/parsers/int/index.js.map +0 -1
  179. package/dist/config/parsers/int/int.parser.d.ts +0 -6
  180. package/dist/config/parsers/int/int.parser.js +0 -20
  181. package/dist/config/parsers/int/int.parser.js.map +0 -1
  182. package/dist/config/parsers/json/index.d.ts +0 -1
  183. package/dist/config/parsers/json/index.js +0 -18
  184. package/dist/config/parsers/json/index.js.map +0 -1
  185. package/dist/config/parsers/json/json.parser.d.ts +0 -6
  186. package/dist/config/parsers/json/json.parser.js +0 -16
  187. package/dist/config/parsers/json/json.parser.js.map +0 -1
  188. package/dist/config/parsers/number/index.d.ts +0 -1
  189. package/dist/config/parsers/number/index.js +0 -18
  190. package/dist/config/parsers/number/index.js.map +0 -1
  191. package/dist/config/parsers/number/number.parser.d.ts +0 -6
  192. package/dist/config/parsers/number/number.parser.js +0 -23
  193. package/dist/config/parsers/number/number.parser.js.map +0 -1
  194. package/dist/config/parsers/path/fspath.parser.d.ts +0 -8
  195. package/dist/config/parsers/path/fspath.parser.js +0 -59
  196. package/dist/config/parsers/path/fspath.parser.js.map +0 -1
  197. package/dist/config/parsers/path/index.d.ts +0 -1
  198. package/dist/config/parsers/path/index.js +0 -18
  199. package/dist/config/parsers/path/index.js.map +0 -1
  200. package/dist/config/parsers/size/index.d.ts +0 -1
  201. package/dist/config/parsers/size/index.js +0 -18
  202. package/dist/config/parsers/size/index.js.map +0 -1
  203. package/dist/config/parsers/size/size.parser.d.ts +0 -7
  204. package/dist/config/parsers/size/size.parser.js +0 -21
  205. package/dist/config/parsers/size/size.parser.js.map +0 -1
  206. package/dist/config/parsers/string/index.d.ts +0 -1
  207. package/dist/config/parsers/string/index.js +0 -18
  208. package/dist/config/parsers/string/index.js.map +0 -1
  209. package/dist/config/parsers/string/string.parser.d.ts +0 -8
  210. package/dist/config/parsers/string/string.parser.js +0 -29
  211. package/dist/config/parsers/string/string.parser.js.map +0 -1
  212. package/dist/config/parsers/timespan/index.d.ts +0 -1
  213. package/dist/config/parsers/timespan/index.js +0 -18
  214. package/dist/config/parsers/timespan/index.js.map +0 -1
  215. package/dist/config/parsers/timespan/timespan.parser.d.ts +0 -7
  216. package/dist/config/parsers/timespan/timespan.parser.js +0 -21
  217. package/dist/config/parsers/timespan/timespan.parser.js.map +0 -1
  218. package/dist/config/parsers/url/exception.d.ts +0 -7
  219. package/dist/config/parsers/url/exception.js +0 -25
  220. package/dist/config/parsers/url/exception.js.map +0 -1
  221. package/dist/config/parsers/url/index.d.ts +0 -2
  222. package/dist/config/parsers/url/index.js +0 -21
  223. package/dist/config/parsers/url/index.js.map +0 -1
  224. package/dist/config/parsers/url/url.parser.d.ts +0 -9
  225. package/dist/config/parsers/url/url.parser.js +0 -38
  226. package/dist/config/parsers/url/url.parser.js.map +0 -1
  227. package/dist/logging/logger-initializing.module.d.ts +0 -6
  228. package/dist/logging/logger-initializing.module.js.map +0 -1
  229. package/dist/logging/logging.module.js.map +0 -1
  230. package/src/config/parsers/array/array.parser.ts +0 -34
  231. package/src/config/parsers/array/index.ts +0 -1
  232. package/src/config/parsers/boolean/bool.parser.ts +0 -20
  233. package/src/config/parsers/boolean/bool.spec.ts +0 -4
  234. package/src/config/parsers/boolean/index.ts +0 -1
  235. package/src/config/parsers/enum/enum.parser.ts +0 -23
  236. package/src/config/parsers/enum/enum.spec.ts +0 -5
  237. package/src/config/parsers/enum/index.ts +0 -1
  238. package/src/config/parsers/index.ts +0 -11
  239. package/src/config/parsers/int/index.ts +0 -1
  240. package/src/config/parsers/int/int.parser.ts +0 -20
  241. package/src/config/parsers/int/int.spec.ts +0 -5
  242. package/src/config/parsers/json/index.ts +0 -1
  243. package/src/config/parsers/json/json.parser.ts +0 -15
  244. package/src/config/parsers/number/index.ts +0 -1
  245. package/src/config/parsers/number/number.parser.ts +0 -23
  246. package/src/config/parsers/number/number.spec.ts +0 -5
  247. package/src/config/parsers/path/fspath.parser.ts +0 -64
  248. package/src/config/parsers/path/index.ts +0 -1
  249. package/src/config/parsers/size/index.ts +0 -1
  250. package/src/config/parsers/size/size.parser.ts +0 -22
  251. package/src/config/parsers/size/size.spec.ts +0 -4
  252. package/src/config/parsers/string/index.ts +0 -1
  253. package/src/config/parsers/string/string.parser.ts +0 -26
  254. package/src/config/parsers/string/string.spec.ts +0 -10
  255. package/src/config/parsers/timespan/index.ts +0 -1
  256. package/src/config/parsers/timespan/timespan.parser.ts +0 -22
  257. package/src/config/parsers/timespan/timespan.spec.ts +0 -5
  258. package/src/config/parsers/url/exception.ts +0 -21
  259. package/src/config/parsers/url/index.ts +0 -2
  260. package/src/config/parsers/url/url.parser.ts +0 -44
  261. package/src/logging/logger-initializing.module.ts +0 -55
@@ -1,4 +1,9 @@
1
- import type { DynamicModule, OnModuleInit } from '@nestjs/common';
1
+ import type {
2
+ DynamicModule,
3
+ FactoryProvider,
4
+ OnModuleInit,
5
+ ValueProvider,
6
+ } from '@nestjs/common';
2
7
  import { Module } from '@nestjs/common';
3
8
  import { APP_INTERCEPTOR } from '@nestjs/core';
4
9
  import type { ContextAPI, TraceAPI } from '@opentelemetry/api';
@@ -6,19 +11,19 @@ import { text } from '@rsdk/common';
6
11
  import { ILogger, LoggerFactory } from '@rsdk/logging';
7
12
 
8
13
  import { InjectLogger } from '../logging';
9
- import type { PlatformOptions } from '../types';
14
+ import type { PlatformOptions, WithGenericHeaders } from '../types';
10
15
 
11
16
  import {
12
- DEFAULT_TRACING_HEADERS,
13
- DEFAULT_TRACING_OPTIONS,
17
+ HEADER_EXTRACTORS,
14
18
  HEADER_MAPPINGS,
19
+ REQUEST_ID_HEADERS,
15
20
  } from './constants';
16
21
  import { TracingHeaders } from './tracing.actx';
22
+ import { TracingConfig } from './tracing.config';
17
23
  import { TracingInterceptor } from './tracing.interceptor';
18
24
  import type { TracingHeaderMappings } from './types';
19
25
 
20
26
  export interface TracingOptions {
21
- includeDefault: boolean;
22
27
  additionalMappings?: TracingHeaderMappings[];
23
28
  }
24
29
 
@@ -30,31 +35,75 @@ export class TracingModule implements OnModuleInit {
30
35
  constructor(@InjectLogger(TracingModule) private readonly logger: ILogger) {}
31
36
 
32
37
  static forRoot({
33
- tracing = DEFAULT_TRACING_OPTIONS,
34
- plugins = [],
38
+ tracing,
39
+ plugins,
40
+ transports,
35
41
  }: PlatformOptions): DynamicModule {
36
- const mappings: TracingHeaderMappings[] = [];
42
+ return {
43
+ global: true,
44
+ module: TracingModule,
45
+ providers: [
46
+ { provide: APP_INTERCEPTOR, useClass: TracingInterceptor },
47
+ this.createHeaderExtractorsProvider(transports, plugins),
48
+ this.createHeaderMappingsProvider(tracing, plugins),
49
+ ],
50
+ };
51
+ }
37
52
 
38
- if (tracing?.includeDefault) {
39
- mappings.push(...DEFAULT_TRACING_HEADERS);
40
- }
53
+ /**
54
+ * Creates a provider for header mappings configuration.
55
+ * Combines default headers, plugin-provided headers and additional custom mappings
56
+ * into a single collection.
57
+ */
58
+ private static createHeaderMappingsProvider(
59
+ options: TracingOptions = {},
60
+ plugins: PlatformOptions['plugins'],
61
+ ): FactoryProvider {
62
+ const mappings: TracingHeaderMappings[] = [];
41
63
 
42
- for (const plugin of plugins) {
64
+ for (const plugin of plugins ?? []) {
43
65
  mappings.push(...(plugin.additionalTracingHeaders?.() ?? []));
44
66
  }
45
67
 
46
- mappings.push(...(tracing.additionalMappings ?? []));
68
+ mappings.push(...(options.additionalMappings ?? []));
47
69
 
48
70
  return {
49
- global: true,
50
- module: TracingModule,
51
- providers: [
52
- { provide: APP_INTERCEPTOR, useClass: TracingInterceptor },
53
- { provide: HEADER_MAPPINGS, useValue: mappings },
54
- ],
71
+ provide: HEADER_MAPPINGS,
72
+ inject: [TracingConfig],
73
+ useFactory: (config: TracingConfig): TracingHeaderMappings[] => {
74
+ if (config.traceRequestId) {
75
+ mappings.push(REQUEST_ID_HEADERS);
76
+ }
77
+
78
+ return mappings;
79
+ },
55
80
  };
56
81
  }
57
82
 
83
+ /**
84
+ * Creates a provider for header extractors from different transport protocols.
85
+ * Collects extractors from both transport implementations and plugins to handle
86
+ * protocol-specific header extraction.
87
+ */
88
+ private static createHeaderExtractorsProvider(
89
+ transports: PlatformOptions['transports'],
90
+ plugins: PlatformOptions['plugins'],
91
+ ): ValueProvider {
92
+ const extractors = new Map<string, WithGenericHeaders>();
93
+
94
+ for (const transport of transports ?? []) {
95
+ extractors.set(transport.getProtocol(), transport);
96
+ }
97
+
98
+ for (const plugin of plugins ?? []) {
99
+ for (const x of plugin.tracingHeadersExtractors?.() ?? []) {
100
+ extractors.set(x.protocol, x.extractor);
101
+ }
102
+ }
103
+
104
+ return { provide: HEADER_EXTRACTORS, useValue: extractors };
105
+ }
106
+
58
107
  // Можно добавить включение и выключение при изменении конфига.
59
108
  async onModuleInit(): Promise<void> {
60
109
  if (!this.context || !this.trace) {
@@ -1,5 +1,6 @@
1
1
  import type { ArgumentsHost } from '@nestjs/common';
2
2
 
3
+ import { InternalException } from '../exceptions';
3
4
  import type { ITransport } from '../types';
4
5
 
5
6
  /**
@@ -12,8 +13,17 @@ export class ProtocolDetector {
12
13
  /**
13
14
  * Detects transport's protocol by ArgumentHost (context)
14
15
  */
15
- getProtocol(context: ArgumentsHost): string | undefined {
16
- return this.getTransport(context)?.getProtocol();
16
+ getProtocol(context: ArgumentsHost): string {
17
+ /**
18
+ * FIXME: Грязный хак, который можно исправить только
19
+ * "прицепив" к транспорту. А ещё учесть, что на http-транспорте
20
+ * может быть и обычный http!
21
+ */
22
+ if ((context.getType() as any) === 'graphql') {
23
+ return 'graphql';
24
+ }
25
+
26
+ return this.getTransport(context).getProtocol();
17
27
  }
18
28
 
19
29
  /**
@@ -24,7 +34,17 @@ export class ProtocolDetector {
24
34
  return this.getProtocol(context) === protocol;
25
35
  }
26
36
 
27
- private getTransport(context: ArgumentsHost): ITransport | undefined {
28
- return this.transports.findLast((x) => x.matchByContext(context));
37
+ private getTransport(context: ArgumentsHost): ITransport {
38
+ const transport = this.transports.findLast((x) =>
39
+ x.matchByContext(context),
40
+ );
41
+
42
+ if (!transport) {
43
+ throw new InternalException('Cant detect transport protocol', {
44
+ details: { context },
45
+ });
46
+ }
47
+
48
+ return transport;
29
49
  }
30
50
  }
@@ -1,9 +1,6 @@
1
1
  import type { DynamicModule } from '@nestjs/common';
2
2
  import type { NestModuleDefinition } from '@rsdk/common.nestjs';
3
- import { flatten } from 'lodash';
4
3
 
5
- import type { IErrorsFormatter, IErrorsSender } from '../exceptions.handling';
6
- import { GlobalExceptionsModule } from '../exceptions.handling';
7
4
  import { HealthModule } from '../health/health.module';
8
5
  import { MetricsModule } from '../metrics';
9
6
  import type { PlatformExtendedOptions } from '../types';
@@ -18,7 +15,6 @@ export class PlatformTransportModule {
18
15
  PlatformTransportModule.initProtocolDetector(options),
19
16
  PlatformTransportModule.initHealthchecks(options),
20
17
  PlatformTransportModule.initMetrics(options),
21
- PlatformTransportModule.initExceptionsHandling(options),
22
18
  ...PlatformTransportModule.getTransportModules(options),
23
19
  ],
24
20
  module: PlatformTransportModule,
@@ -72,25 +68,4 @@ export class PlatformTransportModule {
72
68
  .map((x) => x.getHealthController()),
73
69
  });
74
70
  }
75
-
76
- private static initExceptionsHandling(
77
- options: PlatformExtendedOptions,
78
- ): DynamicModule {
79
- const transports = options.transports ?? [];
80
-
81
- return GlobalExceptionsModule.forRoot({
82
- formatters: [
83
- ...transports.map((x) => x.getErrorsFormatter()),
84
- ...(options.plugins?.map((x) => x.errorFormatter?.()) || []),
85
- ].filter((x): x is IErrorsFormatter => Boolean(x)),
86
- senders: [
87
- ...transports.map((x) => x.getErrorsSender?.()),
88
- ...(options.plugins?.map((x) => x.errorSender?.()) || []),
89
- ].filter((x): x is IErrorsSender => Boolean(x)),
90
- transformers: [
91
- ...flatten(transports.map((x) => x.getErrorTransformers())),
92
- ...flatten(options.plugins?.map((x) => x.errorTransformers?.() || [])),
93
- ],
94
- });
95
- }
96
71
  }
@@ -2,14 +2,15 @@ import type { DynamicModule, MiddlewareConsumer } from '@nestjs/common';
2
2
  import type { Constructor } from '@rsdk/common';
3
3
  import type { ResourceExtractor } from '@rsdk/metadata';
4
4
 
5
- import type {
6
- IErrorsFormatter,
7
- IErrorsSender,
8
- IErrorsTransformer,
9
- } from '../exceptions.handling';
10
5
  import type { TracingHeaderMappings } from '../tracing';
11
6
 
12
- import type { WithGenericHeaders } from './transports';
7
+ import type {
8
+ WithErrorFormatting,
9
+ WithErrorSending,
10
+ WithErrorTransforming,
11
+ WithGenericHeaders,
12
+ WithLogFormatting,
13
+ } from './transports';
13
14
 
14
15
  export type SpecifiedTransports = Readonly<string[]>;
15
16
  export type AppropriateTransports = SpecifiedTransports | 'any';
@@ -26,7 +27,10 @@ export const hasSpecifiedTransports = (
26
27
  */
27
28
  export interface PlatformAppPlugin<
28
29
  T extends AppropriateTransports = AppropriateTransports,
29
- > {
30
+ > extends Partial<WithErrorFormatting>,
31
+ Partial<WithErrorSending>,
32
+ Partial<WithErrorTransforming>,
33
+ Partial<WithLogFormatting> {
30
34
  /**
31
35
  * Additional resource extractors
32
36
  * Maybe used with custom plugin entity
@@ -58,21 +62,6 @@ export interface PlatformAppPlugin<
58
62
  */
59
63
  modules?(): (Constructor | DynamicModule)[];
60
64
 
61
- /**
62
- * Additional error transformers
63
- */
64
- errorTransformers?(): IErrorsTransformer[];
65
-
66
- /**
67
- * Additional error formatter
68
- */
69
- errorFormatter?(): IErrorsFormatter;
70
-
71
- /**
72
- * Additional errors sender
73
- */
74
- errorSender?(): IErrorsSender;
75
-
76
65
  /**
77
66
  * Apply additional middleware (using MiddlewareConsumer).
78
67
  * Will work only if application has HttpAdapter
@@ -14,6 +14,7 @@ import type {
14
14
  IErrorsSender,
15
15
  IErrorsTransformer,
16
16
  } from '../exceptions.handling';
17
+ import type { LogFormatter } from '../logging';
17
18
 
18
19
  import type { NestModuleDefinitions } from './options';
19
20
 
@@ -36,10 +37,40 @@ export interface WithGenericHeaders {
36
37
  export const hasGenericHeaders = (x: unknown): x is WithGenericHeaders =>
37
38
  typeof (x as any)?.getHeaders === 'function';
38
39
 
40
+ export interface WithErrorFormatting {
41
+ errorFormatter(): IErrorsFormatter;
42
+ }
43
+
44
+ export interface WithErrorSending {
45
+ /**
46
+ * @returns sending error algorithm
47
+ */
48
+ errorSender(): IErrorsSender;
49
+ }
50
+
51
+ export interface WithErrorTransforming {
52
+ /**
53
+ * @returns error transformers or empty array
54
+ */
55
+ errorTransformers(): IErrorsTransformer[];
56
+ }
57
+
58
+ export interface WithLogFormatting {
59
+ /**
60
+ * @returns logging formatter
61
+ */
62
+ logFormatter(): LogFormatter;
63
+ }
64
+
39
65
  /**
40
66
  * Base functionality of HTTP or microservice transport
41
67
  */
42
- export interface ITransport extends WithGenericHeaders {
68
+ export interface ITransport
69
+ extends WithGenericHeaders,
70
+ WithErrorFormatting,
71
+ WithErrorTransforming,
72
+ WithLogFormatting,
73
+ Partial<WithErrorSending> {
43
74
  /**
44
75
  * @returns protocol in text format
45
76
  */
@@ -57,21 +88,6 @@ export interface ITransport extends WithGenericHeaders {
57
88
  */
58
89
  matchByContext(ctx: ArgumentsHost): boolean;
59
90
 
60
- /**
61
- * @returns error formatting algorithm
62
- */
63
- getErrorsFormatter(): IErrorsFormatter;
64
-
65
- /**
66
- * @returns error transformers or empty array
67
- */
68
- getErrorTransformers(): IErrorsTransformer[];
69
-
70
- /**
71
- * @returns sending error algorithm
72
- */
73
- getErrorsSender?(): IErrorsSender;
74
-
75
91
  /**
76
92
  * Modules for register metrics, healthchecks, configs and supporting tools, such as clients
77
93
  */
@@ -1,31 +1,23 @@
1
1
  import { text } from '@rsdk/common';
2
2
  import { LoggerFactory } from '@rsdk/logging';
3
3
 
4
- const logger = LoggerFactory.create('PlatformContext');
5
-
6
- if (!process.env.RSDK_CLI_MODE) {
7
- logger.info('Registering unhandled rejections handler...');
8
- }
4
+ const logger = LoggerFactory.create('UnhandledRejectionHandler');
9
5
 
10
6
  // This code enriches output if unhandled exceptions happen
11
- process.on('unhandledRejection', (reason: any, promise) => {
12
- logger.error(
13
- text`
14
- Unhandled promise rejection happened! Process is shutting down
15
- with exit code 1. See: https://github.com/nodejs/node/issues/20392
16
- and https://nodejs.org/api/process.html#event-unhandledrejection
17
- `,
18
- { promise, reason },
19
- );
7
+ process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
8
+ const message = text`
9
+ Unhandled promise rejection happened! Process is shutting down
10
+ with exit code 1. See: https://github.com/nodejs/node/issues/20392
11
+ and https://nodejs.org/api/process.html#event-unhandledrejection
12
+ `;
13
+
14
+ logger.error(message, { promise, reason });
20
15
 
21
16
  /**
22
17
  * Additionally writes to standard console, because both <promise> and <reason>
23
18
  * can be not serializable
24
19
  */
25
- console.error('Found unhandled rejection', {
26
- promise,
27
- reason,
28
- });
20
+ console.error(message, { promise, reason });
29
21
 
30
22
  process.exit(1);
31
23
  });
@@ -0,0 +1,292 @@
1
+ import type { ExecutionContext } from '@nestjs/common';
2
+ import type { ILogger } from '@rsdk/logging';
3
+ import { DEFAULT_LEVEL } from '@rsdk/logging';
4
+ import { ApiZone } from '@rsdk/zones';
5
+ import { of, throwError } from 'rxjs';
6
+
7
+ import type { Config } from '../src/config';
8
+ import { InternalException } from '../src/exceptions';
9
+ import type { LogFormatter } from '../src/logging/formatters';
10
+ import type { LoggingConfig } from '../src/logging/logging.config';
11
+ import { LoggingInterceptor } from '../src/logging/logging.interceptor';
12
+ import type { ProtocolDetector } from '../src/transport/protocol.detector';
13
+
14
+ /**
15
+ * TODO: Надо куда-то вынести, но @rsdk/testing зависит
16
+ * от @rsdk/core, так что возникает цикл. Разобраться!
17
+ */
18
+ export const createMockLogger = (): jest.Mocked<ILogger> => {
19
+ return {
20
+ debug: jest.fn(),
21
+ info: jest.fn(),
22
+ warn: jest.fn(),
23
+ error: jest.fn(),
24
+ fatal: jest.fn(),
25
+ trace: jest.fn(),
26
+ log: jest.fn(),
27
+ };
28
+ };
29
+
30
+ /**
31
+ * TODO: Надо куда-то вынести, но @rsdk/testing зависит
32
+ * от @rsdk/core, так что возникает цикл. Разобраться!
33
+ */
34
+ export const createMockExecutionContext = (): jest.Mocked<ExecutionContext> => {
35
+ return {
36
+ getClass: jest.fn().mockReturnValue(class TestController {}),
37
+ getHandler: jest.fn(),
38
+ getArgs: jest.fn(),
39
+ getArgByIndex: jest.fn(),
40
+ switchToRpc: jest.fn(),
41
+ switchToHttp: jest.fn(),
42
+ switchToWs: jest.fn(),
43
+ getType: jest.fn(),
44
+ };
45
+ };
46
+
47
+ describe('LoggingInterceptor', () => {
48
+ let mockLogger: jest.Mocked<ILogger>;
49
+ let mockDetector: jest.Mocked<ProtocolDetector>;
50
+ let mockFormatter: jest.Mocked<LogFormatter>;
51
+
52
+ const makeInterceptor = (
53
+ configOverrides: Partial<Omit<LoggingConfig, keyof Config>> = {},
54
+ ): LoggingInterceptor => {
55
+ const config: Omit<LoggingConfig, keyof Config> = {
56
+ /**
57
+ * Defaults
58
+ */
59
+ logRequestBody: false,
60
+ logResponseBody: false,
61
+ excludeZones: [],
62
+ level: DEFAULT_LEVEL,
63
+ redact: [],
64
+ sync: false,
65
+ otelEnabled: false,
66
+
67
+ /**
68
+ * Overrides
69
+ */
70
+ ...configOverrides,
71
+ };
72
+
73
+ return new LoggingInterceptor(
74
+ mockLogger,
75
+ new Map([[mockFormatter.protocol, mockFormatter]]),
76
+ mockDetector,
77
+ config as LoggingConfig,
78
+ );
79
+ };
80
+
81
+ /**
82
+ * TODO: Чтобы вот такого не делать, нужно будет убрать базовый класс
83
+ * config в принципе. Это можно сделать, привязывая функциональность
84
+ * обновления и прочего к ссылкам на созданные инстансы
85
+ */
86
+ beforeEach(async () => {
87
+ mockLogger = createMockLogger();
88
+
89
+ mockDetector = {
90
+ getProtocol: jest.fn(),
91
+ } as unknown as jest.Mocked<ProtocolDetector>;
92
+
93
+ mockFormatter = {
94
+ protocol: 'test',
95
+ getPath: jest.fn().mockReturnValue('/test/path'),
96
+ formatRequest: jest.fn().mockReturnValue({}),
97
+ formatResponse: jest.fn().mockReturnValue({}),
98
+ shouldLog: jest.fn().mockReturnValue(true),
99
+ };
100
+ });
101
+
102
+ describe('no error', () => {
103
+ it('should log request and response for non-excluded zone', (done) => {
104
+ // Arrange
105
+ mockDetector.getProtocol.mockReturnValue('test');
106
+ const mockContext = createMockExecutionContext();
107
+ const interceptor = makeInterceptor();
108
+
109
+ // Act & Assert
110
+ interceptor
111
+ .intercept(mockContext, { handle: () => of('response data') })
112
+ .subscribe({
113
+ complete: () => {
114
+ /* eslint-disable prettier/prettier */
115
+ expect(mockLogger.debug).toHaveBeenCalledTimes(2);
116
+ expect(mockLogger.debug).toHaveBeenNthCalledWith(1, '>>> Received [TEST] /test/path', expect.any(Object));
117
+ expect(mockLogger.debug).toHaveBeenNthCalledWith(2, '<<< Handled [TEST] /test/path', expect.objectContaining({
118
+ executionTime: expect.any(Number),
119
+ }));
120
+ /* eslint-enable prettier/prettier */
121
+
122
+ done();
123
+ },
124
+ });
125
+ });
126
+
127
+ it('should not log request and response for excluded zone', (done) => {
128
+ // Arrange
129
+ mockDetector.getProtocol.mockReturnValue('test');
130
+ const mockContext = createMockExecutionContext();
131
+
132
+ // Mock zone metadata
133
+ @ApiZone('excluded-zone')
134
+ class WithExcludedZone {}
135
+
136
+ mockContext.getClass.mockReturnValue(WithExcludedZone);
137
+ jest.spyOn(Reflect, 'getMetadata').mockReturnValue('excluded-zone');
138
+
139
+ const interceptor = makeInterceptor({
140
+ excludeZones: ['excluded-zone'],
141
+ });
142
+
143
+ // Act & Assert
144
+ interceptor
145
+ .intercept(mockContext, { handle: () => of('response data') })
146
+ .subscribe({
147
+ complete: () => {
148
+ expect(mockLogger.debug).toHaveBeenCalledTimes(0);
149
+ done();
150
+ },
151
+ });
152
+ });
153
+
154
+ it('should include request body when logRequestBody is true', (done) => {
155
+ // Arrange
156
+ mockDetector.getProtocol.mockReturnValue('test');
157
+ const mockContext = createMockExecutionContext();
158
+ const interceptor = makeInterceptor({ logRequestBody: true });
159
+
160
+ // Act & Assert
161
+ interceptor
162
+ .intercept(mockContext, { handle: () => of('response data') })
163
+ .subscribe({
164
+ complete: () => {
165
+ expect(mockFormatter.formatRequest).toHaveBeenCalledWith(
166
+ mockContext,
167
+ true,
168
+ );
169
+ done();
170
+ },
171
+ });
172
+ });
173
+
174
+ it('should include response body when logResponseBody is true', (done) => {
175
+ // Arrange
176
+ mockDetector.getProtocol.mockReturnValue('test');
177
+ const mockContext = createMockExecutionContext();
178
+ const interceptor = makeInterceptor({ logResponseBody: true });
179
+
180
+ // Act & Assert
181
+ interceptor
182
+ .intercept(mockContext, { handle: () => of('response data') })
183
+ .subscribe({
184
+ complete: () => {
185
+ expect(mockFormatter.formatResponse).toHaveBeenCalledWith(
186
+ mockContext,
187
+ 'response data',
188
+ );
189
+ done();
190
+ },
191
+ });
192
+ });
193
+
194
+ it('should use default formatter for unknown protocol', (done) => {
195
+ // Arrange
196
+ mockDetector.getProtocol.mockReturnValue('unknown');
197
+ const mockContext = createMockExecutionContext();
198
+ const interceptor = makeInterceptor();
199
+
200
+ // Act & Assert
201
+ interceptor
202
+ .intercept(mockContext, { handle: () => of('response data') })
203
+ .subscribe({
204
+ complete: () => {
205
+ expect(mockLogger.warn).toHaveBeenCalledWith(
206
+ 'no formatter for protocol <unknown>...',
207
+ );
208
+ done();
209
+ },
210
+ });
211
+ });
212
+
213
+ it('should not log if formatter.shouldLog returns false', (done) => {
214
+ // Arrange
215
+ mockDetector.getProtocol.mockReturnValue('test');
216
+ const mockContext = createMockExecutionContext();
217
+
218
+ mockFormatter.shouldLog = jest.fn().mockReturnValue(false);
219
+ const interceptor = makeInterceptor();
220
+
221
+ // Act & Assert
222
+ interceptor
223
+ .intercept(mockContext, { handle: () => of('response data') })
224
+ .subscribe({
225
+ complete: () => {
226
+ expect(mockLogger.debug).not.toHaveBeenCalled();
227
+ done();
228
+ },
229
+ });
230
+ });
231
+ });
232
+
233
+ describe('with error', () => {
234
+ it('should log errors even for excluded zones', (done) => {
235
+ // Arrange
236
+ mockDetector.getProtocol.mockReturnValue('test');
237
+ const mockContext = createMockExecutionContext();
238
+
239
+ // Mock zone metadata
240
+ jest.spyOn(Reflect, 'getMetadata').mockReturnValue('excluded-zone');
241
+
242
+ const interceptor = makeInterceptor({
243
+ excludeZones: ['excluded-zone'],
244
+ });
245
+
246
+ const error = new InternalException('Test error');
247
+
248
+ // Act & Assert
249
+ interceptor
250
+ .intercept(mockContext, { handle: () => throwError(() => error) })
251
+ .subscribe({
252
+ error: () => {
253
+ expect(mockLogger.error).toHaveBeenCalledWith(
254
+ 'Failed [TEST] /test/path',
255
+ expect.objectContaining({
256
+ request: {},
257
+ cause: error,
258
+ path: '/test/path',
259
+ zone: 'excluded-zone',
260
+ }),
261
+ );
262
+ done();
263
+ },
264
+ });
265
+ });
266
+
267
+ it('should log error with request details', (done) => {
268
+ // Arrange
269
+ mockDetector.getProtocol.mockReturnValue('test');
270
+ const mockContext = createMockExecutionContext();
271
+ const error = new InternalException('Test error');
272
+ const interceptor = makeInterceptor();
273
+
274
+ // Act & Assert
275
+ interceptor
276
+ .intercept(mockContext, { handle: () => throwError(() => error) })
277
+ .subscribe({
278
+ error: () => {
279
+ expect(mockLogger.error).toHaveBeenCalledWith(
280
+ 'Failed [TEST] /test/path',
281
+ expect.objectContaining({
282
+ request: '[ALREADY LOGGED]',
283
+ cause: error,
284
+ path: '/test/path',
285
+ }),
286
+ );
287
+ done();
288
+ },
289
+ });
290
+ });
291
+ });
292
+ });
@@ -1,11 +0,0 @@
1
- import type { PropertyParser } from '../../types';
2
- export declare class ArrayParser<T> implements PropertyParser<T[]> {
3
- private readonly singleValueParser;
4
- private readonly options;
5
- constructor(singleValueParser: PropertyParser<T>, options?: {
6
- separator: string;
7
- });
8
- type(): string;
9
- description(): string;
10
- parse(value: unknown): T[];
11
- }