@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,12 +1,13 @@
1
- import { globalOasTlmConfig } from "../config.js";
2
1
  import jwt from 'jsonwebtoken';
3
- export function authMiddleware(req, res, next) {
4
- const apiKey = req.cookies.apiKey;
5
- if (apiKey) {
6
- const decoded = jwt.verify(apiKey, globalOasTlmConfig.jwtSecret);
7
- if (decoded.password === globalOasTlmConfig.password) {
8
- return next();
2
+ export function getAuthMiddleware(oasTlmConfig) {
3
+ return function authMiddleware(req, res, next) {
4
+ const apiKey = req.cookies.apiKey;
5
+ if (apiKey) {
6
+ const decoded = jwt.verify(apiKey, oasTlmConfig.auth.jwtSecret);
7
+ if (decoded.password === oasTlmConfig.auth.password) {
8
+ return next();
9
+ }
9
10
  }
10
- }
11
- res.status(401).redirect(globalOasTlmConfig.baseURL + '/login');
11
+ res.status(401).redirect(oasTlmConfig.general.baseUrl + oasTlmConfig.general.uiPath + '/login');
12
+ };
12
13
  }
@@ -1,7 +1,9 @@
1
1
  import { Router } from 'express';
2
- import { login, logout, check } from './authController.js';
3
- const router = Router();
4
- router.post('/login', login);
5
- router.get('/logout', logout);
6
- router.get('/check', check);
7
- export default router;
2
+ import { getLogin, getLogout, getCheck } from './authController.js';
3
+ export const getAuthRoutes = (oasTlmConfig) => {
4
+ const router = Router();
5
+ router.post('/login', getLogin(oasTlmConfig));
6
+ router.get('/logout', getLogout(oasTlmConfig));
7
+ router.get('/check', getCheck(oasTlmConfig));
8
+ return router;
9
+ };
@@ -1,22 +1,33 @@
1
- import { globalOasTlmConfig } from '../config.js';
1
+ import { inMemoryDbLogExporter } from '../telemetry/telemetryRegistry.js';
2
+ import logger from '../utils/logger.js';
3
+ import { convertRegexRecursively } from '../utils/regexUtils.js';
2
4
  export const listLogs = async (req, res) => {
3
5
  try {
4
- const logs = globalOasTlmConfig.logExporter.getFinishedLogs();
6
+ const logs = inMemoryDbLogExporter.getFinishedLogs();
5
7
  res.send({ logsCount: logs.length, logs: logs });
6
8
  }
7
9
  catch (err) {
8
- console.error(err);
10
+ logger.error(err);
9
11
  res.status(500).send({ error: 'Failed to list log data' });
10
12
  }
11
13
  };
12
14
  export const findLogs = async (req, res) => {
13
15
  const body = req.body;
14
- const messageSearch = body?.search || null; // Search term for MiniSearch
15
- const findQuery = body?.find || {}; // Query for NeDB
16
- console.dir(`findLogs called with query: ${JSON.stringify(findQuery)} and search ${messageSearch}`, { depth: 3 });
16
+ const messageSearch = body?.textSearch || null; // Search term for MiniSearch
17
+ const findQuery = body?.query || {}; // Query for NeDB
18
+ logger.debug(`findLogs called with query: ${JSON.stringify(findQuery)} and search ${messageSearch}`, { depth: 3 });
19
+ let processedQuery;
20
+ try {
21
+ processedQuery = convertRegexRecursively(findQuery);
22
+ }
23
+ catch (error) {
24
+ logger.error(error.message);
25
+ res.status(400).send({ error: error.message });
26
+ return; // Exit if invalid regex was encountered
27
+ }
17
28
  try {
18
29
  const results = await new Promise((resolve, reject) => {
19
- globalOasTlmConfig.logExporter.find(findQuery, messageSearch, (err, docs) => {
30
+ inMemoryDbLogExporter.find(processedQuery, messageSearch, (err, docs) => {
20
31
  if (err)
21
32
  return reject(err);
22
33
  resolve(docs);
@@ -26,12 +37,12 @@ export const findLogs = async (req, res) => {
26
37
  res.send({ logsCount: typedResults.length, logs: typedResults });
27
38
  }
28
39
  catch (err) {
29
- console.error(err);
40
+ logger.error(err);
30
41
  res.status(500).send({ error: 'Failed to find logs', details: err.message });
31
42
  }
32
43
  };
33
44
  export const resetLogs = (req, res) => {
34
- globalOasTlmConfig.logExporter.reset();
45
+ inMemoryDbLogExporter.reset();
35
46
  res.send('Logs reset');
36
47
  };
37
48
  export const insertLogsToDb = async (req, res) => {
@@ -49,13 +60,13 @@ export const insertLogsToDb = async (req, res) => {
49
60
  try {
50
61
  let message = '';
51
62
  if (resetData) {
52
- globalOasTlmConfig.logExporter.reset();
63
+ inMemoryDbLogExporter.reset();
53
64
  message += 'Logs Database reset. ';
54
65
  }
55
66
  await new Promise((resolve, reject) => {
56
- globalOasTlmConfig.logExporter.insert(cleanedLogs, (err, newDocs) => {
67
+ inMemoryDbLogExporter.insert(cleanedLogs, (err, newDocs) => {
57
68
  if (err) {
58
- console.error('Error inserting logs:', err);
69
+ logger.error('Error inserting logs:', err);
59
70
  return reject(err);
60
71
  }
61
72
  resolve(newDocs);
@@ -65,19 +76,28 @@ export const insertLogsToDb = async (req, res) => {
65
76
  res.send({ message, InsertedLogsCount: cleanedLogs.length });
66
77
  }
67
78
  catch (err) {
68
- console.error(err);
79
+ logger.error(err);
69
80
  res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
70
81
  }
71
82
  };
72
83
  export const startLogs = (req, res) => {
73
- globalOasTlmConfig.logExporter.start();
84
+ inMemoryDbLogExporter.enable();
74
85
  res.send('Log collection started');
75
86
  };
76
87
  export const stopLogs = (req, res) => {
77
- globalOasTlmConfig.logExporter.stop();
88
+ inMemoryDbLogExporter.disable();
78
89
  res.send('Log collection stopped');
79
90
  };
80
91
  export const statusLogs = (req, res) => {
81
- const isRunning = globalOasTlmConfig.logExporter.isRunning() || false;
92
+ const isRunning = inMemoryDbLogExporter.isEnabled() || false;
82
93
  res.send({ active: isRunning });
83
94
  };
95
+ export const setRetentionTimeLogs = (req, res) => {
96
+ const retentionTime = req.body.retentionTime;
97
+ if (typeof retentionTime !== 'number' || retentionTime <= 0) {
98
+ res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
99
+ return;
100
+ }
101
+ inMemoryDbLogExporter.retentionTimeInSeconds = retentionTime;
102
+ res.send({ message: `Retention time set to ${retentionTime} seconds.` });
103
+ };
@@ -1,12 +1,16 @@
1
1
  import { Router } from 'express';
2
- import { startLogs, stopLogs, statusLogs, resetLogs, listLogs, findLogs, insertLogsToDb } from './logController.js';
3
- export const logRoutes = Router();
4
- // Logs Control
5
- logRoutes.get('/start', startLogs);
6
- logRoutes.get('/stop', stopLogs);
7
- logRoutes.get('/status', statusLogs);
8
- logRoutes.get('/reset', resetLogs);
9
- logRoutes.get('/', listLogs);
10
- logRoutes.post('/', insertLogsToDb);
11
- logRoutes.post('/find', findLogs);
12
- export default logRoutes;
2
+ import { startLogs, stopLogs, statusLogs, resetLogs, listLogs, findLogs, insertLogsToDb, setRetentionTimeLogs } from './logController.js';
3
+ export const getLogRoutes = () => {
4
+ const router = Router();
5
+ // Logs Control
6
+ router.post('/start', startLogs);
7
+ router.post('/stop', stopLogs);
8
+ router.get('/status', statusLogs);
9
+ router.post('/reset', resetLogs);
10
+ router.post('/retention-time', setRetentionTimeLogs);
11
+ router.get('/', listLogs);
12
+ router.post('/', insertLogsToDb);
13
+ router.post('/find', findLogs);
14
+ return router;
15
+ };
16
+ export default getLogRoutes;
@@ -1,7 +1,8 @@
1
- import { globalOasTlmConfig } from '../config.js';
1
+ import { inMemoryDbMetricExporter } from '../telemetry/telemetryRegistry.js';
2
+ import { convertRegexRecursively } from '../utils/regexUtils.js';
2
3
  export const listMetrics = async (req, res) => {
3
4
  try {
4
- const metrics = globalOasTlmConfig.metricsExporter.getFinishedMetrics();
5
+ const metrics = inMemoryDbMetricExporter.getFinishedMetrics();
5
6
  res.send({ metricsCount: metrics.length, metrics: metrics });
6
7
  }
7
8
  catch (err) {
@@ -11,8 +12,17 @@ export const listMetrics = async (req, res) => {
11
12
  };
12
13
  export const findMetrics = (req, res) => {
13
14
  const body = req.body;
14
- const search = body?.search ? body.search : {};
15
- globalOasTlmConfig.metricsExporter.find(search, (err, docs) => {
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) => {
16
26
  if (err) {
17
27
  console.error(err);
18
28
  res.status(404).send({ metricsCount: 0, metrics: [], error: err });
@@ -23,7 +33,7 @@ export const findMetrics = (req, res) => {
23
33
  });
24
34
  };
25
35
  export const resetMetrics = (req, res) => {
26
- globalOasTlmConfig.metricsExporter.reset();
36
+ inMemoryDbMetricExporter.reset();
27
37
  res.send('Metrics reset');
28
38
  };
29
39
  export const insertMetricsToDb = async (req, res) => {
@@ -41,11 +51,11 @@ export const insertMetricsToDb = async (req, res) => {
41
51
  try {
42
52
  let message = '';
43
53
  if (resetData) {
44
- globalOasTlmConfig.metricsExporter.reset();
54
+ inMemoryDbMetricExporter.reset();
45
55
  message += 'Metrics Database reset. ';
46
56
  }
47
57
  await new Promise((resolve, reject) => {
48
- globalOasTlmConfig.metricsExporter.insert(cleanedMetrics, (err, newDocs) => {
58
+ inMemoryDbMetricExporter.insert(cleanedMetrics, (err, newDocs) => {
49
59
  if (err) {
50
60
  console.error('Error inserting metrics:', err);
51
61
  return reject(err);
@@ -62,14 +72,23 @@ export const insertMetricsToDb = async (req, res) => {
62
72
  }
63
73
  };
64
74
  export const startMetrics = (req, res) => {
65
- globalOasTlmConfig.metricsExporter.start();
75
+ inMemoryDbMetricExporter.enable();
66
76
  res.send('Metrics collection started');
67
77
  };
68
78
  export const stopMetrics = (req, res) => {
69
- globalOasTlmConfig.metricsExporter.stop();
79
+ inMemoryDbMetricExporter.disable();
70
80
  res.send('Metrics collection stopped');
71
81
  };
72
82
  export const statusMetrics = (req, res) => {
73
- const isRunning = globalOasTlmConfig.metricsExporter.isRunning() || false;
83
+ const isRunning = inMemoryDbMetricExporter.isEnabled() || false;
74
84
  res.send({ active: isRunning });
75
85
  };
86
+ export const setRetentionTimeMetrics = (req, res) => {
87
+ const retentionTime = req.body.retentionTime;
88
+ if (typeof retentionTime !== 'number' || retentionTime <= 0) {
89
+ res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
+ return;
91
+ }
92
+ inMemoryDbMetricExporter.retentionTimeInSeconds = retentionTime;
93
+ res.send({ message: `Retention time set to ${retentionTime} seconds.` });
94
+ };
@@ -1,12 +1,16 @@
1
1
  import { Router } from 'express';
2
- import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics } from './metricsController.js';
3
- export const metricsRoutes = Router();
4
- // Metrics Control
5
- metricsRoutes.get('/', listMetrics);
6
- metricsRoutes.post('/', insertMetricsToDb);
7
- metricsRoutes.post('/find', findMetrics);
8
- metricsRoutes.get('/reset', resetMetrics);
9
- metricsRoutes.get('/start', startMetrics);
10
- metricsRoutes.get('/stop', stopMetrics);
11
- metricsRoutes.get('/status', statusMetrics);
12
- export default metricsRoutes;
2
+ import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setRetentionTimeMetrics } from './metricsController.js';
3
+ export const getMetricsRoutes = () => {
4
+ const router = Router();
5
+ // Metrics Control
6
+ router.post('/start', startMetrics);
7
+ router.post('/stop', stopMetrics);
8
+ router.get('/status', statusMetrics);
9
+ router.post('/reset', resetMetrics);
10
+ router.post('/retention-time', setRetentionTimeMetrics);
11
+ router.get('/', listMetrics);
12
+ router.post('/', insertMetricsToDb);
13
+ router.post('/find', findMetrics);
14
+ return router;
15
+ };
16
+ export default getMetricsRoutes;
@@ -1,29 +1,51 @@
1
- import { globalOasTlmConfig } from '../config.js';
2
1
  import axios from 'axios';
3
2
  // @ts-expect-error: import-from-string does not have proper type declarations
4
3
  import { importFromString, requireFromString } from 'import-from-string';
5
4
  // @ts-expect-error: dynamic-installer does not have proper type declarations
6
5
  import { installDependencies } from 'dynamic-installer';
7
6
  import logger from '../utils/logger.js';
7
+ import { pluginService } from './pluginService.js';
8
8
  export const listPlugins = (req, res) => {
9
- res.send(globalOasTlmConfig.dynamicSpanExporter.getPlugins().map((plugin) => {
9
+ const plugins = pluginService.getPlugins().map((plugin) => {
10
10
  return {
11
11
  id: plugin.id,
12
12
  name: plugin.name,
13
13
  url: plugin.url,
14
14
  active: plugin.active
15
15
  };
16
- }));
16
+ });
17
+ res.send({
18
+ pluginsCount: plugins.length,
19
+ plugins: plugins
20
+ });
17
21
  };
18
22
  export const registerPlugin = async (req, res) => {
19
- const pluginResource = req.body;
20
- logger.info(`Plugin Registration Request: = ${JSON.stringify(req.body, null, 2)}...`);
21
- logger.info(`Getting plugin at ${pluginResource.url}...`);
22
23
  let pluginCode;
24
+ const pluginResource = req.body;
25
+ logger.debug(`Plugin Registration Request: = ${JSON.stringify(req.body, null, 2)}...`);
26
+ // Validate plugin id
27
+ if (!pluginResource.id || typeof pluginResource.id !== "string") {
28
+ res.status(400).send(`Plugin id must be provided and must be a string`);
29
+ return;
30
+ }
31
+ // Check for duplicate plugin id
32
+ const existingPlugin = pluginService.getPlugins().find((plugin) => plugin.id === pluginResource.id);
33
+ if (existingPlugin) {
34
+ res.status(400).send(`A plugin with id "${pluginResource.id}" already exists.`);
35
+ return;
36
+ }
23
37
  if (!pluginResource.url && !pluginResource.code) {
24
38
  res.status(400).send(`Plugin code or URL must be provided`);
25
39
  return;
26
40
  }
41
+ if (!pluginResource.moduleFormat) {
42
+ res.status(400).send(`Plugin moduleFormat must be provided (e.g., "cjs" or "esm")`);
43
+ return;
44
+ }
45
+ if (!["cjs", "esm"].includes(pluginResource.moduleFormat.toLowerCase())) {
46
+ res.status(400).send(`Invalid moduleFormat "${pluginResource.moduleFormat}". Supported formats are "cjs" and "esm".`);
47
+ return;
48
+ }
27
49
  let module;
28
50
  try {
29
51
  if (pluginResource.code) {
@@ -38,6 +60,7 @@ export const registerPlugin = async (req, res) => {
38
60
  return;
39
61
  }
40
62
  if (pluginResource.install) {
63
+ logger.info("Installing dependencies for plugin: " + pluginResource.name);
41
64
  const dependenciesStatus = await installDependencies(pluginResource.install);
42
65
  if (!dependenciesStatus.success) {
43
66
  if (pluginResource.install.ignoreErrors === true) {
@@ -49,16 +72,14 @@ export const registerPlugin = async (req, res) => {
49
72
  }
50
73
  }
51
74
  }
52
- logger.info("Plugin size: " + pluginCode?.length);
53
- logger.info("Plugin format: " + pluginResource?.moduleFormat);
54
- if (pluginResource?.moduleFormat && pluginResource.moduleFormat.toUpperCase() == "ESM") {
75
+ logger.debug("Plugin format (provided): " + pluginResource?.moduleFormat);
76
+ if (pluginResource.moduleFormat.toLowerCase() === "esm") {
55
77
  logger.info("ESM detected");
56
78
  module = await importFromString(pluginCode);
57
79
  }
58
80
  else {
59
81
  logger.info("CJS detected (default)");
60
82
  module = await requireFromString(pluginCode);
61
- logger.info(module);
62
83
  }
63
84
  }
64
85
  catch (error) {
@@ -67,14 +88,14 @@ export const registerPlugin = async (req, res) => {
67
88
  return;
68
89
  }
69
90
  const plugin = module.default?.plugin ?? module.plugin;
70
- if (plugin == undefined) {
71
- res.status(400).send(`Plugin code should export a "plugin" object`);
72
- logger.info("Error in plugin code: no plugin object exported");
91
+ if (!plugin) {
92
+ res.status(400).send(`Plugin code should export a valid "plugin" object or static class`);
93
+ logger.info("Error in plugin code: no valid plugin object exported");
73
94
  return;
74
95
  }
75
96
  for (const requiredFunction of ["load", "getName", "isConfigured"]) {
76
- if (plugin[requiredFunction] == undefined) {
77
- res.status(400).send(`The plugin code exports a "plugin" object, however it should have a "${requiredFunction}" method`);
97
+ if (typeof plugin[requiredFunction] !== "function") {
98
+ res.status(400).send(`The plugin code exports a "plugin" object, but it must have a "${requiredFunction}" method`);
78
99
  logger.info("Error in plugin code: some required functions are missing");
79
100
  return;
80
101
  }
@@ -89,15 +110,15 @@ export const registerPlugin = async (req, res) => {
89
110
  }
90
111
  if (plugin.isConfigured()) {
91
112
  logger.info(`Loaded plugin <${plugin.getName()}>`);
92
- pluginResource.plugin = plugin;
113
+ pluginResource.pluginImplementation = plugin;
93
114
  pluginResource.name = plugin.getName();
94
115
  pluginResource.active = true;
95
- globalOasTlmConfig.dynamicSpanExporter.pushPlugin(pluginResource);
96
- globalOasTlmConfig.dynamicSpanExporter.activatePlugin(pluginResource.plugin);
116
+ pluginService.pushPlugin(pluginResource);
117
+ pluginService.activatePlugin(pluginResource);
97
118
  res.status(201).send(`Plugin registered`);
98
119
  }
99
120
  else {
100
- logger.error(`Plugin <${plugin.getName()}> can not be configured`);
121
+ logger.error(`Plugin <${plugin.getName()}> cannot be configured`);
101
122
  res.status(400).send(`Plugin configuration problem`);
102
123
  }
103
124
  };
@@ -1,7 +1,8 @@
1
1
  import { Router } from 'express';
2
2
  import { listPlugins, registerPlugin } from './pluginController.js';
3
- export const pluginRoutes = Router();
4
- // Plugins
5
- pluginRoutes.get('/plugins', listPlugins);
6
- pluginRoutes.post('/plugins', registerPlugin);
7
- export default pluginRoutes;
3
+ export const getPluginRoutes = () => {
4
+ const router = Router();
5
+ router.get('/', listPlugins);
6
+ router.post('/', registerPlugin);
7
+ return router;
8
+ };
@@ -0,0 +1,19 @@
1
+ class PluginService {
2
+ constructor() {
3
+ this.plugins = [];
4
+ }
5
+ getPlugins() {
6
+ return this.plugins;
7
+ }
8
+ pushPlugin(plugin) {
9
+ this.plugins.push(plugin);
10
+ }
11
+ activatePlugin(plugin) {
12
+ this.plugins.forEach(p => {
13
+ if (p.id === plugin.id) {
14
+ p.active = true;
15
+ }
16
+ });
17
+ }
18
+ }
19
+ export const pluginService = new PluginService();
@@ -1,52 +1,48 @@
1
- import { globalOasTlmConfig } from '../config.js';
2
- export const startTelemetry = (req, res) => {
3
- globalOasTlmConfig.dynamicSpanExporter.exporter.start();
4
- res.send('Telemetry started');
1
+ import { inMemoryDbSpanExporter } from '../telemetry/telemetryRegistry.js';
2
+ import { convertRegexRecursively } from '../utils/regexUtils.js';
3
+ export const startTraces = (req, res) => {
4
+ inMemoryDbSpanExporter.enable();
5
+ res.send('Traces started');
5
6
  };
6
- export const stopTelemetry = (req, res) => {
7
- globalOasTlmConfig.dynamicSpanExporter.exporter.stop();
8
- res.send('Telemetry stopped');
7
+ export const stopTraces = (req, res) => {
8
+ inMemoryDbSpanExporter.disable();
9
+ res.send('Traces stopped');
9
10
  };
10
- export const statusTelemetry = (req, res) => {
11
- const isRunning = globalOasTlmConfig.dynamicSpanExporter.exporter.isRunning() || false;
11
+ export const statusTraces = (req, res) => {
12
+ const isRunning = inMemoryDbSpanExporter.isEnabled() || false;
12
13
  res.send({ active: isRunning });
13
14
  };
14
- export const resetTelemetry = (req, res) => {
15
- globalOasTlmConfig.dynamicSpanExporter.exporter.reset();
16
- res.send('Telemetry reset');
15
+ export const resetTraces = (req, res) => {
16
+ inMemoryDbSpanExporter.reset();
17
+ res.send('Traces reset');
17
18
  };
18
- export const listTelemetry = async (req, res) => {
19
+ export const listTraces = async (req, res) => {
19
20
  try {
20
- const spans = await globalOasTlmConfig.dynamicSpanExporter.exporter.getFinishedSpans();
21
+ const spans = inMemoryDbSpanExporter.getFinishedSpans();
21
22
  res.send({ spansCount: spans.length, spans: spans });
22
23
  }
23
24
  catch (err) {
24
25
  console.error(err);
25
- res.status(500).send({ error: 'Failed to list telemetry data' });
26
+ res.status(500).send({ error: 'Failed to list traces data' });
26
27
  }
27
28
  };
28
- export const findTelemetry = (req, res) => {
29
+ export const findTraces = (req, res) => {
29
30
  const body = req.body;
30
- const search = body?.search ? body.search : {};
31
- if (body?.flags?.containsRegex) {
32
- try {
33
- //@ts-expect-error yes
34
- body.config?.regexIds?.forEach(regexId => {
35
- if (search[regexId])
36
- search[regexId] = new RegExp(search[regexId]);
37
- });
38
- }
39
- catch (e) {
40
- console.error(e);
41
- res.status(404).send({ spansCount: 0, spans: [], error: e });
42
- return;
43
- }
31
+ const query = body?.query ? body.query : {};
32
+ let processedQuery;
33
+ try {
34
+ processedQuery = convertRegexRecursively(query);
44
35
  }
45
- globalOasTlmConfig.dynamicSpanExporter.exporter.find(search, (err, docs) => {
36
+ catch (error) {
37
+ console.error(error.message);
38
+ res.status(400).send({ error: error.message });
39
+ return; // Exit if invalid regex was encountered
40
+ }
41
+ inMemoryDbSpanExporter.find(processedQuery, (err, docs) => {
46
42
  if (err) {
47
43
  console.error(err);
48
- res.status(404).send({ spansCount: 0, spans: [], error: err });
49
- return;
44
+ res.status(404).send({ spansCount: 0, spans: [], error: err.message });
45
+ return; // Exit the function to prevent further execution
50
46
  }
51
47
  const spans = docs;
52
48
  res.send({ spansCount: spans.length, spans: spans });
@@ -67,11 +63,11 @@ export const insertTracesToDb = async (req, res) => {
67
63
  try {
68
64
  let message = '';
69
65
  if (resetData) {
70
- globalOasTlmConfig.dynamicSpanExporter.exporter.reset();
66
+ inMemoryDbSpanExporter.reset();
71
67
  message += 'Traces Database reset. ';
72
68
  }
73
69
  await new Promise((resolve, reject) => {
74
- globalOasTlmConfig.dynamicSpanExporter.exporter.insert(cleanedTraces, (err, newDocs) => {
70
+ inMemoryDbSpanExporter.insert(cleanedTraces, (err, newDocs) => {
75
71
  if (err) {
76
72
  console.error('Error inserting traces:', err);
77
73
  return reject(err);
@@ -87,3 +83,12 @@ export const insertTracesToDb = async (req, res) => {
87
83
  res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
88
84
  }
89
85
  };
86
+ export const setRetentionTimeTraces = (req, res) => {
87
+ const retentionTime = req.body.retentionTime;
88
+ if (typeof retentionTime !== 'number' || retentionTime <= 0) {
89
+ res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
90
+ return;
91
+ }
92
+ inMemoryDbSpanExporter.retentionTimeInSeconds = retentionTime;
93
+ res.send({ message: `Retention time set to ${retentionTime} seconds.` });
94
+ };
@@ -1,12 +1,16 @@
1
1
  import { Router } from 'express';
2
- import { startTelemetry, stopTelemetry, statusTelemetry, resetTelemetry, listTelemetry, findTelemetry, insertTracesToDb } from './traceController.js';
3
- export const traceRoutes = Router();
4
- traceRoutes.get('/', listTelemetry);
5
- traceRoutes.post('/', insertTracesToDb);
6
- traceRoutes.post('/find', findTelemetry);
7
- // Telemetry Control
8
- traceRoutes.get('/start', startTelemetry);
9
- traceRoutes.get('/stop', stopTelemetry);
10
- traceRoutes.get('/status', statusTelemetry);
11
- traceRoutes.get('/reset', resetTelemetry);
12
- export default traceRoutes;
2
+ import { startTraces, stopTraces, statusTraces, resetTraces, listTraces, findTraces, insertTracesToDb, setRetentionTimeTraces } from './traceController.js';
3
+ export const getTraceRoutes = () => {
4
+ const router = Router();
5
+ // Telemetry Control
6
+ router.post('/start', startTraces);
7
+ router.post('/stop', stopTraces);
8
+ router.get('/status', statusTraces);
9
+ router.post('/reset', resetTraces);
10
+ router.get('/', listTraces);
11
+ router.post('/', insertTracesToDb);
12
+ router.post('/find', findTraces);
13
+ router.post('/retention-time', setRetentionTimeTraces);
14
+ return router;
15
+ };
16
+ export default getTraceRoutes;
@@ -2,22 +2,27 @@ import express, { Router } from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import logger from '../utils/logger.js';
5
- let relativePath = '../../ui';
6
- if (process.env.OASTLM_ENV === 'development') {
7
- relativePath = '../../dist/ui';
8
- 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/.');
9
- }
10
- const customFilename = fileURLToPath(import.meta.url);
11
- const customDirname = path.dirname(customFilename);
12
- const staticFilesPath = path.join(customDirname, relativePath);
13
- export const uiRoutes = Router();
14
- // This only works once the app is built: src/ --> dist/esm/
15
- // This file: dist/esm/routes/
16
- // UI bundle: dist/ui/
17
- // For development, the UI is served separately.
18
- uiRoutes.use(express.static(staticFilesPath));
19
- uiRoutes.get('*', (_req, res) => {
20
- // Serve the index.html file for all routes
21
- res.sendFile(path.join(staticFilesPath, 'index.html'));
22
- });
23
- export default uiRoutes;
5
+ import { bootEnvVariables } from '../config/bootConfig.js';
6
+ export const getUIRoutes = () => {
7
+ // This is when this file is in /dist
8
+ let relativePath = '../../ui';
9
+ // This is when this file in in /src
10
+ if (bootEnvVariables.OASTLM_BOOT_ENV === 'development') {
11
+ relativePath = '../../dist/ui';
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
+ }
14
+ const customFilename = fileURLToPath(import.meta.url);
15
+ const customDirname = path.dirname(customFilename);
16
+ const staticFilesPath = path.join(customDirname, relativePath);
17
+ const router = Router();
18
+ // This only works once the app is built: src/ --> dist/esm/
19
+ // This file: dist/esm/routes/
20
+ // UI bundle: dist/ui/
21
+ // For development, the UI is served separately.
22
+ router.use(express.static(staticFilesPath));
23
+ router.get('*', (_req, res) => {
24
+ // Serve the index.html file for all routes
25
+ res.sendFile(path.join(staticFilesPath, 'index.html'));
26
+ });
27
+ return router;
28
+ };