@oas-tools/oas-telemetry 0.7.0-alpha.2 → 0.7.0-alpha.3

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 (154) hide show
  1. package/.env.example +50 -17
  2. package/README.md +242 -240
  3. package/dist/cjs/config/bootConfig.cjs +18 -0
  4. package/dist/cjs/config/config.cjs +145 -0
  5. package/dist/cjs/config/config.types.cjs +5 -0
  6. package/dist/cjs/index.cjs +19 -25
  7. package/dist/cjs/{tlmRoutes.cjs → routesManager.cjs} +28 -21
  8. package/dist/cjs/{exporters/InMemoryLogRecordExporter.cjs → telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs} +42 -19
  9. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +97 -0
  10. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +118 -0
  11. package/dist/cjs/telemetry/custom-implementations/exporters/PluginLogExporter.cjs +52 -0
  12. package/dist/cjs/telemetry/custom-implementations/exporters/PluginMetricExporter.cjs +53 -0
  13. package/dist/cjs/telemetry/custom-implementations/exporters/PluginSpanExporter.cjs +69 -0
  14. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.cjs +70 -0
  15. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.cjs +70 -0
  16. package/dist/cjs/{utils → telemetry/custom-implementations/utils}/circular.cjs +39 -49
  17. package/dist/cjs/telemetry/custom-implementations/wrappers.cjs +175 -0
  18. package/dist/cjs/telemetry/initializeTelemetry.cjs +74 -0
  19. package/dist/cjs/telemetry/telemetryConfigurator.cjs +84 -0
  20. package/dist/cjs/telemetry/telemetryRegistry.cjs +40 -0
  21. package/dist/cjs/tlm-ai/agent.cjs +82 -63
  22. package/dist/cjs/tlm-ai/aiController.cjs +5 -4
  23. package/dist/cjs/tlm-ai/aiRoutes.cjs +9 -6
  24. package/dist/cjs/tlm-ai/tools.cjs +16 -9
  25. package/dist/cjs/tlm-auth/authController.cjs +14 -15
  26. package/dist/cjs/tlm-auth/authMiddleware.cjs +11 -10
  27. package/dist/cjs/tlm-auth/authRoutes.cjs +9 -7
  28. package/dist/cjs/tlm-log/logController.cjs +45 -18
  29. package/dist/cjs/tlm-log/logRoutes.cjs +16 -11
  30. package/dist/cjs/tlm-metric/metricsController.cjs +37 -12
  31. package/dist/cjs/tlm-metric/metricsRoutes.cjs +16 -11
  32. package/dist/cjs/tlm-plugin/pluginController.cjs +40 -19
  33. package/dist/cjs/tlm-plugin/pluginRoutes.cjs +8 -6
  34. package/dist/cjs/tlm-plugin/pluginService.cjs +25 -0
  35. package/dist/cjs/tlm-trace/traceController.cjs +54 -45
  36. package/dist/cjs/tlm-trace/traceRoutes.cjs +16 -11
  37. package/dist/cjs/tlm-ui/uiRoutes.cjs +26 -20
  38. package/dist/cjs/tlm-util/utilController.cjs +8 -9
  39. package/dist/cjs/tlm-util/utilRoutes.cjs +22 -19
  40. package/dist/cjs/types/index.cjs +0 -1
  41. package/dist/cjs/utils/logger.cjs +3 -5
  42. package/dist/cjs/utils/regexUtils.cjs +27 -0
  43. package/dist/esm/config/bootConfig.js +11 -0
  44. package/dist/esm/config/config.js +126 -0
  45. package/dist/esm/index.js +18 -29
  46. package/dist/esm/{tlmRoutes.js → routesManager.js} +27 -22
  47. package/dist/esm/{exporters/InMemoryLogRecordExporter.js → telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js} +31 -17
  48. package/dist/esm/{exporters/InMemoryDBMetricsExporter.js → telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js} +31 -17
  49. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +105 -0
  50. package/dist/esm/telemetry/custom-implementations/exporters/PluginLogExporter.js +44 -0
  51. package/dist/esm/telemetry/custom-implementations/exporters/PluginMetricExporter.js +43 -0
  52. package/dist/esm/telemetry/custom-implementations/exporters/PluginSpanExporter.js +61 -0
  53. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.js +64 -0
  54. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.js +64 -0
  55. package/dist/esm/telemetry/custom-implementations/utils/circular.js +76 -0
  56. package/dist/esm/telemetry/custom-implementations/wrappers.js +163 -0
  57. package/dist/esm/telemetry/initializeTelemetry.js +71 -0
  58. package/dist/esm/telemetry/telemetryConfigurator.js +74 -0
  59. package/dist/esm/telemetry/telemetryRegistry.js +34 -0
  60. package/dist/esm/tlm-ai/agent.js +77 -59
  61. package/dist/esm/tlm-ai/aiController.js +5 -4
  62. package/dist/esm/tlm-ai/aiRoutes.js +7 -5
  63. package/dist/esm/tlm-ai/tools.js +18 -9
  64. package/dist/esm/tlm-auth/authController.js +9 -10
  65. package/dist/esm/tlm-auth/authMiddleware.js +10 -9
  66. package/dist/esm/tlm-auth/authRoutes.js +8 -6
  67. package/dist/esm/tlm-log/logController.js +36 -16
  68. package/dist/esm/tlm-log/logRoutes.js +15 -11
  69. package/dist/esm/tlm-metric/metricsController.js +29 -10
  70. package/dist/esm/tlm-metric/metricsRoutes.js +15 -11
  71. package/dist/esm/tlm-plugin/pluginController.js +40 -19
  72. package/dist/esm/tlm-plugin/pluginRoutes.js +6 -5
  73. package/dist/esm/tlm-plugin/pluginService.js +19 -0
  74. package/dist/esm/tlm-trace/traceController.js +40 -35
  75. package/dist/esm/tlm-trace/traceRoutes.js +15 -11
  76. package/dist/esm/tlm-ui/uiRoutes.js +24 -19
  77. package/dist/esm/tlm-util/utilController.js +8 -9
  78. package/dist/esm/tlm-util/utilRoutes.js +17 -15
  79. package/dist/esm/types/index.js +0 -1
  80. package/dist/esm/utils/logger.js +3 -4
  81. package/dist/esm/utils/regexUtils.js +23 -0
  82. package/dist/types/config/bootConfig.d.ts +5 -0
  83. package/dist/types/config/config.d.ts +253 -0
  84. package/dist/types/config/config.types.d.ts +34 -0
  85. package/dist/types/index.d.ts +5 -4
  86. package/dist/types/routesManager.d.ts +3 -0
  87. package/dist/types/{exporters/InMemoryLogRecordExporter.d.ts → telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts} +6 -6
  88. package/dist/types/{exporters/InMemoryDBMetricsExporter.d.ts → telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts} +7 -7
  89. package/dist/types/{exporters/InMemoryDbExporter.d.ts → telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts} +9 -9
  90. package/dist/types/telemetry/custom-implementations/exporters/PluginLogExporter.d.ts +8 -0
  91. package/dist/types/telemetry/custom-implementations/exporters/PluginMetricExporter.d.ts +12 -0
  92. package/dist/types/telemetry/custom-implementations/exporters/PluginSpanExporter.d.ts +14 -0
  93. package/dist/types/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.d.ts +32 -0
  94. package/dist/types/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.d.ts +34 -0
  95. package/dist/types/telemetry/custom-implementations/utils/circular.d.ts +27 -0
  96. package/dist/types/telemetry/custom-implementations/wrappers.d.ts +52 -0
  97. package/dist/types/telemetry/initializeTelemetry.d.ts +1 -0
  98. package/dist/types/telemetry/telemetryConfigurator.d.ts +2 -0
  99. package/dist/types/telemetry/telemetryRegistry.d.ts +20 -0
  100. package/dist/types/tlm-ai/agent.d.ts +2 -1
  101. package/dist/types/tlm-ai/aiController.d.ts +4 -3
  102. package/dist/types/tlm-ai/aiRoutes.d.ts +2 -2
  103. package/dist/types/tlm-ai/tools.d.ts +3 -1
  104. package/dist/types/tlm-auth/authController.d.ts +4 -3
  105. package/dist/types/tlm-auth/authMiddleware.d.ts +2 -1
  106. package/dist/types/tlm-auth/authRoutes.d.ts +2 -2
  107. package/dist/types/tlm-log/logController.d.ts +1 -0
  108. package/dist/types/tlm-log/logRoutes.d.ts +2 -2
  109. package/dist/types/tlm-metric/metricsController.d.ts +1 -0
  110. package/dist/types/tlm-metric/metricsRoutes.d.ts +2 -2
  111. package/dist/types/tlm-plugin/pluginRoutes.d.ts +1 -2
  112. package/dist/types/tlm-plugin/pluginService.d.ts +9 -0
  113. package/dist/types/tlm-trace/traceController.d.ts +7 -6
  114. package/dist/types/tlm-trace/traceRoutes.d.ts +2 -2
  115. package/dist/types/tlm-ui/uiRoutes.d.ts +1 -2
  116. package/dist/types/tlm-util/utilController.d.ts +2 -1
  117. package/dist/types/tlm-util/utilRoutes.d.ts +2 -2
  118. package/dist/types/types/index.d.ts +7 -46
  119. package/dist/types/utils/regexUtils.d.ts +1 -0
  120. package/dist/ui/assets/index-D9HsRlaQ.js +437 -0
  121. package/dist/ui/assets/index-DEyIcKBi.css +1 -0
  122. package/dist/ui/index.html +3 -3
  123. package/dist/ui/oas-tlm.svg +185 -0
  124. package/package.json +12 -7
  125. package/dist/cjs/config.cjs +0 -31
  126. package/dist/cjs/exporters/InMemoryDBMetricsExporter.cjs +0 -74
  127. package/dist/cjs/exporters/InMemoryDbExporter.cjs +0 -102
  128. package/dist/cjs/exporters/consoleExporter.cjs +0 -47
  129. package/dist/cjs/exporters/dynamicExporter.cjs +0 -57
  130. package/dist/cjs/instrumentation/index.cjs +0 -28
  131. package/dist/cjs/instrumentation/logs.cjs +0 -46
  132. package/dist/cjs/instrumentation/metrics.cjs +0 -27
  133. package/dist/cjs/instrumentation/traces.cjs +0 -19
  134. package/dist/esm/config.js +0 -20
  135. package/dist/esm/exporters/InMemoryDbExporter.js +0 -102
  136. package/dist/esm/exporters/consoleExporter.js +0 -38
  137. package/dist/esm/exporters/dynamicExporter.js +0 -50
  138. package/dist/esm/instrumentation/index.js +0 -26
  139. package/dist/esm/instrumentation/logs.js +0 -34
  140. package/dist/esm/instrumentation/metrics.js +0 -18
  141. package/dist/esm/instrumentation/traces.js +0 -12
  142. package/dist/esm/utils/circular.js +0 -84
  143. package/dist/types/config.d.ts +0 -6
  144. package/dist/types/exporters/consoleExporter.d.ts +0 -13
  145. package/dist/types/exporters/dynamicExporter.d.ts +0 -25
  146. package/dist/types/instrumentation/logs.d.ts +0 -1
  147. package/dist/types/instrumentation/metrics.d.ts +0 -1
  148. package/dist/types/instrumentation/traces.d.ts +0 -1
  149. package/dist/types/tlmRoutes.d.ts +0 -2
  150. package/dist/types/utils/circular.d.ts +0 -31
  151. package/dist/ui/assets/index-BNhZBPi2.css +0 -1
  152. package/dist/ui/assets/index-DxGAMrAl.js +0 -401
  153. package/dist/ui/vite.svg +0 -1
  154. /package/dist/{types/instrumentation/index.d.ts → esm/config/config.types.js} +0 -0
@@ -1,19 +1,24 @@
1
1
  import { ExportResultCode } from '@opentelemetry/core';
2
2
  import dataStore from '@seald-io/nedb';
3
3
  import { applyNesting } from '../utils/circular.js';
4
- export class InMemoryDBMetricsExporter {
5
- constructor() {
6
- this._metrics = new dataStore();
7
- this._stopped = false;
4
+ import { Enabler } from '../wrappers.js';
5
+ import logger from '../../../utils/logger.js';
6
+ export class InMemoryDbMetricExporter extends Enabler {
7
+ constructor(retentionTimeInSeconds = 3600) {
8
+ super();
9
+ this._retentionTimeInSeconds = retentionTimeInSeconds;
10
+ this._metrics = new dataStore({ timestampData: true });
11
+ this._metrics.ensureIndex({ fieldName: 'createdAt' });
12
+ this._startCleanupJob();
8
13
  }
9
14
  export(metrics, resultCallback) {
10
15
  try {
11
- if (!this._stopped) {
16
+ if (this.isEnabled()) {
12
17
  const scopeMetrics = metrics?.scopeMetrics;
13
18
  const cleanMetrics = applyNesting(scopeMetrics);
14
19
  this._metrics.insert(cleanMetrics, (err, _newDoc) => {
15
20
  if (err) {
16
- console.error('Insertion Error:', err);
21
+ logger.error('Insertion Error:', err);
17
22
  return;
18
23
  }
19
24
  });
@@ -21,24 +26,15 @@ export class InMemoryDBMetricsExporter {
21
26
  setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
22
27
  }
23
28
  catch (error) {
24
- console.error('Error exporting metrics\n' + error.message + '\n' + error.stack);
29
+ logger.error('Error exporting metrics\n' + error.message + '\n' + error.stack);
25
30
  return resultCallback({
26
31
  code: ExportResultCode.FAILED,
27
32
  error: new Error('Error exporting metrics\n' + error.message + '\n' + error.stack),
28
33
  });
29
34
  }
30
35
  }
31
- start() {
32
- this._stopped = false;
33
- }
34
- stop() {
35
- this._stopped = true;
36
- }
37
- isRunning() {
38
- return !this._stopped;
39
- }
40
36
  shutdown() {
41
- this._stopped = true;
37
+ this._enabled = false;
42
38
  this._metrics = new dataStore();
43
39
  return this.forceFlush();
44
40
  }
@@ -62,4 +58,22 @@ export class InMemoryDBMetricsExporter {
62
58
  insert(metrics, callback) {
63
59
  this._metrics.insert(metrics, callback);
64
60
  }
61
+ set retentionTimeInSeconds(retentionTimeInSeconds) {
62
+ this._retentionTimeInSeconds = retentionTimeInSeconds;
63
+ logger.info(`InMemoryDbMetricExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
64
+ }
65
+ _startCleanupJob() {
66
+ const interval = 1000;
67
+ setInterval(() => {
68
+ const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
69
+ this._metrics.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
70
+ if (err) {
71
+ logger.error('Error in TTL cleanup:', err);
72
+ }
73
+ else if (numRemoved > 0) {
74
+ logger.debug(`TTL cleanup: removed ${numRemoved} expired metrics`);
75
+ }
76
+ });
77
+ }, interval);
78
+ }
65
79
  }
@@ -0,0 +1,105 @@
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+ import dataStore from '@seald-io/nedb';
3
+ import logger from '../../../utils/logger.js';
4
+ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
5
+ import { Enabler } from '../wrappers.js';
6
+ export class InMemoryDbSpanExporter extends Enabler {
7
+ constructor(retentionTimeInSeconds = 3600) {
8
+ super();
9
+ this._baseUrl = '/telemetry'; // Default base URL, can be overridden by the config
10
+ this._retentionTimeInSeconds = retentionTimeInSeconds;
11
+ this._spans = new dataStore({ timestampData: true });
12
+ this._spans.ensureIndex({ fieldName: 'createdAt' });
13
+ this._startCleanupJob();
14
+ }
15
+ ;
16
+ set baseUrl(baseUrl) {
17
+ this._baseUrl = baseUrl;
18
+ }
19
+ set retentionTimeInSeconds(retentionTimeInSeconds) {
20
+ this._retentionTimeInSeconds = retentionTimeInSeconds;
21
+ logger.info(`InMemoryDbSpanExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
22
+ }
23
+ export(readableSpans, resultCallback) {
24
+ logger.debug('InMemoryDbSpanExporter.export called with spans: ', readableSpans.length);
25
+ try {
26
+ if (!this.isEnabled()) {
27
+ logger.debug('InMemoryDbSpanExporter is not enabled. Skipping export.');
28
+ return resultCallback({ code: ExportResultCode.SUCCESS });
29
+ }
30
+ // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
31
+ const cleanSpans = readableSpans
32
+ .map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
33
+ .map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
34
+ .filter(span => {
35
+ const target = span?.attributes?.http?.target; // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils'
36
+ if (target && target.includes(this._baseUrl)) {
37
+ return target.includes(this._baseUrl + '/utils');
38
+ }
39
+ return true;
40
+ });
41
+ // Insert spans into the in-memory database
42
+ this._spans.insert(cleanSpans, (err, _newDoc) => {
43
+ if (err) {
44
+ logger.error(err);
45
+ return;
46
+ }
47
+ });
48
+ setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
49
+ }
50
+ catch (error) {
51
+ logger.error('Error exporting spans\n' + error.message + '\n' + error.stack);
52
+ return resultCallback({
53
+ code: ExportResultCode.FAILED,
54
+ error: new Error('Error exporting spans\n' + error.message + '\n' + error.stack),
55
+ });
56
+ }
57
+ }
58
+ ;
59
+ shutdown() {
60
+ this._spans = new dataStore();
61
+ return this.forceFlush();
62
+ }
63
+ ;
64
+ /**
65
+ * Exports any pending spans in the exporter
66
+ */
67
+ forceFlush() {
68
+ return Promise.resolve();
69
+ }
70
+ ;
71
+ //err,docs
72
+ find(search, callback) {
73
+ this._spans.find(search, callback);
74
+ }
75
+ reset() {
76
+ this._spans = new dataStore();
77
+ }
78
+ ;
79
+ getFinishedSpans() {
80
+ return this._spans.getAllData();
81
+ }
82
+ ;
83
+ /**
84
+ * Inserts spans into the in-memory database.
85
+ * @param spans - The spans to insert.
86
+ * @param callback - The callback to execute after insertion.
87
+ */
88
+ insert(spans, callback) {
89
+ this._spans.insert(spans, callback);
90
+ }
91
+ _startCleanupJob() {
92
+ const interval = 1000;
93
+ setInterval(() => {
94
+ const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
95
+ this._spans.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
96
+ if (err) {
97
+ logger.error('Error in TTL cleanup:', err);
98
+ }
99
+ else if (numRemoved > 0) {
100
+ logger.debug(`TTL cleanup: removed ${numRemoved} expired spans`);
101
+ }
102
+ });
103
+ }, interval);
104
+ }
105
+ }
@@ -0,0 +1,44 @@
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+ import logger from '../../../utils/logger.js';
3
+ import { Enabler } from '../wrappers.js';
4
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
5
+ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
6
+ export class PluginLogExporter extends Enabler {
7
+ constructor() {
8
+ super();
9
+ }
10
+ export(logs, resultCallback) {
11
+ logger.debug('PluginLogExporter.export called with logs: ', logs.length);
12
+ try {
13
+ if (!this.isEnabled()) {
14
+ logger.debug('PluginLogExporter is not enabled. Skipping export.');
15
+ return resultCallback({ code: ExportResultCode.SUCCESS });
16
+ }
17
+ const cleanLogs = logs
18
+ .map(log => removeCircularRefs(log))
19
+ .map(log => applyNesting(log));
20
+ pluginService.getPlugins().forEach((pluginResource, i) => {
21
+ if (typeof pluginResource.pluginImplementation.newLog === 'function') {
22
+ cleanLogs.forEach((log) => {
23
+ logger.debug(`Sending log to plugin (Plugin #${i}) <${pluginResource.name}>`);
24
+ pluginResource.pluginImplementation.newLog(log);
25
+ });
26
+ }
27
+ else {
28
+ logger.debug(`Plugin <${pluginResource.name}> does not implement newLog method. Skipping log export.`);
29
+ }
30
+ });
31
+ setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
32
+ }
33
+ catch (error) {
34
+ logger.error('Error exporting logs\n' + error.message + '\n' + error.stack);
35
+ return resultCallback({
36
+ code: ExportResultCode.FAILED,
37
+ error: new Error('Error exporting logs\n' + error.message + '\n' + error.stack),
38
+ });
39
+ }
40
+ }
41
+ shutdown() {
42
+ return Promise.resolve();
43
+ }
44
+ }
@@ -0,0 +1,43 @@
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+ import logger from '../../../utils/logger.js';
3
+ import { Enabler } from '../wrappers.js';
4
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
5
+ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
6
+ export class PluginMetricExporter extends Enabler {
7
+ constructor() {
8
+ super();
9
+ }
10
+ export(metrics, resultCallback) {
11
+ logger.debug('PluginMetricExporter.export called');
12
+ try {
13
+ if (!this.isEnabled()) {
14
+ logger.debug('PluginMetricExporter is not enabled. Skipping export.');
15
+ return resultCallback({ code: ExportResultCode.SUCCESS });
16
+ }
17
+ const cleanMetrics = applyNesting(removeCircularRefs(metrics));
18
+ pluginService.getPlugins().forEach((pluginResource, i) => {
19
+ if (typeof pluginResource.pluginImplementation.newMetric === 'function') {
20
+ logger.debug(`Sending metric to plugin (Plugin #${i}) <${pluginResource.name}>`);
21
+ pluginResource.pluginImplementation.newMetric(cleanMetrics);
22
+ }
23
+ else {
24
+ logger.debug(`Plugin <${pluginResource.name}> does not implement newMetric method. Skipping metric export.`);
25
+ }
26
+ });
27
+ setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
28
+ }
29
+ catch (error) {
30
+ logger.error('Error exporting metrics\n' + error.message + '\n' + error.stack);
31
+ return resultCallback({
32
+ code: ExportResultCode.FAILED,
33
+ error: new Error('Error exporting metrics\n' + error.message + '\n' + error.stack),
34
+ });
35
+ }
36
+ }
37
+ shutdown() {
38
+ return Promise.resolve();
39
+ }
40
+ forceFlush() {
41
+ return Promise.resolve();
42
+ }
43
+ }
@@ -0,0 +1,61 @@
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+ import logger from '../../../utils/logger.js';
3
+ import { Enabler } from '../wrappers.js';
4
+ import { pluginService } from '../../../tlm-plugin/pluginService.js';
5
+ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
6
+ export class PluginSpanExporter extends Enabler {
7
+ constructor() {
8
+ super();
9
+ this._baseUrl = '/telemetry'; // Default base URL, can be overridden by the config
10
+ }
11
+ ;
12
+ set baseUrl(baseUrl) {
13
+ this._baseUrl = baseUrl;
14
+ }
15
+ export(readableSpans, resultCallback) {
16
+ logger.debug('PluginSpanExporter.export called with spans: ', readableSpans.length);
17
+ try {
18
+ if (!this.isEnabled()) {
19
+ logger.debug('PluginSpanExporter is not enabled. Skipping export.');
20
+ return resultCallback({ code: ExportResultCode.SUCCESS });
21
+ }
22
+ // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
23
+ const cleanSpans = readableSpans
24
+ .map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
25
+ .map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
26
+ .filter(span => {
27
+ const target = span?.attributes?.http?.target; // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils'
28
+ if (target && target.includes(this._baseUrl)) {
29
+ return target.includes(this._baseUrl + '/utils');
30
+ }
31
+ return true;
32
+ });
33
+ pluginService.getPlugins().forEach((pluginResource, i) => {
34
+ if (typeof pluginResource.pluginImplementation.newTrace === 'function') {
35
+ cleanSpans.forEach((span) => {
36
+ logger.debug(`Sending span to plugin (Plugin #${i}) <${pluginResource.name}>`);
37
+ //TODO: This should be called newSpan instead of newTrace
38
+ pluginResource.pluginImplementation.newTrace(span);
39
+ });
40
+ }
41
+ else {
42
+ logger.debug(`Plugin <${pluginResource.name}> does not implement newTrace method. Skipping span export.`);
43
+ }
44
+ });
45
+ setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
46
+ }
47
+ catch (error) {
48
+ logger.error('Error exporting spans\n' + error.message + '\n' + error.stack);
49
+ return resultCallback({
50
+ code: ExportResultCode.FAILED,
51
+ error: new Error('Error exporting spans\n' + error.message + '\n' + error.stack),
52
+ });
53
+ }
54
+ }
55
+ shutdown() {
56
+ return this.forceFlush();
57
+ }
58
+ forceFlush() {
59
+ return Promise.resolve();
60
+ }
61
+ }
@@ -0,0 +1,64 @@
1
+ import { callWithTimeout } from '@opentelemetry/core';
2
+ import logger from '../../../utils/logger.js'; // optional if you want logging
3
+ export class DynamicMultiLogRecordProcessor {
4
+ constructor(initialProcessors = [], forceFlushTimeoutMillis = 30000) {
5
+ this._processors = [];
6
+ this._processors = [...initialProcessors];
7
+ this._forceFlushTimeoutMillis = forceFlushTimeoutMillis;
8
+ }
9
+ /**
10
+ * Add a new LogRecordProcessor or an array of LogRecordProcessors at runtime.
11
+ */
12
+ addProcessors(processor) {
13
+ if (Array.isArray(processor)) {
14
+ this._processors.push(...processor);
15
+ }
16
+ else {
17
+ this._processors.push(processor);
18
+ }
19
+ }
20
+ /**
21
+ * Remove a specific LogRecordProcessor.
22
+ */
23
+ removeProcessor(processor) {
24
+ this._processors = this._processors.filter(p => p !== processor);
25
+ }
26
+ /**
27
+ * Clear all LogRecordProcessors.
28
+ */
29
+ clearProcessors() {
30
+ this._processors = [];
31
+ }
32
+ /**
33
+ * Called when a log record is emitted.
34
+ */
35
+ onEmit(logRecord, context) {
36
+ for (const processor of this._processors) {
37
+ try {
38
+ processor.onEmit(logRecord, context);
39
+ }
40
+ catch (error) {
41
+ logger?.error?.('Error in onEmit of LogRecordProcessor:', error);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Force flush all processors with timeout.
47
+ */
48
+ async forceFlush() {
49
+ const timeout = this._forceFlushTimeoutMillis;
50
+ const promises = this._processors.map(p => callWithTimeout(p.forceFlush(), timeout));
51
+ try {
52
+ await Promise.all(promises);
53
+ }
54
+ catch (error) {
55
+ logger?.error?.('Error during forceFlush in DynamicMultiLogRecordProcessor:', error);
56
+ }
57
+ }
58
+ /**
59
+ * Shutdown all processors.
60
+ */
61
+ async shutdown() {
62
+ await Promise.all(this._processors.map(p => p.shutdown()));
63
+ }
64
+ }
@@ -0,0 +1,64 @@
1
+ import logger from '../../../utils/logger.js';
2
+ export class DynamicMultiSpanProcessor {
3
+ constructor(initialProcessors = []) {
4
+ this._spanProcessors = [];
5
+ this._spanProcessors = [...initialProcessors];
6
+ }
7
+ /**
8
+ * Add a new SpanProcessor or an array of SpanProcessors at runtime.
9
+ */
10
+ addProcessors(processor) {
11
+ if (Array.isArray(processor)) {
12
+ this._spanProcessors.push(...processor);
13
+ }
14
+ else {
15
+ this._spanProcessors.push(processor);
16
+ }
17
+ }
18
+ /**
19
+ * Remove a specific SpanProcessor if needed.
20
+ */
21
+ removeProcessor(processor) {
22
+ this._spanProcessors = this._spanProcessors.filter(p => p !== processor);
23
+ }
24
+ /**
25
+ * Clear all processors.
26
+ */
27
+ clearProcessors() {
28
+ this._spanProcessors = [];
29
+ }
30
+ /**
31
+ * Called when a span is started.
32
+ */
33
+ onStart(span, context) {
34
+ for (const processor of this._spanProcessors) {
35
+ processor.onStart(span, context);
36
+ }
37
+ }
38
+ /**
39
+ * Called when a span ends.
40
+ */
41
+ onEnd(span) {
42
+ for (const processor of this._spanProcessors) {
43
+ processor.onEnd(span);
44
+ }
45
+ }
46
+ /**
47
+ * Force flush all processors.
48
+ */
49
+ async forceFlush() {
50
+ const promises = this._spanProcessors.map(p => p.forceFlush());
51
+ try {
52
+ await Promise.all(promises);
53
+ }
54
+ catch (error) {
55
+ logger.error('Error during forceFlush:', error);
56
+ }
57
+ }
58
+ /**
59
+ * Shutdown all processors.
60
+ */
61
+ async shutdown() {
62
+ await Promise.all(this._spanProcessors.map(p => p.shutdown()));
63
+ }
64
+ }
@@ -0,0 +1,76 @@
1
+ export function removeCircularRefs(obj) {
2
+ // const seen = new WeakMap(); // Used to keep track of visited objects
3
+ // Replacer function to handle circular references
4
+ function replacer(key, value) {
5
+ if (key === "_spanProcessor") {
6
+ return "oas-telemetry skips this field to avoid circular reference";
7
+ }
8
+ // GENERIC CIRCULAR REFERENCE HANDLING
9
+ // if (typeof value === "object" && value !== null) {
10
+ // // If the object has been visited before, return the name prefixed with "CIRCULAR+"
11
+ // if (seen.has(value)) {
12
+ // return `CIRCULAR${key}`;
13
+ // }
14
+ // seen.set(value, key); // Mark the object as visited with its name
15
+ // }
16
+ return value;
17
+ }
18
+ // Convert the object to a string and then parse it back
19
+ // This will trigger the replacer function to handle circular references
20
+ const jsonString = JSON.stringify(obj, replacer);
21
+ return JSON.parse(jsonString);
22
+ }
23
+ /**
24
+ * Recursively converts dot-separated keys in an object to nested objects.
25
+ *
26
+ * @param {any} obj - The object to process.
27
+ * @returns {any} - The object with all dot-separated keys converted to nested objects.
28
+ * @example
29
+ * Input:
30
+ * {
31
+ * "http.method": "GET",
32
+ * "http.url": "http://example.com",
33
+ * "nested.obj.key": "value"
34
+ * }
35
+ * Output:
36
+ * {
37
+ * "http": {
38
+ * "method": "GET",
39
+ * "url": "http://example.com"
40
+ * },
41
+ * "nested": {
42
+ * "obj": {
43
+ * "key": "value"
44
+ * }
45
+ * }
46
+ * }
47
+ */
48
+ export function applyNesting(obj) {
49
+ if (Array.isArray(obj)) {
50
+ return obj.map(item => applyNesting(item));
51
+ }
52
+ else if (typeof obj === 'object' && obj !== null) {
53
+ const result = {};
54
+ for (const key in obj) {
55
+ const value = applyNesting(obj[key]);
56
+ const keys = key.split('.');
57
+ let temp = result;
58
+ for (let i = 0; i < keys.length; i++) {
59
+ const currentKey = keys[i];
60
+ if (i === keys.length - 1) {
61
+ temp[currentKey] = value;
62
+ }
63
+ else {
64
+ if (!temp[currentKey]) {
65
+ temp[currentKey] = {};
66
+ }
67
+ temp = temp[currentKey];
68
+ }
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ else {
74
+ return obj;
75
+ }
76
+ }