@oas-tools/oas-telemetry 0.7.1 → 0.8.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/.env.example +17 -3
  2. package/README.md +1 -2
  3. package/dist/cjs/config/bootConfig.cjs +16 -14
  4. package/dist/cjs/config/config.cjs +120 -125
  5. package/dist/cjs/config/config.types.cjs +1 -4
  6. package/dist/cjs/docs/openapi.yaml +158 -4
  7. package/dist/cjs/index.cjs +27 -30
  8. package/dist/cjs/routesManager.cjs +62 -70
  9. package/dist/cjs/telemetry/custom-implementations/exporters/DiskLogExporter.cjs +121 -0
  10. package/dist/cjs/telemetry/custom-implementations/exporters/DiskMetricExporter.cjs +101 -0
  11. package/dist/cjs/telemetry/custom-implementations/exporters/DiskTraceExporter.cjs +103 -0
  12. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +194 -190
  13. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +147 -99
  14. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +143 -116
  15. package/dist/cjs/telemetry/custom-implementations/exporters/MultiMetricExporter.cjs +57 -0
  16. package/dist/cjs/telemetry/custom-implementations/instrumentations/logsInstrumentation.cjs +92 -0
  17. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Chunk.cjs +159 -0
  18. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Series.cjs +168 -0
  19. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.cjs +392 -0
  20. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/types.cjs +2 -0
  21. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/utils.cjs +77 -0
  22. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.cjs +65 -63
  23. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.cjs +63 -62
  24. package/dist/cjs/telemetry/custom-implementations/utils/circular.cjs +47 -47
  25. package/dist/cjs/telemetry/custom-implementations/wrappers.cjs +209 -138
  26. package/dist/cjs/telemetry/initializeTelemetry.cjs +35 -91
  27. package/dist/cjs/telemetry/persistence/DiskImporter.cjs +85 -0
  28. package/dist/cjs/telemetry/persistence/DiskUtils.cjs +61 -0
  29. package/dist/cjs/telemetry/persistence/DiskWriter.cjs +66 -0
  30. package/dist/cjs/telemetry/telemetryConfigurator.cjs +139 -72
  31. package/dist/cjs/telemetry/telemetryRegistry.cjs +45 -31
  32. package/dist/cjs/tlm-ai/agent.cjs +49 -64
  33. package/dist/cjs/tlm-ai/aiController.cjs +54 -76
  34. package/dist/cjs/tlm-ai/aiRoutes.cjs +17 -20
  35. package/dist/cjs/tlm-ai/aiService.cjs +91 -95
  36. package/dist/cjs/tlm-ai/tools.cjs +177 -174
  37. package/dist/cjs/tlm-auth/authController.cjs +80 -123
  38. package/dist/cjs/tlm-auth/authMiddleware.cjs +25 -30
  39. package/dist/cjs/tlm-auth/authRoutes.cjs +11 -14
  40. package/dist/cjs/tlm-log/logController.cjs +135 -116
  41. package/dist/cjs/tlm-log/logRoutes.cjs +19 -20
  42. package/dist/cjs/tlm-log/logService.cjs +29 -0
  43. package/dist/cjs/tlm-metric/metricsController.cjs +154 -122
  44. package/dist/cjs/tlm-metric/metricsRoutes.cjs +22 -20
  45. package/dist/cjs/tlm-metric/metricsService.cjs +26 -0
  46. package/dist/cjs/tlm-plugin/pluginController.cjs +128 -140
  47. package/dist/cjs/tlm-plugin/pluginProcess.cjs +89 -94
  48. package/dist/cjs/tlm-plugin/pluginRoutes.cjs +11 -14
  49. package/dist/cjs/tlm-plugin/pluginService.cjs +73 -74
  50. package/dist/cjs/tlm-trace/traceController.cjs +140 -123
  51. package/dist/cjs/tlm-trace/traceRoutes.cjs +19 -20
  52. package/dist/cjs/tlm-trace/traceService.cjs +29 -0
  53. package/dist/cjs/tlm-ui/uiRoutes.cjs +63 -32
  54. package/dist/cjs/tlm-util/utilController.cjs +68 -70
  55. package/dist/cjs/tlm-util/utilRoutes.cjs +51 -63
  56. package/dist/cjs/types/index.cjs +2 -5
  57. package/dist/cjs/utils/logger.cjs +38 -43
  58. package/dist/cjs/utils/regexUtils.cjs +22 -22
  59. package/dist/esm/config/bootConfig.js +5 -2
  60. package/dist/esm/config/config.js +9 -2
  61. package/dist/esm/docs/openapi.yaml +158 -4
  62. package/dist/esm/index.js +9 -8
  63. package/dist/esm/routesManager.js +6 -10
  64. package/dist/esm/telemetry/custom-implementations/exporters/DiskLogExporter.js +114 -0
  65. package/dist/esm/telemetry/custom-implementations/exporters/DiskMetricExporter.js +94 -0
  66. package/dist/esm/telemetry/custom-implementations/exporters/DiskTraceExporter.js +96 -0
  67. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +38 -7
  68. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +107 -48
  69. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +60 -29
  70. package/dist/esm/telemetry/custom-implementations/exporters/MultiMetricExporter.js +53 -0
  71. package/dist/esm/telemetry/custom-implementations/instrumentations/logsInstrumentation.js +85 -0
  72. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Chunk.js +155 -0
  73. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Series.js +164 -0
  74. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.js +385 -0
  75. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/types.js +1 -0
  76. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/utils.js +74 -0
  77. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.js +2 -1
  78. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.js +1 -1
  79. package/dist/esm/telemetry/custom-implementations/wrappers.js +77 -6
  80. package/dist/esm/telemetry/initializeTelemetry.js +27 -69
  81. package/dist/esm/telemetry/persistence/DiskImporter.js +78 -0
  82. package/dist/esm/telemetry/persistence/DiskUtils.js +51 -0
  83. package/dist/esm/telemetry/persistence/DiskWriter.js +59 -0
  84. package/dist/esm/telemetry/telemetryConfigurator.js +110 -39
  85. package/dist/esm/telemetry/telemetryRegistry.js +12 -1
  86. package/dist/esm/tlm-ai/agent.js +5 -3
  87. package/dist/esm/tlm-ai/aiController.js +3 -3
  88. package/dist/esm/tlm-ai/aiService.js +6 -2
  89. package/dist/esm/tlm-ai/tools.js +5 -9
  90. package/dist/esm/tlm-auth/authController.js +3 -2
  91. package/dist/esm/tlm-log/logController.js +62 -18
  92. package/dist/esm/tlm-log/logRoutes.js +3 -1
  93. package/dist/esm/tlm-log/logService.js +25 -0
  94. package/dist/esm/tlm-metric/metricsController.js +116 -50
  95. package/dist/esm/tlm-metric/metricsRoutes.js +8 -3
  96. package/dist/esm/tlm-metric/metricsService.js +22 -0
  97. package/dist/esm/tlm-plugin/pluginController.js +6 -11
  98. package/dist/esm/tlm-plugin/pluginService.js +2 -4
  99. package/dist/esm/tlm-trace/traceController.js +87 -36
  100. package/dist/esm/tlm-trace/traceRoutes.js +3 -1
  101. package/dist/esm/tlm-trace/traceService.js +25 -0
  102. package/dist/esm/tlm-ui/uiRoutes.js +5 -5
  103. package/dist/esm/tlm-util/utilController.js +3 -9
  104. package/dist/esm/tlm-util/utilRoutes.js +2 -2
  105. package/dist/types/config/bootConfig.d.ts +3 -0
  106. package/dist/types/config/config.d.ts +48 -7
  107. package/dist/types/config/config.types.d.ts +7 -0
  108. package/dist/types/index.d.ts +2 -3
  109. package/dist/types/telemetry/custom-implementations/exporters/DiskLogExporter.d.ts +24 -0
  110. package/dist/types/telemetry/custom-implementations/exporters/DiskMetricExporter.d.ts +23 -0
  111. package/dist/types/telemetry/custom-implementations/exporters/DiskTraceExporter.d.ts +23 -0
  112. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +3 -1
  113. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +56 -15
  114. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +8 -4
  115. package/dist/types/telemetry/custom-implementations/exporters/MultiMetricExporter.d.ts +9 -0
  116. package/dist/types/telemetry/custom-implementations/instrumentations/logsInstrumentation.d.ts +23 -0
  117. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Chunk.d.ts +49 -0
  118. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Series.d.ts +67 -0
  119. package/dist/types/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.d.ts +69 -0
  120. package/dist/types/telemetry/custom-implementations/metrics/tsdb/types.d.ts +68 -0
  121. package/dist/types/telemetry/custom-implementations/metrics/tsdb/utils.d.ts +21 -0
  122. package/dist/types/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.d.ts +2 -2
  123. package/dist/types/telemetry/custom-implementations/wrappers.d.ts +2 -1
  124. package/dist/types/telemetry/persistence/DiskImporter.d.ts +17 -0
  125. package/dist/types/telemetry/persistence/DiskUtils.d.ts +11 -0
  126. package/dist/types/telemetry/persistence/DiskWriter.d.ts +21 -0
  127. package/dist/types/telemetry/telemetryConfigurator.d.ts +1 -1
  128. package/dist/types/telemetry/telemetryRegistry.d.ts +8 -0
  129. package/dist/types/tlm-ai/agent.d.ts +1 -1
  130. package/dist/types/tlm-ai/aiService.d.ts +1 -1
  131. package/dist/types/tlm-log/logController.d.ts +2 -0
  132. package/dist/types/tlm-log/logService.d.ts +4 -0
  133. package/dist/types/tlm-metric/metricsController.d.ts +11 -2
  134. package/dist/types/tlm-metric/metricsService.d.ts +6 -0
  135. package/dist/types/tlm-trace/traceController.d.ts +9 -7
  136. package/dist/types/tlm-trace/traceService.d.ts +4 -0
  137. package/dist/types/types/index.d.ts +2 -2
  138. package/dist/ui/assets/{ApiDocsPage-C_VVPPHa.js → ApiDocsPage-DTCgVbW2.js} +2 -2
  139. package/dist/ui/assets/CollapsibleCard-lWgfsaAn.js +1 -0
  140. package/dist/ui/assets/DevToolsPage-DEhf8CBy.js +1 -0
  141. package/dist/ui/assets/LandingPage-CfEHCDxY.js +6 -0
  142. package/dist/ui/assets/LogsPage-DFDKRuGH.js +1 -0
  143. package/dist/ui/assets/{NotFoundPage-B3quk3P1.js → NotFoundPage-DCy0DcV7.js} +1 -1
  144. package/dist/ui/assets/PluginCreatePage-BawZ5_-h.js +50 -0
  145. package/dist/ui/assets/PluginPage-D3FmgU7d.js +27 -0
  146. package/dist/ui/assets/TraceSpansPage-D0_L45Rb.js +6 -0
  147. package/dist/ui/assets/VirtualizedListPanel-q605n9He.js +16 -0
  148. package/dist/ui/assets/alert-DBAFshSi.js +1133 -0
  149. package/dist/ui/assets/badge-DGNBtnxU.js +1 -0
  150. package/dist/ui/assets/{chevron-down-CPsvsmqj.js → chevron-down-CFEqYzGC.js} +1 -1
  151. package/dist/ui/assets/{chevron-up-Df9jMo1X.js → chevron-up-lDnFwAJq.js} +1 -1
  152. package/dist/ui/assets/{circle-alert-DOPQPvU8.js → circle-alert-BpYUuRs7.js} +1 -1
  153. package/dist/ui/assets/dialog-1dRyI6SC.js +15 -0
  154. package/dist/ui/assets/index-C7RfU6hR.js +1 -0
  155. package/dist/ui/assets/index-C9dDYIpd.js +305 -0
  156. package/dist/ui/assets/index-D6f1KjWV.css +1 -0
  157. package/dist/ui/assets/info-CuJQWoBU.js +6 -0
  158. package/dist/ui/assets/{input-Dzvg_ZEZ.js → input-BLXaar0X.js} +1 -1
  159. package/dist/ui/assets/label-DfAcltsl.js +1 -0
  160. package/dist/ui/assets/{loader-circle-CrvlRy5o.js → loader-circle-B7oLyPsi.js} +1 -1
  161. package/dist/ui/assets/{loginPage-qa4V-B70.js → loginPage-DswZvOJ-.js} +1 -1
  162. package/dist/ui/assets/metrics-page-BhtXrfUW.js +31 -0
  163. package/dist/ui/assets/metrics-page-D1GxaB_c.css +1 -0
  164. package/dist/ui/assets/popover-IDker85U.js +11 -0
  165. package/dist/ui/assets/select-B8y5IidE.js +6 -0
  166. package/dist/ui/assets/separator-B6EzrxYY.js +6 -0
  167. package/dist/ui/assets/severityOptions-DtCsaAZK.js +11 -0
  168. package/dist/ui/assets/square-pen-D_oecB1x.js +6 -0
  169. package/dist/ui/assets/switch-Dqo0XkRD.js +1 -0
  170. package/dist/ui/assets/trace-DJq1miYa.js +1 -0
  171. package/dist/ui/assets/upload-prIohEdY.js +11 -0
  172. package/dist/ui/assets/{utilService-DNyqzwj0.js → utilService-C8TJKLqs.js} +1 -1
  173. package/dist/ui/assets/wand-sparkles-OgXuzsSx.js +6 -0
  174. package/dist/ui/index.html +2 -2
  175. package/package.json +44 -49
  176. package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +0 -1
  177. package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +0 -1
  178. package/dist/ui/assets/LandingPage-CppFBA6K.js +0 -6
  179. package/dist/ui/assets/LogsPage-9Fq8GArS.js +0 -26
  180. package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +0 -50
  181. package/dist/ui/assets/PluginPage-DMDSihrZ.js +0 -27
  182. package/dist/ui/assets/alert-jQ9HCPIf.js +0 -1133
  183. package/dist/ui/assets/badge-CNq0-mH5.js +0 -1
  184. package/dist/ui/assets/card-DFAwwhN3.js +0 -1
  185. package/dist/ui/assets/index-BkD6DijD.js +0 -15
  186. package/dist/ui/assets/index-CERGVYZK.js +0 -292
  187. package/dist/ui/assets/index-CSIPf9qw.css +0 -1
  188. package/dist/ui/assets/label-DuVnkZ4q.js +0 -1
  189. package/dist/ui/assets/select-DhS8YUtJ.js +0 -1
  190. package/dist/ui/assets/separator-isK4chBP.js +0 -6
  191. package/dist/ui/assets/severityOptions-O38dSOfk.js +0 -11
  192. package/dist/ui/assets/switch-Z3mImG9n.js +0 -1
  193. package/dist/ui/assets/tabs-_77MUUQe.js +0 -16
  194. package/dist/ui/assets/upload-C1LT4Gkb.js +0 -16
@@ -7,9 +7,19 @@ import { Enabler } from '../wrappers.js';
7
7
  import logger from '../../../utils/logger.js';
8
8
  import { pluginService } from '../../../tlm-plugin/pluginService.js';
9
9
  export class InMemoryDbLogExporter extends Enabler {
10
+ _db = null;
11
+ _miniSearch = null;
12
+ _retentionTimeInSeconds;
13
+ _initialized = false;
10
14
  constructor(retentionTimeInSeconds = 3600) {
11
15
  super();
12
16
  this._retentionTimeInSeconds = retentionTimeInSeconds;
17
+ this._startCleanupJob();
18
+ }
19
+ _ensureInitialized() {
20
+ if (this._initialized)
21
+ return;
22
+ this._initialized = true;
13
23
  this._db = new Datastore({ timestampData: true });
14
24
  this._db.ensureIndex({ fieldName: 'createdAt' });
15
25
  this._miniSearch = new MiniSearch({
@@ -17,7 +27,7 @@ export class InMemoryDbLogExporter extends Enabler {
17
27
  storeFields: ['_id'],
18
28
  idField: '_id',
19
29
  });
20
- this._startCleanupJob();
30
+ logger.info(`[LogExporter] In-memory storage created`);
21
31
  }
22
32
  /*
23
33
  * SUPER WARNING:
@@ -31,6 +41,7 @@ export class InMemoryDbLogExporter extends Enabler {
31
41
  * @param resultCallback
32
42
  */
33
43
  export(logs, resultCallback) {
44
+ this._ensureInitialized();
34
45
  const logsToInsert = logs.map(logRecord => {
35
46
  // Remove circular references first, then apply nesting, then export info
36
47
  const formattedLog = this._formatLogRecord(logRecord);
@@ -48,7 +59,17 @@ export class InMemoryDbLogExporter extends Enabler {
48
59
  resultCallback({ code: ExportResultCode.SUCCESS });
49
60
  }
50
61
  reset() {
51
- this._db = new Datastore();
62
+ this._ensureInitialized();
63
+ // Remove all logs from the in-memory database.
64
+ this._db.remove({}, { multi: true }, (err) => {
65
+ if (err) {
66
+ logger.error(`[LogExporter] Error during reset: ${err.message}`);
67
+ }
68
+ else {
69
+ logger.info(`[LogExporter] Reset - all logs cleared`);
70
+ }
71
+ });
72
+ // Clear mini search index
52
73
  this._miniSearch = new MiniSearch({
53
74
  fields: ['body'],
54
75
  storeFields: ['_id'],
@@ -61,8 +82,10 @@ export class InMemoryDbLogExporter extends Enabler {
61
82
  async shutdown() {
62
83
  this._db = null;
63
84
  this._miniSearch = null;
85
+ this._initialized = false;
64
86
  }
65
87
  async find(findConfig) {
88
+ this._ensureInitialized();
66
89
  const { query, messageSearch, limit, sortOrder } = findConfig;
67
90
  const finalQuery = { ...query };
68
91
  const effectiveSortOrder = sortOrder || { timestamp: -1 };
@@ -73,10 +96,13 @@ export class InMemoryDbLogExporter extends Enabler {
73
96
  finalQuery._id = { $in: ids };
74
97
  }
75
98
  const docs = await new Promise((resolve, reject) => {
76
- this._db.find(finalQuery)
77
- .sort(effectiveSortOrder)
78
- .limit(limit)
79
- .exec((err, docs) => {
99
+ let query_exec = this._db.find(finalQuery)
100
+ .sort(effectiveSortOrder);
101
+ // Only apply limit if provided
102
+ if (limit !== undefined) {
103
+ query_exec = query_exec.limit(limit);
104
+ }
105
+ query_exec.exec((err, docs) => {
80
106
  if (err)
81
107
  reject(err);
82
108
  else
@@ -103,6 +129,7 @@ export class InMemoryDbLogExporter extends Enabler {
103
129
  });
104
130
  }
105
131
  getFinishedLogs() {
132
+ this._ensureInitialized();
106
133
  return this._db.getAllData();
107
134
  }
108
135
  /**
@@ -136,6 +163,8 @@ export class InMemoryDbLogExporter extends Enabler {
136
163
  return this._retentionTimeInSeconds;
137
164
  }
138
165
  _insertLogs(logsToInsert, resultCallback) {
166
+ if (!this._db)
167
+ return resultCallback({ code: ExportResultCode.FAILED });
139
168
  this._db.insert(logsToInsert, (err, newDocs) => {
140
169
  if (err) {
141
170
  console.dir(err);
@@ -143,7 +172,7 @@ export class InMemoryDbLogExporter extends Enabler {
143
172
  return;
144
173
  }
145
174
  // console.dir(newDocs, { depth: 3 });
146
- newDocs.forEach((doc) => this._miniSearch.add(doc));
175
+ newDocs.forEach((doc) => this._miniSearch?.add(doc));
147
176
  resultCallback({ code: ExportResultCode.SUCCESS });
148
177
  });
149
178
  return;
@@ -151,6 +180,8 @@ export class InMemoryDbLogExporter extends Enabler {
151
180
  _startCleanupJob() {
152
181
  const interval = 1000;
153
182
  setInterval(() => {
183
+ if (!this._db)
184
+ return; // Safety check
154
185
  const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
155
186
  this._db.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
156
187
  if (err) {
@@ -1,32 +1,55 @@
1
1
  import { ExportResultCode } from '@opentelemetry/core';
2
- import dataStore from '@seald-io/nedb';
3
- import { applyNesting } from '../utils/circular.js';
4
2
  import { Enabler } from '../wrappers.js';
5
3
  import logger from '../../../utils/logger.js';
6
4
  import { pluginService } from '../../../tlm-plugin/pluginService.js';
5
+ import { rawToOtel } from '../metrics/tsdb/utils.js';
6
+ import { SeriesRegistry } from '../metrics/tsdb/SeriesRegistry.js';
7
+ /**
8
+ * In-Memory TSDB Metric Exporter
9
+ * Series identified by: scope + metricName + attributes
10
+ */
7
11
  export class InMemoryDbMetricExporter extends Enabler {
8
- constructor(retentionTimeInSeconds = 3600) {
12
+ static DEFAULT_CONFIG = {
13
+ retentionTimeInSeconds: 3600,
14
+ chunkSize: 120,
15
+ maxChunks: 60
16
+ };
17
+ registry;
18
+ config;
19
+ cachedResource = null;
20
+ _initialized = false;
21
+ _ensureInitialized() {
22
+ if (this._initialized)
23
+ return;
24
+ this._initialized = true;
25
+ logger.info(`[MetricExporter] In-memory storage created`);
26
+ }
27
+ get rawDataDB() {
28
+ // For debug/inspection only - metrics are stored in registry chunks
29
+ const stats = this.registry.getStats();
30
+ return stats.totalSamples > 0 ? [`[${stats.totalSamples} samples in registry]`] : [];
31
+ }
32
+ constructor(config) {
9
33
  super();
10
- this._retentionTimeInSeconds = retentionTimeInSeconds;
11
- this._metrics = new dataStore({ timestampData: true });
12
- this._metrics.ensureIndex({ fieldName: 'createdAt' });
34
+ this.config = { ...InMemoryDbMetricExporter.DEFAULT_CONFIG, ...config };
35
+ this.registry = new SeriesRegistry(this.config.chunkSize, this.config.maxChunks);
13
36
  this._startCleanupJob();
14
37
  }
15
- export(metrics, resultCallback) {
38
+ export(resourceMetrics, resultCallback) {
39
+ this._ensureInitialized();
16
40
  try {
17
- const scopeMetrics = metrics?.scopeMetrics;
18
- const cleanMetrics = applyNesting(scopeMetrics);
19
- cleanMetrics.forEach((metric) => {
41
+ // Cache resource (unique per exporter instance)
42
+ if (!this.cachedResource) {
43
+ this.cachedResource = resourceMetrics.resource;
44
+ }
45
+ const scopeMetrics = resourceMetrics.scopeMetrics;
46
+ // Broadcast to plugins
47
+ scopeMetrics?.forEach((metric) => {
20
48
  pluginService.broadcastMetric(metric);
21
49
  });
22
- // Insert only if exporter is enabled
23
- if (this.isEnabled()) {
24
- this._metrics.insert(cleanMetrics, (err, _newDoc) => {
25
- if (err) {
26
- logger.error('Insertion Error:', err);
27
- return;
28
- }
29
- });
50
+ // Store if enabled - use new storeScopeMetrics method
51
+ if (this.isEnabled() && scopeMetrics) {
52
+ this.registry.storeScopeMetrics(scopeMetrics);
30
53
  }
31
54
  setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
32
55
  }
@@ -40,48 +63,84 @@ export class InMemoryDbMetricExporter extends Enabler {
40
63
  }
41
64
  shutdown() {
42
65
  this._enabled = false;
43
- this._metrics = new dataStore();
66
+ this.registry.reset();
44
67
  return this.forceFlush();
45
68
  }
69
+ reset() {
70
+ this._ensureInitialized();
71
+ this.registry.reset();
72
+ logger.info(`[MetricExporter] Reset - all metrics cleared`);
73
+ }
46
74
  forceFlush() {
47
75
  return Promise.resolve();
48
76
  }
49
- find(search, callback) {
50
- this._metrics.find(search, callback);
77
+ getCachedResource() {
78
+ return this.cachedResource;
51
79
  }
52
- reset() {
53
- this._metrics = new dataStore();
80
+ set retentionTimeInSeconds(value) {
81
+ this.config.retentionTimeInSeconds = value;
82
+ logger.info(`Retention time set to ${value} seconds`);
83
+ }
84
+ get retentionTimeInSeconds() {
85
+ return this.config.retentionTimeInSeconds;
86
+ }
87
+ getStats() {
88
+ this._ensureInitialized();
89
+ return this.registry.getStats();
90
+ }
91
+ _startCleanupJob() {
92
+ setInterval(() => {
93
+ const retentionTimeNs = this.config.retentionTimeInSeconds * 1_000_000_000;
94
+ const result = this.registry.evictOldData(retentionTimeNs);
95
+ if (result.evictedChunks > 0 || result.evictedSeries > 0) {
96
+ logger.debug(`Cleanup: evicted ${result.evictedChunks} chunks, ${result.evictedSeries} series`);
97
+ }
98
+ }, 5000);
54
99
  }
55
- getFinishedMetrics() {
56
- return this._metrics.getAllData();
100
+ /**
101
+ * Find metrics by scope+metric queries with filters
102
+ * Supports both raw and otel formats
103
+ */
104
+ find(request) {
105
+ this._ensureInitialized();
106
+ const format = request.format || 'raw';
107
+ // Get raw results from registry using new unified query method
108
+ const rawResults = this.registry.query(request.scopeMetrics, request.from, request.to);
109
+ // Convert format if needed
110
+ if (format === 'otel') {
111
+ return { results: rawToOtel(rawResults) };
112
+ }
113
+ return { results: rawResults };
57
114
  }
58
115
  /**
59
- * Inserts metrics into the in-memory database.
60
- * @param metrics - The metrics to insert.
61
- * @param callback - The callback to execute after insertion.
116
+ * Export all metrics as line-delimited JSON (one chunk per line)
62
117
  */
63
- insert(metrics, callback) {
64
- this._metrics.insert(metrics, callback);
118
+ exportToLineDelimitedJson() {
119
+ return this.registry.serializeToLineDelimitedJson();
65
120
  }
66
- set retentionTimeInSeconds(retentionTimeInSeconds) {
67
- this._retentionTimeInSeconds = retentionTimeInSeconds;
68
- logger.info(`InMemoryDbMetricExporter retention time set to ${this._retentionTimeInSeconds} seconds`);
121
+ /**
122
+ * Import metrics from line-delimited JSON format
123
+ */
124
+ importFromLineDelimitedJson(lineDelimitedJsonData) {
125
+ this.registry.deserializeFromLineDelimitedJson(lineDelimitedJsonData);
69
126
  }
70
- get retentionTimeInSeconds() {
71
- return this._retentionTimeInSeconds;
127
+ /**
128
+ * Insert metrics in OpenTelemetry (OTEL) format directly into the registry
129
+ * @param scopeMetrics Array of ScopeMetrics (OTEL format)
130
+ */
131
+ insertOtel(scopeMetrics) {
132
+ this._ensureInitialized();
133
+ // Store only in registry (chunks) - no duplication
134
+ if (this.isEnabled()) {
135
+ this.registry.storeScopeMetrics(scopeMetrics);
136
+ }
72
137
  }
73
- _startCleanupJob() {
74
- const interval = 1000;
75
- setInterval(() => {
76
- const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
77
- this._metrics.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
78
- if (err) {
79
- logger.error('Error in TTL cleanup:', err);
80
- }
81
- else if (numRemoved > 0) {
82
- logger.debug(`TTL cleanup: removed ${numRemoved} expired metrics`);
83
- }
84
- });
85
- }, interval);
138
+ /**
139
+ * Insert metrics in raw format (MetricQueryResult[]), converts to OTEL and delegates to insertOtel
140
+ * @param rawScopeMetrics Array of MetricQueryResult (raw format)
141
+ */
142
+ insertRaw(rawScopeMetrics) {
143
+ const otelScopeMetrics = rawToOtel(rawScopeMetrics);
144
+ this.insertOtel(otelScopeMetrics);
86
145
  }
87
146
  }
@@ -5,17 +5,22 @@ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
5
5
  import { Enabler } from '../wrappers.js';
6
6
  import { pluginService } from '../../../tlm-plugin/pluginService.js';
7
7
  export class InMemoryDbSpanExporter extends Enabler {
8
+ _spans = null;
9
+ _retentionTimeInSeconds;
10
+ _initialized = false;
8
11
  constructor(retentionTimeInSeconds = 3600) {
9
12
  super();
10
- this._baseUrl = '/telemetry'; // Default base URL, can be overridden by the config
11
13
  this._retentionTimeInSeconds = retentionTimeInSeconds;
12
- this._spans = new dataStore({ timestampData: true });
13
- this._spans.ensureIndex({ fieldName: 'createdAt' });
14
14
  this._startCleanupJob();
15
15
  }
16
16
  ;
17
- set baseUrl(baseUrl) {
18
- this._baseUrl = baseUrl;
17
+ _ensureInitialized() {
18
+ if (this._initialized)
19
+ return;
20
+ this._initialized = true;
21
+ this._spans = new dataStore({ timestampData: true });
22
+ this._spans.ensureIndex({ fieldName: 'createdAt' });
23
+ logger.info(`[SpanExporter] In-memory storage created`);
19
24
  }
20
25
  set retentionTimeInSeconds(retentionTimeInSeconds) {
21
26
  this._retentionTimeInSeconds = retentionTimeInSeconds;
@@ -25,32 +30,26 @@ export class InMemoryDbSpanExporter extends Enabler {
25
30
  return this._retentionTimeInSeconds;
26
31
  }
27
32
  export(readableSpans, resultCallback) {
33
+ this._ensureInitialized();
28
34
  logger.debug('InMemoryDbSpanExporter.export called with spans: ', readableSpans.length);
29
35
  try {
30
36
  // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
31
37
  const cleanSpans = readableSpans
32
38
  .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;
36
- // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils/generate-log' or 'telemetry/utils/generate-wait'
37
- if (target && target.includes(this._baseUrl)) {
38
- return (target.includes("generate"));
39
- }
40
- return true;
41
- });
39
+ .map(span => applyNesting(span)); // to avoid dot notation in keys (neDB does not support dot notation in keys)
42
40
  cleanSpans.forEach(span => {
43
41
  pluginService.broadcastTrace(span);
44
42
  });
45
- //
46
43
  if (this.isEnabled()) {
47
44
  // Insert spans into the in-memory database
48
- this._spans.insert(cleanSpans, (err, _newDoc) => {
49
- if (err) {
50
- logger.error(err);
51
- return;
52
- }
53
- });
45
+ if (this._spans) {
46
+ this._spans.insert(cleanSpans, (err, _newDoc) => {
47
+ if (err) {
48
+ logger.error(err);
49
+ return;
50
+ }
51
+ });
52
+ }
54
53
  }
55
54
  return resultCallback({ code: ExportResultCode.SUCCESS });
56
55
  }
@@ -64,10 +63,21 @@ export class InMemoryDbSpanExporter extends Enabler {
64
63
  }
65
64
  ;
66
65
  shutdown() {
67
- this._spans = new dataStore();
66
+ this._spans = null;
68
67
  return this.forceFlush();
69
68
  }
70
69
  ;
70
+ reset() {
71
+ this._ensureInitialized();
72
+ this._spans.remove({}, { multi: true }, (err) => {
73
+ if (err) {
74
+ logger.error(`[SpanExporter] Error during reset: ${err.message}`);
75
+ }
76
+ else {
77
+ logger.info(`[SpanExporter] Reset - all spans cleared`);
78
+ }
79
+ });
80
+ }
71
81
  /**
72
82
  * Exports any pending spans in the exporter
73
83
  */
@@ -75,15 +85,30 @@ export class InMemoryDbSpanExporter extends Enabler {
75
85
  return Promise.resolve();
76
86
  }
77
87
  ;
78
- //err,docs
79
- find(search, callback) {
80
- this._spans.find(search, callback);
81
- }
82
- reset() {
83
- this._spans = new dataStore();
88
+ async find(findConfig) {
89
+ this._ensureInitialized();
90
+ const { query, limit, sortOrder } = findConfig;
91
+ const effectiveSortOrder = sortOrder || { timestamp: -1 };
92
+ const docs = await new Promise((resolve, reject) => {
93
+ let query_exec = this._spans.find(query)
94
+ .sort(effectiveSortOrder);
95
+ // Only apply limit if provided
96
+ if (limit !== undefined) {
97
+ query_exec = query_exec.limit(limit);
98
+ }
99
+ query_exec.exec((err, docs) => {
100
+ if (err)
101
+ reject(err);
102
+ else
103
+ resolve(docs);
104
+ });
105
+ });
106
+ return docs;
84
107
  }
85
- ;
86
108
  getFinishedSpans() {
109
+ this._ensureInitialized();
110
+ if (!this._spans)
111
+ return [];
87
112
  return this._spans.getAllData();
88
113
  }
89
114
  ;
@@ -93,11 +118,17 @@ export class InMemoryDbSpanExporter extends Enabler {
93
118
  * @param callback - The callback to execute after insertion.
94
119
  */
95
120
  insert(spans, callback) {
121
+ this._ensureInitialized();
122
+ if (!this._spans) {
123
+ return callback(new Error('Spans database not initialized'), []);
124
+ }
96
125
  this._spans.insert(spans, callback);
97
126
  }
98
127
  _startCleanupJob() {
99
128
  const interval = 1000;
100
129
  setInterval(() => {
130
+ if (!this._spans)
131
+ return; // Safety check - not initialized yet
101
132
  const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
102
133
  this._spans.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
103
134
  if (err) {
@@ -0,0 +1,53 @@
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+ export class MultiMetricExporter {
3
+ exporters;
4
+ constructor(exporters) {
5
+ this.exporters = exporters;
6
+ }
7
+ export(resourceMetrics, resultCallback) {
8
+ if (!this.exporters.length) {
9
+ resultCallback({ code: ExportResultCode.SUCCESS });
10
+ return;
11
+ }
12
+ let pending = this.exporters.length;
13
+ let failed = false;
14
+ let firstError;
15
+ this.exporters.forEach((exporter) => {
16
+ try {
17
+ exporter.export(resourceMetrics, (result) => {
18
+ if (result.code === ExportResultCode.FAILED) {
19
+ failed = true;
20
+ if (result.error && !firstError) {
21
+ firstError = result.error;
22
+ }
23
+ }
24
+ pending -= 1;
25
+ if (pending === 0) {
26
+ resultCallback(failed
27
+ ? { code: ExportResultCode.FAILED, error: firstError }
28
+ : { code: ExportResultCode.SUCCESS });
29
+ }
30
+ });
31
+ }
32
+ catch (error) {
33
+ failed = true;
34
+ if (!firstError) {
35
+ firstError = error instanceof Error ? error : new Error(String(error));
36
+ }
37
+ pending -= 1;
38
+ if (pending === 0) {
39
+ resultCallback({ code: ExportResultCode.FAILED, error: firstError });
40
+ }
41
+ }
42
+ });
43
+ }
44
+ async shutdown() {
45
+ await Promise.all(this.exporters.map((exporter) => exporter.shutdown()));
46
+ }
47
+ async forceFlush() {
48
+ await Promise.all(this.exporters
49
+ .map((exporter) => exporter.forceFlush)
50
+ .filter((forceFlush) => typeof forceFlush === 'function')
51
+ .map((forceFlush) => forceFlush()));
52
+ }
53
+ }
@@ -0,0 +1,85 @@
1
+ import { InstrumentationBase } from '@opentelemetry/instrumentation';
2
+ import { SeverityNumber } from '@opentelemetry/api-logs';
3
+ import util from 'util';
4
+ import { originalConsoleMethods } from '../../telemetryRegistry.js';
5
+ export class LogsInstrumentation extends InstrumentationBase {
6
+ _loggerProvider;
7
+ _otelLogger;
8
+ constructor(config = {}) {
9
+ super('@oas-telemetry/logs-instrumentation', '1.0.0', config);
10
+ }
11
+ /**
12
+ * No-op: this instrumentation does not patch modules loaded via require().
13
+ */
14
+ init() {
15
+ return [];
16
+ }
17
+ /**
18
+ * Called by the SDK when the LoggerProvider is available.
19
+ * This is the correct way for an instrumentation to receive a logger provider.
20
+ */
21
+ setLoggerProvider(provider) {
22
+ this._loggerProvider = provider;
23
+ this._otelLogger = provider.getLogger(this.instrumentationName);
24
+ // If already enabled, re-apply patches with the new logger provider.
25
+ if (this.isEnabled()) {
26
+ this._unpatchConsole();
27
+ this._patchConsole();
28
+ }
29
+ }
30
+ /**
31
+ * Called by the SDK after all providers have been registered.
32
+ */
33
+ enable() {
34
+ super.enable();
35
+ // Fallback: if no logger provider has been set yet, we do nothing.
36
+ if (!this._loggerProvider) {
37
+ return;
38
+ }
39
+ this._patchConsole();
40
+ }
41
+ disable() {
42
+ super.disable();
43
+ this._unpatchConsole();
44
+ }
45
+ _patchConsole() {
46
+ if (!this._otelLogger)
47
+ return;
48
+ Object.keys(originalConsoleMethods).forEach((method) => {
49
+ const original = originalConsoleMethods[method];
50
+ console[method] = (...args) => {
51
+ const { number, text } = getSeverityForMethod(method);
52
+ this._otelLogger.emit({
53
+ severityNumber: number,
54
+ severityText: text,
55
+ body: util.format(...args),
56
+ attributes: {
57
+ source: `console.${method}`,
58
+ library: this.instrumentationName,
59
+ },
60
+ });
61
+ original(...args);
62
+ };
63
+ });
64
+ }
65
+ _unpatchConsole() {
66
+ Object.keys(originalConsoleMethods).forEach((method) => {
67
+ console[method] = originalConsoleMethods[method];
68
+ });
69
+ }
70
+ }
71
+ function getSeverityForMethod(method) {
72
+ switch (method) {
73
+ case "log":
74
+ case "info":
75
+ return { number: SeverityNumber.INFO, text: "INFO" };
76
+ case "debug":
77
+ return { number: SeverityNumber.DEBUG, text: "DEBUG" };
78
+ case "warn":
79
+ return { number: SeverityNumber.WARN, text: "WARN" };
80
+ case "error":
81
+ return { number: SeverityNumber.ERROR, text: "ERROR" };
82
+ default:
83
+ return { number: SeverityNumber.INFO, text: "INFO" };
84
+ }
85
+ }