@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
@@ -1,70 +1,41 @@
1
1
  import { inMemoryDbMetricExporter } from '../telemetry/telemetryRegistry.js';
2
- import { convertRegexRecursively } from '../utils/regexUtils.js';
3
- export const listMetrics = async (req, res) => {
2
+ import { importMetricsToMemory, sanitizeMetricRecords } from './metricsService.js';
3
+ import { gzipSync } from 'zlib';
4
+ export const getMetricsStats = async (req, res) => {
4
5
  try {
5
- const metrics = inMemoryDbMetricExporter.getFinishedMetrics();
6
- res.send({ metricsCount: metrics.length, metrics: metrics });
6
+ const stats = inMemoryDbMetricExporter.getStats();
7
+ res.send(stats);
7
8
  }
8
9
  catch (err) {
9
10
  console.error(err);
10
- res.status(500).send({ error: 'Failed to list metrics data' });
11
+ res.status(500).send({ error: 'Failed to get metrics stats' });
11
12
  }
12
13
  };
13
- export const findMetrics = (req, res) => {
14
- const body = req.body;
15
- const query = body?.query ? body.query : {};
16
- let processedQuery;
17
- try {
18
- processedQuery = convertRegexRecursively(query);
19
- }
20
- catch (error) {
21
- console.error(error.message);
22
- res.status(400).send({ error: error.message });
23
- return; // Exit if invalid regex was encountered
24
- }
25
- inMemoryDbMetricExporter.find(processedQuery, (err, docs) => {
26
- if (err) {
27
- console.error(err);
28
- res.status(404).send({ metricsCount: 0, metrics: [], error: err });
29
- return;
30
- }
31
- const metrics = docs;
32
- res.send({ metricsCount: metrics.length, metrics: metrics });
33
- });
34
- };
35
14
  export const resetMetrics = (req, res) => {
36
15
  inMemoryDbMetricExporter.reset();
37
16
  res.send('Metrics reset');
38
17
  };
39
18
  export const insertMetricsToDb = async (req, res) => {
40
- const jsonContent = req.body.metrics;
41
- const resetData = req.query.reset === 'true';
42
- if (!Array.isArray(jsonContent)) {
43
- res.status(400).send({ error: 'Invalid data format.' });
44
- return;
45
- }
46
- const cleanedMetrics = jsonContent.map((metric) => {
47
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
48
- const { _id, ...rest } = metric; // Remove _id if it exists
49
- return rest; // Return the cleaned metric object
50
- });
51
19
  try {
20
+ const scopeMetricsData = (req.body || {}).scopeMetrics;
21
+ const resetData = req.query.reset === 'true';
22
+ const format = (req.body.format || req.query.format || 'raw');
23
+ if (!Array.isArray(scopeMetricsData)) {
24
+ res.status(400).send({ error: 'Invalid data format. Expected array in body.scopeMetrics' });
25
+ return;
26
+ }
27
+ const cleanedScopeMetrics = sanitizeMetricRecords(scopeMetricsData);
52
28
  let message = '';
29
+ importMetricsToMemory(cleanedScopeMetrics, { reset: resetData, format });
53
30
  if (resetData) {
54
- inMemoryDbMetricExporter.reset();
55
31
  message += 'Metrics Database reset. ';
56
32
  }
57
- await new Promise((resolve, reject) => {
58
- inMemoryDbMetricExporter.insert(cleanedMetrics, (err, newDocs) => {
59
- if (err) {
60
- console.error('Error inserting metrics:', err);
61
- return reject(err);
62
- }
63
- resolve(newDocs);
64
- });
33
+ message += `Inserted ${cleanedScopeMetrics.length} scopeMetrics (format: ${format}).`;
34
+ res.send({
35
+ message,
36
+ scopeMetricsCount: cleanedScopeMetrics.length,
37
+ format
65
38
  });
66
- message += `Inserted ${cleanedMetrics.length} metrics.`;
67
- res.send({ message, InsertedMetricsCount: cleanedMetrics.length });
68
39
  }
69
40
  catch (err) {
70
41
  console.error(err);
@@ -84,7 +55,7 @@ export const statusMetrics = (req, res) => {
84
55
  res.send({ active: isRunning });
85
56
  };
86
57
  export const setMetricRetentionTime = (req, res) => {
87
- const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
58
+ const retentionTimeInSeconds = (req.body || {}).retentionTimeInSeconds;
88
59
  if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
89
60
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
61
  return;
@@ -96,3 +67,98 @@ export const getMetricRetentionTime = (req, res) => {
96
67
  const retentionTimeInSeconds = inMemoryDbMetricExporter.retentionTimeInSeconds || 0;
97
68
  res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
98
69
  };
70
+ /**
71
+ * Find metrics by scope+metric queries with filters (POST /metrics/find or GET /metrics)
72
+ * All parameters are optional:
73
+ * - If scopeMetrics is not provided or empty, returns all metrics
74
+ * - If startTime/endTime not provided, returns all available data
75
+ * - format can be 'raw' (default) or 'otel'
76
+ */
77
+ export const findMetrics = async (req, res) => {
78
+ try {
79
+ const { scopeMetrics, from, to, format } = req.body || {};
80
+ // Validate scopeMetrics structure if provided
81
+ if (scopeMetrics && Array.isArray(scopeMetrics)) {
82
+ for (const query of scopeMetrics) {
83
+ if (!query.scope || !query.scope.name || !query.descriptor || !query.descriptor.name) {
84
+ res.status(400).json({
85
+ error: 'Each query must have scope.name and descriptor.name defined'
86
+ });
87
+ return;
88
+ }
89
+ }
90
+ }
91
+ // Execute query
92
+ const response = inMemoryDbMetricExporter.find({
93
+ scopeMetrics: scopeMetrics && scopeMetrics.length > 0 ? scopeMetrics : undefined,
94
+ from,
95
+ to,
96
+ format: format || 'raw'
97
+ });
98
+ res.json({
99
+ format: format || 'raw',
100
+ scopeMetricsCount: response.results.length,
101
+ scopeMetrics: response.results,
102
+ });
103
+ }
104
+ catch (err) {
105
+ console.error(err);
106
+ res.status(500).json({ error: 'Failed to find metrics' });
107
+ }
108
+ };
109
+ export const exportMetrics = (req, res) => {
110
+ try {
111
+ const response = inMemoryDbMetricExporter.find({
112
+ format: 'raw'
113
+ });
114
+ const responseBody = {
115
+ format: 'raw',
116
+ scopeMetricsCount: response.results.length,
117
+ scopeMetrics: response.results,
118
+ };
119
+ const payload = JSON.stringify(responseBody);
120
+ const payloadSize = Buffer.byteLength(payload, 'utf-8');
121
+ const acceptsGzip = String(req.headers['accept-encoding'] || '').includes('gzip');
122
+ const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '');
123
+ res.setHeader('Content-Type', 'application/json');
124
+ res.setHeader('Content-Disposition', `attachment; filename="metrics-${timestamp}.json"`);
125
+ res.setHeader('Vary', 'Accept-Encoding');
126
+ if (acceptsGzip && payloadSize > 64 * 1024) {
127
+ const compressed = gzipSync(payload);
128
+ res.setHeader('Content-Encoding', 'gzip');
129
+ res.setHeader('Content-Length', compressed.length.toString());
130
+ res.end(compressed);
131
+ return;
132
+ }
133
+ res.setHeader('Content-Length', payloadSize.toString());
134
+ res.end(payload);
135
+ }
136
+ catch (err) {
137
+ console.error('Failed to export metrics:', err);
138
+ res.status(500).send({ error: 'Failed to export metrics', details: err.message });
139
+ }
140
+ };
141
+ export const importMetrics = async (req, res) => {
142
+ const resetData = req.query.reset === 'true';
143
+ const body = req.body || {};
144
+ const format = (body.format || req.query.format || 'raw');
145
+ const importedScopeMetrics = Array.isArray(body) ? body : body.scopeMetrics;
146
+ try {
147
+ if (!Array.isArray(importedScopeMetrics)) {
148
+ res.status(400).send({ error: 'Invalid data format. Expected an array in request body or body.scopeMetrics.' });
149
+ return;
150
+ }
151
+ const cleanedMetrics = sanitizeMetricRecords(importedScopeMetrics);
152
+ importMetricsToMemory(cleanedMetrics, { reset: resetData, format });
153
+ let message = '';
154
+ if (resetData) {
155
+ message += 'Metrics Database reset. ';
156
+ }
157
+ message += `Imported ${cleanedMetrics.length} metrics (format: ${format}).`;
158
+ res.send({ message, ImportedMetricsCount: cleanedMetrics.length, format });
159
+ }
160
+ catch (err) {
161
+ console.error('Import failed:', err);
162
+ res.status(400).send({ error: 'Failed to import metrics', details: err.message });
163
+ }
164
+ };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setMetricRetentionTime, getMetricRetentionTime } from './metricsController.js';
2
+ import { resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setMetricRetentionTime, getMetricRetentionTime, getMetricsStats, findMetrics, exportMetrics, importMetrics, } from './metricsController.js';
3
3
  export const getMetricsRoutes = () => {
4
4
  const router = Router();
5
5
  // Metrics Control
@@ -9,9 +9,14 @@ export const getMetricsRoutes = () => {
9
9
  router.post('/reset', resetMetrics);
10
10
  router.post('/retention-time', setMetricRetentionTime);
11
11
  router.get('/retention-time', getMetricRetentionTime);
12
- router.get('/', listMetrics);
13
- router.post('/', insertMetricsToDb);
12
+ // Export/Import
13
+ router.get('/export', exportMetrics);
14
+ router.post('/import', importMetrics);
15
+ // Query endpoints
14
16
  router.post('/find', findMetrics);
17
+ router.get('/stats', getMetricsStats);
18
+ router.get('/', findMetrics);
19
+ router.post('/', insertMetricsToDb);
15
20
  return router;
16
21
  };
17
22
  export default getMetricsRoutes;
@@ -0,0 +1,22 @@
1
+ import { inMemoryDbMetricExporter } from '../telemetry/telemetryRegistry.js';
2
+ export function sanitizeMetricRecords(records) {
3
+ return records.map((record) => {
4
+ const { _id, ...rest } = record;
5
+ return rest;
6
+ });
7
+ }
8
+ export function importMetricsToMemory(records, options) {
9
+ if (options?.reset) {
10
+ inMemoryDbMetricExporter.reset();
11
+ }
12
+ if (!records.length) {
13
+ return 0;
14
+ }
15
+ if ((options?.format || 'raw') === 'otel') {
16
+ inMemoryDbMetricExporter.insertOtel(records);
17
+ }
18
+ else {
19
+ inMemoryDbMetricExporter.insertRaw(records);
20
+ }
21
+ return records.length;
22
+ }
@@ -5,7 +5,6 @@ import logger from "../utils/logger.js";
5
5
  import { pluginService } from "./pluginService.js";
6
6
  import { fileURLToPath } from "url";
7
7
  export const listPlugins = (req, res) => {
8
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
8
  const plugins = pluginService.getPlugins().map(({ process, ...rest }) => rest);
10
9
  res.send({
11
10
  pluginsCount: plugins.length,
@@ -56,16 +55,12 @@ export const registerPlugin = async (req, res) => {
56
55
  return;
57
56
  }
58
57
  const isCjs = typeof __filename !== "undefined" && typeof __dirname !== "undefined";
59
- const __filenameUniversal = isCjs
60
- ? __filename
61
- : fileURLToPath(import.meta.url);
62
- const __dirnameUniversal = isCjs
63
- ? __dirname
64
- : path.dirname(__filenameUniversal);
58
+ // @ts-ignore -- import.meta no existe en el build CJS
59
+ const currentDirectory = isCjs ? __dirname : path.dirname(fileURLToPath(import.meta.url));
65
60
  const pluginProcessFile = isCjs
66
61
  ? "pluginProcess.cjs"
67
62
  : "pluginProcess.js";
68
- const child = fork(path.resolve(__dirnameUniversal, pluginProcessFile), [], {
63
+ const child = fork(path.resolve(currentDirectory, pluginProcessFile), [], {
69
64
  stdio: ["pipe", "pipe", "pipe", "ipc"],
70
65
  });
71
66
  child.stdout?.on("data", (data) => {
@@ -108,7 +103,7 @@ export const registerPlugin = async (req, res) => {
108
103
  });
109
104
  };
110
105
  export const activatePlugin = (req, res) => {
111
- const { id } = req.params;
106
+ const id = req.params.id;
112
107
  const plugin = pluginService.getPlugins().find((p) => p.id === id);
113
108
  if (!plugin) {
114
109
  res.status(404).send(`Plugin with id "${id}" not found.`);
@@ -118,7 +113,7 @@ export const activatePlugin = (req, res) => {
118
113
  res.status(200).send(`Plugin "${id}" activated.`);
119
114
  };
120
115
  export const deactivatePlugin = (req, res) => {
121
- const { id } = req.params;
116
+ const id = req.params.id;
122
117
  const plugin = pluginService.getPlugins().find((p) => p.id === id);
123
118
  if (!plugin) {
124
119
  res.status(404).send(`Plugin with id "${id}" not found.`);
@@ -128,7 +123,7 @@ export const deactivatePlugin = (req, res) => {
128
123
  res.status(200).send(`Plugin "${id}" deactivated.`);
129
124
  };
130
125
  export const deletePlugin = (req, res) => {
131
- const { id } = req.params;
126
+ const id = req.params.id;
132
127
  const plugin = pluginService.getPlugins().find((p) => p.id === id);
133
128
  if (!plugin) {
134
129
  res.status(404).send(`Plugin with id "${id}" not found.`);
@@ -1,9 +1,7 @@
1
1
  import logger from "../utils/logger.js";
2
2
  class PluginService {
3
- constructor() {
4
- this.plugins = [];
5
- this.enabled = false;
6
- }
3
+ plugins = [];
4
+ enabled = false;
7
5
  getPlugins() {
8
6
  return this.plugins;
9
7
  }
@@ -1,22 +1,24 @@
1
1
  import { inMemoryDbSpanExporter } from '../telemetry/telemetryRegistry.js';
2
2
  import { convertRegexRecursively } from '../utils/regexUtils.js';
3
- export const startTraces = (req, res) => {
3
+ import { importTracesToMemory, sanitizeTraceRecords } from './traceService.js';
4
+ import { gzipSync } from 'zlib';
5
+ export const startTraces = (_req, res) => {
4
6
  inMemoryDbSpanExporter.enable();
5
7
  res.send('Traces started');
6
8
  };
7
- export const stopTraces = (req, res) => {
9
+ export const stopTraces = (_req, res) => {
8
10
  inMemoryDbSpanExporter.disable();
9
11
  res.send('Traces stopped');
10
12
  };
11
- export const statusTraces = (req, res) => {
13
+ export const statusTraces = (_req, res) => {
12
14
  const isRunning = inMemoryDbSpanExporter.isEnabled() || false;
13
15
  res.send({ active: isRunning });
14
16
  };
15
- export const resetTraces = (req, res) => {
17
+ export const resetTraces = (_req, res) => {
16
18
  inMemoryDbSpanExporter.reset();
17
19
  res.send('Traces reset');
18
20
  };
19
- export const listTraces = async (req, res) => {
21
+ export const listTraces = async (_req, res) => {
20
22
  try {
21
23
  const spans = inMemoryDbSpanExporter.getFinishedSpans();
22
24
  res.send({ spansCount: spans.length, spans: spans });
@@ -26,55 +28,51 @@ export const listTraces = async (req, res) => {
26
28
  res.status(500).send({ error: 'Failed to list traces data' });
27
29
  }
28
30
  };
29
- export const findTraces = (req, res) => {
30
- const body = req.body;
31
- const query = body?.query ? body.query : {};
31
+ export const findTraces = async (req, res) => {
32
+ const body = req.body || {};
33
+ const findQuery = body.query || {};
34
+ const limit = parseInt(body.limit) || 50;
35
+ const sortOrder = body.sort || null;
32
36
  let processedQuery;
33
37
  try {
34
- processedQuery = convertRegexRecursively(query);
38
+ processedQuery = convertRegexRecursively(findQuery);
35
39
  }
36
40
  catch (error) {
37
41
  console.error(error.message);
38
42
  res.status(400).send({ error: error.message });
39
43
  return; // Exit if invalid regex was encountered
40
44
  }
41
- inMemoryDbSpanExporter.find(processedQuery, (err, docs) => {
42
- if (err) {
43
- console.error(err);
44
- res.status(400).send({ spansCount: 0, spans: [], error: err.message });
45
- return; // Exit the function to prevent further execution
46
- }
47
- const spans = docs;
48
- res.send({ spansCount: spans.length, spans: spans });
49
- });
45
+ try {
46
+ const findConfig = {
47
+ query: processedQuery,
48
+ limit,
49
+ sortOrder
50
+ };
51
+ const docs = await inMemoryDbSpanExporter.find(findConfig);
52
+ res.send({
53
+ spansCount: docs.length,
54
+ spans: docs,
55
+ });
56
+ }
57
+ catch (err) {
58
+ console.error(err);
59
+ res.status(500).send({ spansCount: 0, spans: [], error: err.message });
60
+ }
50
61
  };
51
62
  export const insertTracesToDb = async (req, res) => {
52
- const jsonContent = req.body.spans;
63
+ const jsonContent = (req.body || {}).spans;
53
64
  const resetData = req.query.reset === 'true';
54
65
  if (!Array.isArray(jsonContent)) {
55
66
  res.status(400).send({ error: 'Invalid data format.' });
56
67
  return;
57
68
  }
58
- const cleanedTraces = jsonContent.map((trace) => {
59
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
60
- const { _id, ...rest } = trace; // Remove _id if it exists
61
- return rest; // Return the cleaned trace object
62
- });
69
+ const cleanedTraces = sanitizeTraceRecords(jsonContent);
63
70
  try {
64
71
  let message = '';
72
+ await importTracesToMemory(cleanedTraces, { reset: resetData });
65
73
  if (resetData) {
66
- inMemoryDbSpanExporter.reset();
67
74
  message += 'Traces Database reset. ';
68
75
  }
69
- await new Promise((resolve, reject) => {
70
- inMemoryDbSpanExporter.insert(cleanedTraces, (err, newDocs) => {
71
- if (err) {
72
- console.error('Error inserting traces:', err);
73
- return reject(err);
74
- }
75
- resolve(newDocs);
76
- });
77
- });
78
76
  message += `Inserted ${cleanedTraces.length} traces.`;
79
77
  res.send({ message, InsertedTracesCount: cleanedTraces.length });
80
78
  }
@@ -83,8 +81,31 @@ export const insertTracesToDb = async (req, res) => {
83
81
  res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
84
82
  }
85
83
  };
84
+ export const importTraces = async (req, res) => {
85
+ const resetData = req.query.reset === 'true';
86
+ const body = req.body || {};
87
+ const importedSpans = Array.isArray(body) ? body : body.spans;
88
+ try {
89
+ if (!Array.isArray(importedSpans)) {
90
+ res.status(400).send({ error: 'Invalid data format. Expected an array in request body or body.spans.' });
91
+ return;
92
+ }
93
+ const cleanedTraces = sanitizeTraceRecords(importedSpans);
94
+ let message = '';
95
+ await importTracesToMemory(cleanedTraces, { reset: resetData });
96
+ if (resetData) {
97
+ message += 'Traces Database reset. ';
98
+ }
99
+ message += `Imported ${cleanedTraces.length} traces.`;
100
+ res.send({ message, ImportedTracesCount: cleanedTraces.length });
101
+ }
102
+ catch (err) {
103
+ console.error('Import failed:', err);
104
+ res.status(400).send({ error: 'Failed to import traces', details: err.message });
105
+ }
106
+ };
86
107
  export const setTraceRetentionTime = (req, res) => {
87
- const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
108
+ const retentionTimeInSeconds = (req.body || {}).retentionTimeInSeconds;
88
109
  if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
89
110
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
111
  return;
@@ -92,7 +113,37 @@ export const setTraceRetentionTime = (req, res) => {
92
113
  inMemoryDbSpanExporter.retentionTimeInSeconds = retentionTimeInSeconds;
93
114
  res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
94
115
  };
95
- export const getTraceRetentionTime = (req, res) => {
116
+ export const getTraceRetentionTime = (_req, res) => {
96
117
  const retentionTimeInSeconds = inMemoryDbSpanExporter.retentionTimeInSeconds || 0;
97
118
  res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
98
119
  };
120
+ export const exportTraces = async (req, res) => {
121
+ try {
122
+ const findConfig = {
123
+ query: {},
124
+ sortOrder: { startTime: -1 }
125
+ };
126
+ const docs = await inMemoryDbSpanExporter.find(findConfig);
127
+ const responseBody = { spansCount: docs.length, spans: docs };
128
+ const payload = JSON.stringify(responseBody);
129
+ const payloadSize = Buffer.byteLength(payload, 'utf-8');
130
+ const acceptsGzip = String(req.headers['accept-encoding'] || '').includes('gzip');
131
+ const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '');
132
+ res.setHeader('Content-Type', 'application/json');
133
+ res.setHeader('Content-Disposition', `attachment; filename="traces-${timestamp}.json"`);
134
+ res.setHeader('Vary', 'Accept-Encoding');
135
+ if (acceptsGzip && payloadSize > 64 * 1024) {
136
+ const compressed = gzipSync(payload);
137
+ res.setHeader('Content-Encoding', 'gzip');
138
+ res.setHeader('Content-Length', compressed.length.toString());
139
+ res.end(compressed);
140
+ return;
141
+ }
142
+ res.setHeader('Content-Length', payloadSize.toString());
143
+ res.end(payload);
144
+ }
145
+ catch (err) {
146
+ console.error('Failed to export traces:', err);
147
+ res.status(500).send({ error: 'Failed to export traces', details: err.message });
148
+ }
149
+ };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { startTraces, stopTraces, statusTraces, resetTraces, listTraces, findTraces, insertTracesToDb, setTraceRetentionTime, getTraceRetentionTime } from './traceController.js';
2
+ import { startTraces, stopTraces, statusTraces, resetTraces, listTraces, findTraces, insertTracesToDb, importTraces, setTraceRetentionTime, getTraceRetentionTime, exportTraces } from './traceController.js';
3
3
  export const getTraceRoutes = () => {
4
4
  const router = Router();
5
5
  // Telemetry Control
@@ -7,6 +7,8 @@ export const getTraceRoutes = () => {
7
7
  router.post('/stop', stopTraces);
8
8
  router.get('/status', statusTraces);
9
9
  router.post('/reset', resetTraces);
10
+ router.get('/export', exportTraces);
11
+ router.post('/import', importTraces);
10
12
  router.get('/', listTraces);
11
13
  router.post('/', insertTracesToDb);
12
14
  router.post('/find', findTraces);
@@ -0,0 +1,25 @@
1
+ import { inMemoryDbSpanExporter } from '../telemetry/telemetryRegistry.js';
2
+ export function sanitizeTraceRecords(records) {
3
+ return records.map((record) => {
4
+ const { _id, ...rest } = record;
5
+ return rest;
6
+ });
7
+ }
8
+ export async function importTracesToMemory(records, options) {
9
+ if (options?.reset) {
10
+ inMemoryDbSpanExporter.reset();
11
+ }
12
+ if (!records.length) {
13
+ return 0;
14
+ }
15
+ await new Promise((resolve, reject) => {
16
+ inMemoryDbSpanExporter.insert(records, (err) => {
17
+ if (err) {
18
+ reject(err);
19
+ return;
20
+ }
21
+ resolve();
22
+ });
23
+ });
24
+ return records.length;
25
+ }
@@ -11,17 +11,17 @@ export const getUIRoutes = () => {
11
11
  relativePath = '../../dist/ui';
12
12
  logger.warn('🚧 This process is serving the OASTLM UI from the build directory, but you are in development mode. For live updates, run the React app separately and access it at http://localhost:5173/.');
13
13
  }
14
- const customFilename = fileURLToPath(import.meta.url);
15
- const customDirname = path.dirname(customFilename);
16
- const staticFilesPath = path.join(customDirname, relativePath);
14
+ const isCjs = typeof __filename !== "undefined" && typeof __dirname !== "undefined";
15
+ // @ts-ignore -- import.meta no existe en el build CJS
16
+ const currentDirectory = isCjs ? __dirname : path.dirname(fileURLToPath(import.meta.url));
17
+ const staticFilesPath = path.join(currentDirectory, relativePath);
17
18
  const router = Router();
18
19
  // This only works once the app is built: src/ --> dist/esm/
19
20
  // This file: dist/esm/routes/
20
21
  // UI bundle: dist/ui/
21
22
  // For development, the UI is served separately.
22
23
  router.use(express.static(staticFilesPath));
23
- router.get('*', (_req, res) => {
24
- // Serve the index.html file for all routes
24
+ router.use((_req, res) => {
25
25
  res.sendFile(path.join(staticFilesPath, 'index.html'));
26
26
  });
27
27
  return router;
@@ -47,24 +47,18 @@ export const specLoader = (_req, res, oasTlmConfig) => {
47
47
  export const heapStats = (req, res) => {
48
48
  const heapStats = v8.getHeapStatistics();
49
49
  const roundedHeapStats = Object.getOwnPropertyNames(heapStats).reduce(function (map, stat) {
50
- //@ts-expect-error yes
51
50
  map[stat] = Math.round((heapStats[stat] / 1024 / 1024) * 1000) / 1000;
52
51
  return map;
53
52
  }, {});
54
- // @ts-expect-error yes
55
53
  roundedHeapStats['units'] = 'MB';
56
54
  res.send(roundedHeapStats);
57
55
  };
58
56
  const isCjs = typeof __filename !== "undefined" && typeof __dirname !== "undefined";
59
- const __filenameUniversal = isCjs
60
- ? __filename
61
- : fileURLToPath(import.meta.url);
62
- const __dirnameUniversal = isCjs
63
- ? __dirname
64
- : path.dirname(__filenameUniversal);
57
+ // @ts-ignore -- import.meta no existe en el build CJS
58
+ const currentDirectory = isCjs ? __dirname : path.dirname(fileURLToPath(import.meta.url));
65
59
  export const getOasTelemetrySpec = (_req, res) => {
66
60
  try {
67
- const specPath = path.join(__dirnameUniversal, '../docs/openapi.yaml');
61
+ const specPath = path.join(currentDirectory, '../docs/openapi.yaml');
68
62
  const data = readFileSync(specPath, { encoding: 'utf8', flag: 'r' });
69
63
  let json = data;
70
64
  json = JSON.stringify(yaml.load(data), null, 2);
@@ -27,8 +27,8 @@ export const getUtilsRoutes = (oasTlmConfig) => {
27
27
  res.send({ message: 'Started generating mock logs' });
28
28
  });
29
29
  // This route is NOT ignored by the spanExporter
30
- router.get('/generate-wait/:seconds?', async (req, res) => {
31
- const seconds = parseInt(req.params.seconds ?? "1", 10);
30
+ router.get('/generate-wait', async (req, res) => {
31
+ const seconds = parseInt(req.query.seconds ?? "1", 10);
32
32
  const waitTime = isNaN(seconds) ? 1 : seconds;
33
33
  await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
34
34
  res.send({ waited: waitTime });
@@ -3,4 +3,7 @@ export declare const bootEnvVariables: {
3
3
  OASTLM_BOOT_MODULE_DISABLED: boolean;
4
4
  OASTLM_BOOT_LOG_LEVEL: string;
5
5
  OASTLM_BOOT_SERVICE_NAME: string;
6
+ OASTLM_BOOT_BASE_URL: string;
7
+ OASTLM_BOOT_AUTOINSTRUMENTATIONS_NODE_DISABLED: boolean;
8
+ OASTLM_BOOT_AUTOINSTRUMENTATIONS_LOGS_DISABLED: boolean;
6
9
  };