@rsdk/core 4.2.4 → 4.3.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 (102) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/index.d.ts +1 -2
  3. package/dist/index.js +1 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/noop.http-adapter.d.ts +2 -0
  6. package/dist/noop.http-adapter.js +6 -0
  7. package/dist/noop.http-adapter.js.map +1 -1
  8. package/dist/platform.module.d.ts +3 -3
  9. package/dist/platform.module.js +23 -23
  10. package/dist/platform.module.js.map +1 -1
  11. package/dist/tracing/decorators/span.decorator.js +5 -6
  12. package/dist/tracing/decorators/span.decorator.js.map +1 -1
  13. package/dist/tracing/index.d.ts +0 -2
  14. package/dist/tracing/index.js +0 -2
  15. package/dist/tracing/index.js.map +1 -1
  16. package/dist/tracing/services/index.d.ts +0 -1
  17. package/dist/tracing/services/index.js +0 -1
  18. package/dist/tracing/services/index.js.map +1 -1
  19. package/dist/tracing/services/trace.injector.d.ts +4 -4
  20. package/dist/tracing/services/trace.injector.js +38 -68
  21. package/dist/tracing/services/trace.injector.js.map +1 -1
  22. package/dist/tracing/tracing.interceptor.d.ts +3 -5
  23. package/dist/tracing/tracing.interceptor.js +15 -91
  24. package/dist/tracing/tracing.interceptor.js.map +1 -1
  25. package/dist/tracing/tracing.module.d.ts +4 -16
  26. package/dist/tracing/tracing.module.js +18 -82
  27. package/dist/tracing/tracing.module.js.map +1 -1
  28. package/dist/tracing/types.d.ts +11 -0
  29. package/dist/tracing/types.js +23 -0
  30. package/dist/tracing/types.js.map +1 -0
  31. package/dist/tracing/utils/save-async-hooks-context.js +2 -3
  32. package/dist/tracing/utils/save-async-hooks-context.js.map +1 -1
  33. package/dist/transport/protocol.detector.d.ts +1 -0
  34. package/dist/transport/protocol.detector.js +5 -2
  35. package/dist/transport/protocol.detector.js.map +1 -1
  36. package/dist/transport/transport.module.js +1 -1
  37. package/dist/transport/transport.module.js.map +1 -1
  38. package/dist/types/constants.d.ts +2 -0
  39. package/dist/types/constants.js +6 -0
  40. package/dist/types/constants.js.map +1 -0
  41. package/dist/types/index.d.ts +2 -0
  42. package/dist/types/index.js +5 -1
  43. package/dist/types/index.js.map +1 -1
  44. package/dist/types/plugins.d.ts +5 -0
  45. package/dist/types/plugins.js.map +1 -1
  46. package/dist/types/tracing.headers-extractor.d.ts +15 -0
  47. package/dist/types/tracing.headers-extractor.js +67 -0
  48. package/dist/types/tracing.headers-extractor.js.map +1 -0
  49. package/dist/types/transports.d.ts +6 -1
  50. package/dist/types/transports.js.map +1 -1
  51. package/package.json +13 -12
  52. package/src/index.ts +0 -3
  53. package/src/noop.http-adapter.ts +8 -0
  54. package/src/platform.module.ts +20 -25
  55. package/src/tracing/decorators/span.decorator.ts +6 -6
  56. package/src/tracing/index.ts +0 -2
  57. package/src/tracing/services/index.ts +0 -1
  58. package/src/tracing/services/trace.injector.ts +49 -92
  59. package/src/tracing/tracing.interceptor.ts +7 -127
  60. package/src/tracing/tracing.module.ts +19 -104
  61. package/src/tracing/types.ts +28 -0
  62. package/src/tracing/utils/save-async-hooks-context.ts +2 -3
  63. package/src/transport/protocol.detector.ts +6 -2
  64. package/src/transport/transport.module.ts +1 -1
  65. package/src/types/constants.ts +2 -0
  66. package/src/types/index.ts +2 -0
  67. package/src/types/plugins.ts +6 -0
  68. package/src/types/tracing.headers-extractor.ts +51 -0
  69. package/src/types/transports.ts +8 -1
  70. package/tsconfig.build.json +1 -1
  71. package/dist/tracing/active-span.module.d.ts +0 -5
  72. package/dist/tracing/active-span.module.js +0 -25
  73. package/dist/tracing/active-span.module.js.map +0 -1
  74. package/dist/tracing/auto-instrumentations.config.d.ts +0 -1
  75. package/dist/tracing/auto-instrumentations.config.js +0 -42
  76. package/dist/tracing/auto-instrumentations.config.js.map +0 -1
  77. package/dist/tracing/grpc.headers.d.ts +0 -11
  78. package/dist/tracing/grpc.headers.js +0 -24
  79. package/dist/tracing/grpc.headers.js.map +0 -1
  80. package/dist/tracing/http.headers.d.ts +0 -11
  81. package/dist/tracing/http.headers.js +0 -22
  82. package/dist/tracing/http.headers.js.map +0 -1
  83. package/dist/tracing/open-telemetry.interceptor.d.ts +0 -5
  84. package/dist/tracing/open-telemetry.interceptor.js +0 -83
  85. package/dist/tracing/open-telemetry.interceptor.js.map +0 -1
  86. package/dist/tracing/services/active-span.storage.d.ts +0 -17
  87. package/dist/tracing/services/active-span.storage.js +0 -46
  88. package/dist/tracing/services/active-span.storage.js.map +0 -1
  89. package/dist/tracing/services/instrumentation.service.d.ts +0 -3
  90. package/dist/tracing/services/instrumentation.service.js +0 -25
  91. package/dist/tracing/services/instrumentation.service.js.map +0 -1
  92. package/dist/tracing/services/request-id.provider.d.ts +0 -11
  93. package/dist/tracing/services/request-id.provider.js +0 -13
  94. package/dist/tracing/services/request-id.provider.js.map +0 -1
  95. package/src/tracing/active-span.module.ts +0 -13
  96. package/src/tracing/auto-instrumentations.config.ts +0 -44
  97. package/src/tracing/grpc.headers.ts +0 -29
  98. package/src/tracing/http.headers.ts +0 -32
  99. package/src/tracing/open-telemetry.interceptor.ts +0 -114
  100. package/src/tracing/services/active-span.storage.ts +0 -32
  101. package/src/tracing/services/instrumentation.service.ts +0 -20
  102. package/src/tracing/services/request-id.provider.ts +0 -21
@@ -8,23 +8,19 @@ import { PlatformPluginModule } from './plugin/plugin.module';
8
8
  import { PlatformTransportModule } from './transport/transport.module';
9
9
  import { AppMetadataModule } from './app-metadata';
10
10
  import { PlatformConfigModule } from './config';
11
- import { SequenceException } from './exceptions';
12
11
  import { LoggerInitializingModule } from './logging';
13
12
  import { TracingModule } from './tracing';
14
- import { PlatformExtendedOptions } from './types';
13
+ import type { PlatformAppPlugin, PlatformExtendedOptions } from './types';
14
+ import { APP_PLUGINS, APP_TRANSPORTS } from './types';
15
15
 
16
16
  export class PlatformModule implements NestModule {
17
- constructor(
18
- @Inject('PlatformExtendedOptions') private options: PlatformExtendedOptions,
19
- ) {}
20
-
21
17
  static forRoot(options: PlatformExtendedOptions): DynamicModule {
22
18
  const imports: (DynamicModule | Constructor)[] = [
23
19
  /**
24
20
  * Последовательность принципиально важна так как в этом модуле происходит инициализация глобального асинхронного контекста (AsyncLocalStorage)
25
21
  */
26
22
  AsyncContextModule,
27
- TracingModule.forRoot({ appName: options.name, processing: 'simple' }),
23
+ TracingModule.forRoot(),
28
24
 
29
25
  // Plugins
30
26
  PlatformPluginModule.forOptions(options),
@@ -36,32 +32,31 @@ export class PlatformModule implements NestModule {
36
32
  ...(options.modules ?? []),
37
33
  ];
38
34
 
39
- // Adding transport module if needed
40
- if (options.transports) {
41
- imports.push(PlatformTransportModule.forOptions(options));
42
- }
35
+ imports.push(PlatformTransportModule.forOptions(options));
36
+ const exportingProviders = [
37
+ {
38
+ provide: APP_PLUGINS,
39
+ useValue: new Set(options.plugins),
40
+ },
41
+ {
42
+ provide: APP_TRANSPORTS,
43
+ useValue: new Set(options.transports),
44
+ },
45
+ ];
43
46
 
44
47
  return {
45
48
  module: PlatformModule,
46
49
  imports,
47
- providers: [
48
- {
49
- provide: 'PlatformExtendedOptions',
50
- useValue: options,
51
- },
52
- ],
53
- exports: ['PlatformExtendedOptions'],
50
+ global: true,
51
+ providers: exportingProviders,
52
+ exports: exportingProviders,
54
53
  };
55
54
  }
56
55
 
57
- configure(consumer: MiddlewareConsumer): void {
58
- if (!this.options) {
59
- throw new SequenceException(
60
- 'Should call PlatformModule.setup() before configuring middleware!',
61
- );
62
- }
56
+ constructor(@Inject(APP_PLUGINS) private plugins: Set<PlatformAppPlugin>) {}
63
57
 
64
- for (const plugin of this.options.plugins || []) {
58
+ configure(consumer: MiddlewareConsumer): void {
59
+ for (const plugin of this.plugins) {
65
60
  plugin?.configureMiddleware?.(consumer);
66
61
  }
67
62
  }
@@ -22,13 +22,13 @@ export const Span = (): MethodDecorator & ClassDecorator =>
22
22
  }
23
23
  if (methodName && descriptor) {
24
24
  TraceInjector.wrap(target, descriptor.value, descriptor);
25
- } else {
26
- const prototype = (target as any).prototype ?? target;
25
+ return;
26
+ }
27
+ const prototype = (target as any).prototype ?? target;
27
28
 
28
- for (const name of metadataScanner.getAllMethodNames(prototype)) {
29
- if (name) {
30
- TraceInjector.wrap(prototype, prototype[name]);
31
- }
29
+ for (const name of metadataScanner.getAllMethodNames(prototype)) {
30
+ if (name) {
31
+ TraceInjector.wrap(prototype, prototype[name]);
32
32
  }
33
33
  }
34
34
  };
@@ -1,6 +1,4 @@
1
- export * from './active-span.module';
2
1
  export * from './request-metadata.module';
3
- export * from './services/active-span.storage';
4
2
  export * from './tracing.module';
5
3
  export * from './utils/create-span';
6
4
  export * from './utils/save-async-hooks-context';
@@ -1,2 +1 @@
1
- export * from './instrumentation.service';
2
1
  export * from './trace.injector';
@@ -1,6 +1,5 @@
1
1
  import type { AttributeValue, Span } from '@opentelemetry/api';
2
2
  import { SpanStatusCode, trace } from '@opentelemetry/api';
3
- import { api } from '@opentelemetry/sdk-node';
4
3
  import type { ErrorLike } from '@rsdk/common';
5
4
  import { isPrimitive, isRecord, normalizer } from '@rsdk/common';
6
5
  import { redecorate } from '@rsdk/decorators';
@@ -19,7 +18,6 @@ export class TraceInjector {
19
18
  // eslint-disable-next-line @typescript-eslint/ban-types
20
19
  original: Function,
21
20
  descriptor?: TypedPropertyDescriptor<any>,
22
- isTracingInterceptor?: boolean,
23
21
  ): void {
24
22
  /**
25
23
  * Означает, что данный метод уже обёрнут
@@ -34,11 +32,7 @@ export class TraceInjector {
34
32
  cls.constructor.name,
35
33
  original.name,
36
34
  );
37
- const wrapped = TraceInjector.createWrapper(
38
- original,
39
- spanName,
40
- isTracingInterceptor,
41
- );
35
+ const wrapped = TraceInjector.createWrapper(original, spanName);
42
36
 
43
37
  redecorate(original, wrapped);
44
38
 
@@ -72,50 +66,57 @@ export class TraceInjector {
72
66
  return 'data is undefined';
73
67
  }
74
68
 
75
- private static createWrapper(
76
- original: any,
77
- spanName: string,
78
- _isTracingInterceptor?: boolean,
79
- ): any {
69
+ static enrich(span: Span, data: unknown): unknown {
70
+ span.setAttribute('result', TraceInjector.toAttribute(data));
71
+ span.setStatus({ code: SpanStatusCode.OK });
72
+
73
+ return data;
74
+ }
75
+
76
+ static createSpanName(className: string, methodName: string): string {
77
+ /**
78
+ * Всего лишь правила формирования имени спана (не более того), для новой логики нужен был именно такой код
79
+ */
80
+ return `${className} -> ${methodName}`;
81
+ }
82
+
83
+ static recordAndRethrow(error: unknown, span: Span): void {
84
+ span.recordException(error as ErrorLike);
85
+ span.setStatus({
86
+ code: SpanStatusCode.ERROR,
87
+ message: (error as ErrorLike).message,
88
+ });
89
+
90
+ throw error;
91
+ }
92
+
93
+ private static isWrapped(prototype: object): boolean {
94
+ assert.ok(prototype);
95
+
96
+ return Reflect.hasMetadata(Constants.TRACE_METADATA_ACTIVE, prototype);
97
+ }
98
+
99
+ private static nowSpan(prototype: object): boolean {
100
+ assert.ok(prototype);
101
+
102
+ return Reflect.hasMetadata(Constants.NO_SPAN_METADATA, prototype);
103
+ }
104
+
105
+ private static setWrapped(prototype: object): void {
106
+ assert.ok(prototype);
107
+
108
+ // Value doesn't matter
109
+ const NOOP = 1;
110
+
111
+ Reflect.defineMetadata(Constants.TRACE_METADATA_ACTIVE, NOOP, prototype);
112
+ }
113
+
114
+ private static createWrapper(original: any, spanName: string): any {
80
115
  return {
81
116
  [original.name](...args: any[]): any {
82
117
  const tracer = trace.getTracer('@rsdk/open-telemetry', '1.0.0');
83
118
 
84
- /**
85
- * Переменный для хранения значений которые получаем для опен телеметрии
86
- */
87
- let traceId: string | undefined;
88
- let spanId: string | undefined;
89
-
90
- /**
91
- * Создаем новый спан
92
- */
93
- const span = tracer.startSpan(spanName, {});
94
-
95
- /**
96
- * После создания контекста ранее при входе в приложение, у нас новый спан ид
97
- * мы перетираем значением которе получили через заголовок
98
- * в рамках приложения он корректный, но при переходе из одного приложения в другой - слетает
99
- * и чтобы 100% все было норм, мы всегда патчим его
100
- */
101
- if (spanId) {
102
- span.spanContext().spanId = spanId;
103
- }
104
-
105
- /**
106
- * Патчим трайс ид, причина выше
107
- */
108
- if (traceId) {
109
- span.spanContext().traceId = traceId;
110
- }
111
- /**
112
- * Чтобы пробросить пропатченный спан нужно запустить две строчки ниже:
113
- * 1) создаем контекст в котором активный спан перебиваем новым
114
- * 2) созданный контекст ставим основным и в нем запускаем под процесс
115
- */
116
- const spanContext = api.trace.setSpan(api.context.active(), span);
117
-
118
- return api.context.with(spanContext, async () => {
119
+ return tracer.startActiveSpan(spanName, (span) => {
119
120
  try {
120
121
  /**
121
122
  * При запуске функции обернутой в опентелеметрию мы выводим и трейсИд и спанИд
@@ -127,6 +128,7 @@ export class TraceInjector {
127
128
  }, spanId: ${span.spanContext().spanId}`,
128
129
  );
129
130
 
131
+ span.setAttribute('args', TraceInjector.toAttribute(args));
130
132
  const result = original.apply(this, args);
131
133
 
132
134
  /**
@@ -185,49 +187,4 @@ export class TraceInjector {
185
187
  },
186
188
  }[original.name];
187
189
  }
188
-
189
- static createSpanName(className: string, methodName: string): string {
190
- /**
191
- * Всего лишь правила формирования имени спана (не более того), для новой логики нужен был именно такой код
192
- */
193
- return `${className} -> ${methodName}`;
194
- }
195
-
196
- private static isWrapped(prototype: object): boolean {
197
- assert.ok(prototype);
198
-
199
- return Reflect.hasMetadata(Constants.TRACE_METADATA_ACTIVE, prototype);
200
- }
201
-
202
- private static nowSpan(prototype: object): boolean {
203
- assert.ok(prototype);
204
-
205
- return Reflect.hasMetadata(Constants.NO_SPAN_METADATA, prototype);
206
- }
207
-
208
- private static setWrapped(prototype: object): void {
209
- assert.ok(prototype);
210
-
211
- // Value doesn't matter
212
- const NOOP = 1;
213
-
214
- Reflect.defineMetadata(Constants.TRACE_METADATA_ACTIVE, NOOP, prototype);
215
- }
216
-
217
- static recordAndRethrow(error: unknown, span: Span): void {
218
- span.recordException(error as ErrorLike);
219
- span.setStatus({
220
- code: SpanStatusCode.ERROR,
221
- message: (error as ErrorLike).message,
222
- });
223
-
224
- throw error;
225
- }
226
-
227
- static enrich(span: Span, data: unknown): unknown {
228
- span.setAttribute('response', TraceInjector.toAttribute(data));
229
- span.setStatus({ code: SpanStatusCode.OK });
230
-
231
- return data;
232
- }
233
190
  }
@@ -4,141 +4,21 @@ import type {
4
4
  NestInterceptor,
5
5
  } from '@nestjs/common';
6
6
  import { Injectable } from '@nestjs/common';
7
- import {
8
- X_B3_PARENT_SPAN_ID,
9
- X_B3_SPAN_ID,
10
- X_B3_TRACE_ID,
11
- } from '@opentelemetry/propagator-b3';
12
- import { omitUndefined } from '@rsdk/common.node';
13
- import { randomUUID } from 'node:crypto';
7
+ import { RequestIdProvider } from '@rsdk/tracing';
14
8
  import type { Observable } from 'rxjs';
15
9
 
16
- import {
17
- OpenTelemetryProvider,
18
- RequestIdProvider,
19
- } from './services/request-id.provider';
20
- import { X_REQUEST_ID } from './constants';
21
- import { GrpcHeaders } from './grpc.headers';
22
- import { HttpHeaders } from './http.headers';
10
+ import { TracingHeadersExtractor } from '../types/tracing.headers-extractor';
23
11
 
24
- type TracingHeaders = {
25
- [X_REQUEST_ID]: string;
26
- [X_B3_SPAN_ID]?: string;
27
- [X_B3_TRACE_ID]?: string;
28
- [X_B3_PARENT_SPAN_ID]?: string;
29
- http?: { httpMethod: string; httpUrl: string; httpRoute: string };
30
- };
31
- const tracingHeaders = [
32
- X_REQUEST_ID,
33
- X_B3_TRACE_ID,
34
- X_B3_SPAN_ID,
35
- X_B3_PARENT_SPAN_ID,
36
- ] as const;
12
+ import { X_REQUEST_ID } from './constants';
37
13
 
38
14
  @Injectable()
39
15
  export class TracingInterceptor implements NestInterceptor {
16
+ constructor(private headersExtractor: TracingHeadersExtractor) {}
17
+
40
18
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
41
- const requestId = TracingInterceptor.extractTracingHeaders(context);
19
+ const h = this.headersExtractor.extract(context).getHeaders();
42
20
 
43
- RequestIdProvider.set(requestId[X_REQUEST_ID]);
44
- OpenTelemetryProvider.set(omitUndefined(requestId));
21
+ RequestIdProvider.set(h[X_REQUEST_ID]);
45
22
  return next.handle();
46
23
  }
47
-
48
- private static extractTracingHeaders(
49
- executionContext: ExecutionContext,
50
- ): TracingHeaders {
51
- /**
52
- * В зависимости от типа контекста запускаем парсинг переданной Metadata
53
- */
54
- switch (executionContext.getType<string>()) {
55
- case 'rpc':
56
- return this.extractFromRpc(executionContext);
57
- case 'http':
58
- return this.extractFromHttp(executionContext);
59
- case 'graphql':
60
- return this.extractFromGraphql(executionContext);
61
- }
62
- return {
63
- [X_REQUEST_ID]: this.createRequestId(),
64
- };
65
- }
66
-
67
- private static extractFromGraphql(
68
- executionContext: ExecutionContext,
69
- ): TracingHeaders {
70
- const req = executionContext.getArgs()[2]?.req; // аналог GqlExecutionContext.create(executionContext).getContext().req
71
-
72
- /**
73
- * При работе с сабскрипшен через веб сокет, заголовки передаются в опции подключения к сабскрипшен в переменную connectionParams
74
- * и мы перегоняем эти данные в request заголовки
75
- */
76
- const headers = {
77
- ...req?.headers,
78
- ...req?.connectionParams?.headers,
79
- };
80
-
81
- const extractedHeaders = new HttpHeaders(headers).get([
82
- X_REQUEST_ID,
83
- X_B3_TRACE_ID,
84
- X_B3_SPAN_ID,
85
- ]);
86
-
87
- const h = extractedHeaders as TracingHeaders;
88
-
89
- h.http = {
90
- httpMethod: req.method!,
91
- httpRoute: req.route?.path || req.routeOptions?.url || req.routerPath,
92
- httpUrl: req.originalUrl || req.url,
93
- };
94
-
95
- h[X_REQUEST_ID] ??= randomUUID();
96
- return h as TracingHeaders;
97
- }
98
-
99
- private static extractFromHttp(
100
- executionContext: ExecutionContext,
101
- ): TracingHeaders {
102
- // TODO:
103
- const req = executionContext.switchToHttp().getRequest<any>();
104
-
105
- const headers = new HttpHeaders(req.headers as Record<string, string>).get(
106
- tracingHeaders,
107
- );
108
-
109
- const h: TracingHeaders = headers as TracingHeaders;
110
-
111
- h[X_REQUEST_ID] ??= randomUUID();
112
- h.http = {
113
- httpMethod: req.method!,
114
- httpRoute: req.route?.path || req.routeOptions?.url || req.routerPath,
115
- httpUrl: req.originalUrl || req.url,
116
- };
117
- return headers as TracingHeaders;
118
- }
119
-
120
- private static extractFromRpc(
121
- executionContext: ExecutionContext,
122
- ): TracingHeaders {
123
- const metadata = executionContext.switchToRpc().getContext();
124
- /**
125
- * Если мы пришли в GRPC и у нас есть методы для работы с Metadata
126
- * то мы конвертируем Metadata в некий виртуальный request с заголовками (заголовки выбраны как общий стандарт проброса мета информации)
127
- */
128
- if (metadata?.get) {
129
- const grpcHeaders = new GrpcHeaders(metadata);
130
-
131
- const h = grpcHeaders.get(tracingHeaders);
132
-
133
- h[X_REQUEST_ID] ??= this.createRequestId();
134
- return h as TracingHeaders;
135
- }
136
- return {
137
- [X_REQUEST_ID]: randomUUID(),
138
- };
139
- }
140
-
141
- private static createRequestId(): string {
142
- return randomUUID();
143
- }
144
24
  }
@@ -1,141 +1,56 @@
1
- import type {
2
- BeforeApplicationShutdown,
3
- DynamicModule,
4
- OnModuleInit,
5
- Provider,
6
- } from '@nestjs/common';
1
+ import type { DynamicModule, OnModuleInit } from '@nestjs/common';
7
2
  import { Module } from '@nestjs/common';
8
3
  import { APP_INTERCEPTOR } from '@nestjs/core';
4
+ import type { SpanContext } from '@opentelemetry/api';
9
5
  import { context, trace } from '@opentelemetry/api';
10
- import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
11
- import { CompositePropagator } from '@opentelemetry/core';
12
- import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
13
- import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3';
14
- import { Resource } from '@opentelemetry/resources';
15
- import { NodeSDK } from '@opentelemetry/sdk-node';
16
- import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
17
- import {
18
- BatchSpanProcessor,
19
- SimpleSpanProcessor,
20
- } from '@opentelemetry/sdk-trace-base';
21
- import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
22
6
  import { ILogger, LoggerFactory } from '@rsdk/logging';
7
+ import { RequestIdProvider } from '@rsdk/tracing';
23
8
 
24
9
  import { InjectLogger } from '../logging';
10
+ import { TracingHeadersExtractor } from '../types/tracing.headers-extractor';
25
11
 
26
- import { RequestIdProvider } from './services/request-id.provider';
27
- import { ActiveSpanModule } from './active-span.module';
28
- import { autoInstumentationOptions } from './auto-instrumentations.config';
29
- import { OpenTelemetryInterceptor } from './open-telemetry.interceptor';
30
12
  import { RequestMetadataModule } from './request-metadata.module';
31
- import { InstrumentationService } from './services';
32
13
  import { TracingModuleConfig } from './tracing.config';
33
14
  import { TracingInterceptor } from './tracing.interceptor';
34
15
 
35
- export interface TracingModuleOptions {
36
- appName: string;
37
- processing: 'simple' | 'batch';
38
- exporter?: SpanExporter;
39
- }
40
-
41
16
  @Module({})
42
- export class TracingModule implements OnModuleInit, BeforeApplicationShutdown {
17
+ export class TracingModule implements OnModuleInit {
43
18
  constructor(
44
19
  @InjectLogger(TracingModule) private readonly logger: ILogger,
45
- private readonly instrumentations: InstrumentationService,
46
- private readonly sdk: NodeSDK,
47
20
  private readonly config: TracingModuleConfig,
48
21
  ) {}
49
22
 
50
- static forRoot(options: TracingModuleOptions): DynamicModule {
23
+ static forRoot(): DynamicModule {
51
24
  return {
52
25
  module: TracingModule,
53
- imports: [RequestMetadataModule, ActiveSpanModule],
26
+ imports: [RequestMetadataModule],
54
27
  providers: [
55
- { provide: APP_INTERCEPTOR, useValue: new TracingInterceptor() },
56
- {
57
- provide: APP_INTERCEPTOR,
58
- useValue: new OpenTelemetryInterceptor(),
59
- },
60
- InstrumentationService,
61
- this.createSDKProvider(options),
28
+ { provide: APP_INTERCEPTOR, useClass: TracingInterceptor },
29
+ TracingHeadersExtractor,
62
30
  ],
63
31
  };
64
32
  }
65
33
 
66
34
  // Можно добавить включение и выключение при изменении конфига.
67
35
  async onModuleInit(): Promise<void> {
68
- if (!this.config.enabled) {
69
- this.logger.info('Tracing is disabled');
70
-
71
- LoggerFactory.applyInstrumentations((record) => {
72
- record['request_id'] = RequestIdProvider.get();
73
- });
74
-
75
- return;
76
- }
36
+ LoggerFactory.applyInstrumentations((record) => {
37
+ record['request_id'] = RequestIdProvider.get();
38
+ const span = trace.getSpan(context.active());
39
+ if (!span) {
40
+ return;
41
+ }
42
+ const spanContext: SpanContext = span.spanContext();
77
43
 
44
+ record['trace_id'] = spanContext.traceId;
45
+ record['span_id'] = spanContext.spanId;
46
+ });
78
47
  this.logger.info('Tracing is enabled');
79
48
 
80
- this.logger.debug('Starting Open Telemetry SDK...');
81
- this.sdk.start();
82
-
83
- this.logger.debug('Instrumenting nest.js entities...');
84
- this.instrumentations.injectWrapTraceInjector();
85
-
86
49
  this.logger.debug('Attaching log messages to spans...');
87
- LoggerFactory.applyInstrumentations();
88
50
  LoggerFactory.onMessage((level, msg) => {
89
51
  const span = trace.getSpan(context.active());
90
52
 
91
53
  span?.addEvent('log', { ...msg, level });
92
54
  });
93
55
  }
94
-
95
- private static createSDKProvider(options: TracingModuleOptions): Provider {
96
- return {
97
- provide: NodeSDK,
98
-
99
- inject: [TracingModuleConfig],
100
- useFactory: (config: TracingModuleConfig): NodeSDK => {
101
- const exporter =
102
- options.exporter ||
103
- new OTLPTraceExporter({
104
- url: config.collectorUrl?.toString(),
105
- });
106
-
107
- const processor =
108
- options.processing === 'simple'
109
- ? new SimpleSpanProcessor(exporter)
110
- : new BatchSpanProcessor(exporter);
111
-
112
- return new NodeSDK({
113
- autoDetectResources: true,
114
- contextManager: new AsyncLocalStorageContextManager(),
115
- instrumentations: config.enabled ? [autoInstumentationOptions] : [],
116
- resource: new Resource({
117
- [SemanticResourceAttributes.SERVICE_NAME]: options.appName,
118
- }),
119
- spanProcessor: processor,
120
- textMapPropagator: new CompositePropagator({
121
- propagators: [
122
- new B3Propagator(),
123
- new B3Propagator({
124
- injectEncoding: B3InjectEncoding.MULTI_HEADER,
125
- }),
126
- ],
127
- }),
128
- });
129
- },
130
- };
131
- }
132
-
133
- async beforeApplicationShutdown(): Promise<void> {
134
- if (!this.config.enabled) {
135
- return;
136
- }
137
- this.logger.info('Shutting down Open Telemetry SDK...');
138
- await this.sdk.shutdown();
139
- this.logger.info('Shutting down Open Telemetry SDK has been done!');
140
- }
141
56
  }
@@ -0,0 +1,28 @@
1
+ import { randomUUID } from 'node:crypto';
2
+
3
+ import { X_REQUEST_ID } from './constants';
4
+
5
+ export type Headers = {
6
+ [X_REQUEST_ID]: string;
7
+ };
8
+
9
+ export const tracingHeaders = [X_REQUEST_ID] as const;
10
+
11
+ export class TracingHeaders {
12
+ constructor(headers: Partial<Headers>) {
13
+ this.headers = {
14
+ ...headers,
15
+ [X_REQUEST_ID]: headers[X_REQUEST_ID] ?? this.createRequestId(),
16
+ } as Headers;
17
+ }
18
+
19
+ getHeaders(): Headers {
20
+ return this.headers;
21
+ }
22
+
23
+ private createRequestId(): string {
24
+ return randomUUID();
25
+ }
26
+
27
+ private headers: Headers;
28
+ }
@@ -3,7 +3,6 @@ import { api } from '@opentelemetry/sdk-node';
3
3
  import type { ErrorLike } from '@rsdk/common';
4
4
 
5
5
  import { TraceInjector } from '../services';
6
- import { ActiveSpanStorage } from '../services/active-span.storage';
7
6
 
8
7
  /**
9
8
  * Проблема: при передаче управления в rxjs теряется контекст асинк локал стораджа
@@ -26,10 +25,10 @@ import { ActiveSpanStorage } from '../services/active-span.storage';
26
25
  export function saveAsyncHooksContext<T>(
27
26
  fn: (...args: any[]) => Promise<T>,
28
27
  ): (...args: any[]) => Promise<T> {
29
- const span = ActiveSpanStorage.getInstance()?.getActiveSpan();
28
+ const span = api.trace.getActiveSpan();
30
29
 
31
30
  return async function (...args): Promise<T> {
32
- const activeSpan = ActiveSpanStorage.getInstance()?.getActiveSpan();
31
+ const activeSpan = api.trace.getActiveSpan();
33
32
  if (activeSpan) {
34
33
  Object.assign(activeSpan.spanContext(), span?.spanContext());
35
34
  return fn(...args);
@@ -6,7 +6,7 @@ import type { ITransport } from '../types';
6
6
  export class ProtocolDetector {
7
7
  constructor(private transports: ITransport[]) {}
8
8
 
9
- getProtocol(context: ArgumentsHost): string | undefined {
9
+ getTransport(context: ArgumentsHost): ITransport | undefined {
10
10
  if (this.transports.length === 0) {
11
11
  return;
12
12
  }
@@ -27,6 +27,10 @@ export class ProtocolDetector {
27
27
  if (matched.length === 0) {
28
28
  return;
29
29
  }
30
- return matched[0].tr.getProtocol();
30
+ return matched[0].tr;
31
+ }
32
+
33
+ getProtocol(context: ArgumentsHost): string | undefined {
34
+ return this.getTransport(context)?.getProtocol();
31
35
  }
32
36
  }
@@ -43,7 +43,7 @@ export class PlatformTransportModule {
43
43
  providers: [
44
44
  {
45
45
  provide: ProtocolDetector,
46
- useFactory: () => new ProtocolDetector(options.transports ?? []),
46
+ useValue: new ProtocolDetector(options.transports ?? []),
47
47
  },
48
48
  ],
49
49
  global: true,
@@ -0,0 +1,2 @@
1
+ export const APP_PLUGINS = Symbol('PLUGINS');
2
+ export const APP_TRANSPORTS = Symbol('TRANSPORTS');