@oas-tools/oas-telemetry 0.6.2 → 0.7.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.
- package/README.md +442 -442
- package/dist/{config.cjs → cjs/config.cjs} +6 -7
- package/dist/{exporters → cjs/exporters}/InMemoryDBMetricsExporter.cjs +5 -40
- package/dist/cjs/exporters/InMemoryDbExporter.cjs +87 -0
- package/dist/cjs/exporters/InMemoryLogRecordExporter.cjs +110 -0
- package/dist/{exporters → cjs/exporters}/consoleExporter.cjs +2 -7
- package/dist/{exporters → cjs/exporters}/dynamicExporter.cjs +12 -19
- package/dist/cjs/index.cjs +43 -0
- package/dist/cjs/instrumentation/index.cjs +28 -0
- package/dist/cjs/instrumentation/logs.cjs +46 -0
- package/dist/cjs/instrumentation/metrics.cjs +27 -0
- package/dist/cjs/instrumentation/traces.cjs +19 -0
- package/dist/cjs/tlm-ai/agent.cjs +79 -0
- package/dist/cjs/tlm-ai/aiController.cjs +69 -0
- package/dist/cjs/tlm-ai/aiRoutes.cjs +13 -0
- package/dist/cjs/tlm-ai/knownMicroservices.cjs +13 -0
- package/dist/cjs/tlm-ai/tools.cjs +504 -0
- package/dist/{routes/authRoutes.cjs → cjs/tlm-auth/authController.cjs} +21 -28
- package/dist/{middleware → cjs/tlm-auth}/authMiddleware.cjs +1 -1
- package/dist/cjs/tlm-auth/authRoutes.cjs +14 -0
- package/dist/cjs/tlm-log/logController.cjs +55 -0
- package/dist/cjs/tlm-log/logRoutes.cjs +13 -0
- package/dist/{routes → cjs/tlm-metric}/metricsRoutes.cjs +1 -2
- package/dist/{controllers → cjs/tlm-plugin}/pluginController.cjs +31 -41
- package/dist/cjs/tlm-plugin/pluginRoutes.cjs +13 -0
- package/dist/{controllers/telemetryController.cjs → cjs/tlm-trace/traceController.cjs} +8 -19
- package/dist/cjs/tlm-trace/traceRoutes.cjs +17 -0
- package/dist/cjs/tlm-ui/uiRoutes.cjs +31 -0
- package/dist/cjs/tlm-util/utilController.cjs +63 -0
- package/dist/cjs/tlm-util/utilRoutes.cjs +12 -0
- package/dist/cjs/tlmRoutes.cjs +79 -0
- package/dist/cjs/types/index.cjs +8 -0
- package/dist/cjs/utils/circular.cjs +90 -0
- package/dist/cjs/utils/logger.cjs +28 -0
- package/{src → dist/esm}/config.js +20 -23
- package/{src → dist/esm}/exporters/InMemoryDBMetricsExporter.js +57 -111
- package/dist/esm/exporters/InMemoryDbExporter.js +87 -0
- package/dist/esm/exporters/InMemoryLogRecordExporter.js +95 -0
- package/{src → dist/esm}/exporters/consoleExporter.js +38 -47
- package/{src → dist/esm}/exporters/dynamicExporter.js +50 -62
- package/dist/esm/index.js +39 -0
- package/dist/esm/instrumentation/index.js +26 -0
- package/dist/esm/instrumentation/logs.js +34 -0
- package/dist/esm/instrumentation/metrics.js +18 -0
- package/dist/esm/instrumentation/traces.js +12 -0
- package/dist/esm/tlm-ai/agent.js +68 -0
- package/dist/esm/tlm-ai/aiController.js +45 -0
- package/dist/esm/tlm-ai/aiRoutes.js +7 -0
- package/dist/esm/tlm-ai/knownMicroservices.js +5 -0
- package/dist/esm/tlm-ai/tools.js +490 -0
- package/dist/esm/tlm-auth/authController.js +41 -0
- package/{src/middleware → dist/esm/tlm-auth}/authMiddleware.js +12 -14
- package/dist/esm/tlm-auth/authRoutes.js +7 -0
- package/dist/esm/tlm-log/logController.js +36 -0
- package/dist/esm/tlm-log/logRoutes.js +7 -0
- package/{src/controllers → dist/esm/tlm-metric}/metricsController.js +28 -30
- package/{src/routes → dist/esm/tlm-metric}/metricsRoutes.js +8 -15
- package/{src/controllers → dist/esm/tlm-plugin}/pluginController.js +103 -115
- package/dist/esm/tlm-plugin/pluginRoutes.js +7 -0
- package/dist/esm/tlm-trace/traceController.js +54 -0
- package/dist/esm/tlm-trace/traceRoutes.js +11 -0
- package/dist/esm/tlm-ui/uiRoutes.js +23 -0
- package/dist/esm/tlm-util/utilController.js +57 -0
- package/dist/esm/tlm-util/utilRoutes.js +6 -0
- package/dist/esm/tlmRoutes.js +72 -0
- package/dist/esm/types/index.js +4 -0
- package/dist/esm/utils/circular.js +84 -0
- package/dist/esm/utils/logger.js +19 -0
- package/dist/types/config.d.ts +6 -0
- package/dist/types/exporters/InMemoryDBMetricsExporter.d.ts +15 -0
- package/dist/types/exporters/InMemoryDbExporter.d.ts +24 -0
- package/dist/types/exporters/InMemoryLogRecordExporter.d.ts +27 -0
- package/dist/types/exporters/consoleExporter.d.ts +13 -0
- package/dist/types/exporters/dynamicExporter.d.ts +25 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/instrumentation/index.d.ts +1 -0
- package/dist/types/instrumentation/logs.d.ts +1 -0
- package/dist/types/instrumentation/metrics.d.ts +1 -0
- package/dist/types/instrumentation/traces.d.ts +1 -0
- package/dist/types/tlm-ai/agent.d.ts +1 -0
- package/dist/types/tlm-ai/aiController.d.ts +4 -0
- package/dist/types/tlm-ai/aiRoutes.d.ts +2 -0
- package/dist/types/tlm-ai/knownMicroservices.d.ts +6 -0
- package/dist/types/tlm-ai/tools.d.ts +45 -0
- package/dist/types/tlm-auth/authController.d.ts +4 -0
- package/dist/types/tlm-auth/authMiddleware.d.ts +2 -0
- package/dist/types/tlm-auth/authRoutes.d.ts +2 -0
- package/dist/types/tlm-log/logController.d.ts +4 -0
- package/dist/types/tlm-log/logRoutes.d.ts +2 -0
- package/dist/types/tlm-metric/metricsController.d.ts +4 -0
- package/dist/types/tlm-metric/metricsRoutes.d.ts +2 -0
- package/dist/types/tlm-plugin/pluginController.d.ts +3 -0
- package/dist/types/tlm-plugin/pluginRoutes.d.ts +2 -0
- package/dist/types/tlm-trace/traceController.d.ts +7 -0
- package/dist/types/tlm-trace/traceRoutes.d.ts +2 -0
- package/dist/types/tlm-ui/uiRoutes.d.ts +2 -0
- package/dist/types/tlm-util/utilController.d.ts +3 -0
- package/dist/types/tlm-util/utilRoutes.d.ts +2 -0
- package/dist/types/tlmRoutes.d.ts +2 -0
- package/dist/types/types/index.d.ts +56 -0
- package/dist/types/utils/circular.d.ts +31 -0
- package/dist/types/utils/logger.d.ts +9 -0
- package/dist/ui/assets/index-BNhZBPi2.css +1 -0
- package/dist/ui/assets/index-DxGAMrAl.js +401 -0
- package/dist/ui/index.html +14 -0
- package/dist/ui/vite.svg +1 -0
- package/package.json +80 -77
- package/dist/controllers/uiController.cjs +0 -78
- package/dist/exporters/InMemoryDbExporter.cjs +0 -178
- package/dist/index.cjs +0 -110
- package/dist/openTelemetry.cjs +0 -58
- package/dist/routes/telemetryRoutes.cjs +0 -31
- package/dist/services/uiService.cjs +0 -1520
- package/dist/systemMetrics.cjs +0 -97
- package/src/controllers/telemetryController.js +0 -69
- package/src/controllers/uiController.js +0 -69
- package/src/dev/ui/login.html +0 -32
- package/src/exporters/InMemoryDbExporter.js +0 -181
- package/src/index.js +0 -121
- package/src/openTelemetry.js +0 -58
- package/src/routes/authRoutes.js +0 -53
- package/src/routes/telemetryRoutes.js +0 -38
- package/src/services/uiService.js +0 -1520
- package/src/systemMetrics.js +0 -102
- /package/dist/{controllers → cjs/tlm-metric}/metricsController.cjs +0 -0
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import { Router } from 'express';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
// Metrics Control
|
|
11
|
-
metricsRoutes.get('/', listMetrics);
|
|
12
|
-
metricsRoutes.post('/find', findMetrics);
|
|
13
|
-
metricsRoutes.get('/reset', resetMetrics);
|
|
14
|
-
|
|
15
|
-
export default metricsRoutes;
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { listMetrics, findMetrics, resetMetrics } from './metricsController.js';
|
|
3
|
+
export const metricsRoutes = Router();
|
|
4
|
+
// Metrics Control
|
|
5
|
+
metricsRoutes.get('/', listMetrics);
|
|
6
|
+
metricsRoutes.post('/find', findMetrics);
|
|
7
|
+
metricsRoutes.get('/reset', resetMetrics);
|
|
8
|
+
export default metricsRoutes;
|
|
@@ -1,115 +1,103 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import { importFromString, requireFromString } from 'import-from-string';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
module = await
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
pluginResource.plugin
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
res.status(
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
res.status(400).send(`Plugin configuration problem`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export const checkApiKey = (req, res) => {
|
|
109
|
-
const apiKey = req.query.apiKey || req.body.apiKey;
|
|
110
|
-
if (apiKey === process.env.APIKEY) {
|
|
111
|
-
res.status(200).send({ valid: true });
|
|
112
|
-
} else {
|
|
113
|
-
res.status(401).send({ valid: false });
|
|
114
|
-
}
|
|
115
|
-
}
|
|
1
|
+
import { globalOasTlmConfig } from '../config.js';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
// @ts-expect-error: import-from-string does not have proper type declarations
|
|
4
|
+
import { importFromString, requireFromString } from 'import-from-string';
|
|
5
|
+
// @ts-expect-error: dynamic-installer does not have proper type declarations
|
|
6
|
+
import { installDependencies } from 'dynamic-installer';
|
|
7
|
+
import logger from '../utils/logger.js';
|
|
8
|
+
export const listPlugins = (req, res) => {
|
|
9
|
+
res.send(globalOasTlmConfig.dynamicSpanExporter.getPlugins().map((plugin) => {
|
|
10
|
+
return {
|
|
11
|
+
id: plugin.id,
|
|
12
|
+
name: plugin.name,
|
|
13
|
+
url: plugin.url,
|
|
14
|
+
active: plugin.active
|
|
15
|
+
};
|
|
16
|
+
}));
|
|
17
|
+
};
|
|
18
|
+
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
|
+
let pluginCode;
|
|
23
|
+
if (!pluginResource.url && !pluginResource.code) {
|
|
24
|
+
res.status(400).send(`Plugin code or URL must be provided`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let module;
|
|
28
|
+
try {
|
|
29
|
+
if (pluginResource.code) {
|
|
30
|
+
pluginCode = pluginResource.code;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const response = await axios.get(pluginResource.url);
|
|
34
|
+
pluginCode = response.data;
|
|
35
|
+
}
|
|
36
|
+
if (!pluginCode) {
|
|
37
|
+
res.status(400).send(`Plugin code could not be loaded`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (pluginResource.install) {
|
|
41
|
+
const dependenciesStatus = await installDependencies(pluginResource.install);
|
|
42
|
+
if (!dependenciesStatus.success) {
|
|
43
|
+
if (pluginResource.install.ignoreErrors === true) {
|
|
44
|
+
logger.warn(`Warning: Error installing dependencies: ${JSON.stringify(dependenciesStatus.details)}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
res.status(400).send(`Error installing dependencies: ${JSON.stringify(dependenciesStatus.details)}`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
logger.info("Plugin size: " + pluginCode?.length);
|
|
53
|
+
logger.info("Plugin format: " + pluginResource?.moduleFormat);
|
|
54
|
+
if (pluginResource?.moduleFormat && pluginResource.moduleFormat.toUpperCase() == "ESM") {
|
|
55
|
+
logger.info("ESM detected");
|
|
56
|
+
module = await importFromString(pluginCode);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
logger.info("CJS detected (default)");
|
|
60
|
+
module = await requireFromString(pluginCode);
|
|
61
|
+
logger.info(module);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger.error(`Error loading plugin: ${error}`);
|
|
66
|
+
res.status(400).send(`Error loading plugin: ${error}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
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");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
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`);
|
|
78
|
+
logger.info("Error in plugin code: some required functions are missing");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
await plugin.load(pluginResource.config);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logger.error(`Error loading plugin configuration: ${error}`);
|
|
87
|
+
res.status(400).send(`Error loading plugin configuration: ${error}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (plugin.isConfigured()) {
|
|
91
|
+
logger.info(`Loaded plugin <${plugin.getName()}>`);
|
|
92
|
+
pluginResource.plugin = plugin;
|
|
93
|
+
pluginResource.name = plugin.getName();
|
|
94
|
+
pluginResource.active = true;
|
|
95
|
+
globalOasTlmConfig.dynamicSpanExporter.pushPlugin(pluginResource);
|
|
96
|
+
globalOasTlmConfig.dynamicSpanExporter.activatePlugin(pluginResource.plugin);
|
|
97
|
+
res.status(201).send(`Plugin registered`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
logger.error(`Plugin <${plugin.getName()}> can not be configured`);
|
|
101
|
+
res.status(400).send(`Plugin configuration problem`);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
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;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { globalOasTlmConfig } from '../config.js';
|
|
2
|
+
export const startTelemetry = (req, res) => {
|
|
3
|
+
globalOasTlmConfig.dynamicSpanExporter.exporter.start();
|
|
4
|
+
res.send('Telemetry started');
|
|
5
|
+
};
|
|
6
|
+
export const stopTelemetry = (req, res) => {
|
|
7
|
+
globalOasTlmConfig.dynamicSpanExporter.exporter.stop();
|
|
8
|
+
res.send('Telemetry stopped');
|
|
9
|
+
};
|
|
10
|
+
export const statusTelemetry = (req, res) => {
|
|
11
|
+
const isRunning = globalOasTlmConfig.dynamicSpanExporter.exporter.isRunning() || false;
|
|
12
|
+
res.send({ active: isRunning });
|
|
13
|
+
};
|
|
14
|
+
export const resetTelemetry = (req, res) => {
|
|
15
|
+
globalOasTlmConfig.dynamicSpanExporter.exporter.reset();
|
|
16
|
+
res.send('Telemetry reset');
|
|
17
|
+
};
|
|
18
|
+
export const listTelemetry = async (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const spans = await globalOasTlmConfig.dynamicSpanExporter.exporter.getFinishedSpans();
|
|
21
|
+
res.send({ spansCount: spans.length, spans: spans });
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.error(err);
|
|
25
|
+
res.status(500).send({ error: 'Failed to list telemetry data' });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export const findTelemetry = (req, res) => {
|
|
29
|
+
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
|
+
}
|
|
44
|
+
}
|
|
45
|
+
globalOasTlmConfig.dynamicSpanExporter.exporter.find(search, (err, docs) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
console.error(err);
|
|
48
|
+
res.status(404).send({ spansCount: 0, spans: [], error: err });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const spans = docs;
|
|
52
|
+
res.send({ spansCount: spans.length, spans: spans });
|
|
53
|
+
});
|
|
54
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { startTelemetry, stopTelemetry, statusTelemetry, resetTelemetry, listTelemetry, findTelemetry } from './traceController.js';
|
|
3
|
+
export const traceRoutes = Router();
|
|
4
|
+
traceRoutes.get('/', listTelemetry);
|
|
5
|
+
traceRoutes.post('/find', findTelemetry);
|
|
6
|
+
// Telemetry Control
|
|
7
|
+
traceRoutes.get('/start', startTelemetry);
|
|
8
|
+
traceRoutes.get('/stop', stopTelemetry);
|
|
9
|
+
traceRoutes.get('/status', statusTelemetry);
|
|
10
|
+
traceRoutes.get('/reset', resetTelemetry);
|
|
11
|
+
export default traceRoutes;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import express, { Router } from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
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;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { globalOasTlmConfig } from '../config.js';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
import v8 from 'node:v8';
|
|
6
|
+
export const specLoader = (_req, res) => {
|
|
7
|
+
if (globalOasTlmConfig.specFileName) {
|
|
8
|
+
try {
|
|
9
|
+
const data = readFileSync(globalOasTlmConfig.specFileName, { encoding: 'utf8', flag: 'r' });
|
|
10
|
+
const extension = path.extname(globalOasTlmConfig.specFileName);
|
|
11
|
+
let json = data;
|
|
12
|
+
if (extension == "yaml")
|
|
13
|
+
//@ts-expect-error yes
|
|
14
|
+
json = JSON.stringify(yaml.SafeLoad(data), null, 2);
|
|
15
|
+
res.setHeader('Content-Type', 'application/json');
|
|
16
|
+
res.send(json);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
console.error(`ERROR loading spec file ${globalOasTlmConfig.specFileName}: ${e}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if (globalOasTlmConfig.spec) {
|
|
23
|
+
let spec = null;
|
|
24
|
+
try {
|
|
25
|
+
spec = JSON.parse(globalOasTlmConfig.spec);
|
|
26
|
+
}
|
|
27
|
+
catch (ej) {
|
|
28
|
+
try {
|
|
29
|
+
spec = JSON.stringify(yaml.load(globalOasTlmConfig.spec), null, 2);
|
|
30
|
+
}
|
|
31
|
+
catch (ey) {
|
|
32
|
+
console.error(`Error parsing spec: ${ej} - ${ey}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!spec) {
|
|
36
|
+
res.status(404);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
res.setHeader('Content-Type', 'application/json');
|
|
40
|
+
res.send(spec);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
res.status(404);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
export const heapStats = (req, res) => {
|
|
48
|
+
const heapStats = v8.getHeapStatistics();
|
|
49
|
+
const roundedHeapStats = Object.getOwnPropertyNames(heapStats).reduce(function (map, stat) {
|
|
50
|
+
//@ts-expect-error yes
|
|
51
|
+
map[stat] = Math.round((heapStats[stat] / 1024 / 1024) * 1000) / 1000;
|
|
52
|
+
return map;
|
|
53
|
+
}, {});
|
|
54
|
+
// @ts-expect-error yes
|
|
55
|
+
roundedHeapStats['units'] = 'MB';
|
|
56
|
+
res.send(roundedHeapStats);
|
|
57
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { json } from "express";
|
|
2
|
+
import logger from "./utils/logger.js";
|
|
3
|
+
import cors from 'cors';
|
|
4
|
+
import { globalOasTlmConfig } from "./config.js";
|
|
5
|
+
import metricsRoutes from "./tlm-metric/metricsRoutes.js";
|
|
6
|
+
import cookieParser from 'cookie-parser';
|
|
7
|
+
import authRoutes from './tlm-auth/authRoutes.js';
|
|
8
|
+
import uiRoutes from './tlm-ui/uiRoutes.js';
|
|
9
|
+
import traceRoutes from "./tlm-trace/traceRoutes.js";
|
|
10
|
+
import { authMiddleware } from "./tlm-auth/authMiddleware.js";
|
|
11
|
+
import utilsRoutes from "./tlm-util/utilRoutes.js";
|
|
12
|
+
import logRoutes from './tlm-log/logRoutes.js';
|
|
13
|
+
import aiRoutes from "./tlm-ai/aiRoutes.js";
|
|
14
|
+
export const configureRoutes = (router) => {
|
|
15
|
+
if (process.env.OASTLM_ENV === 'development') {
|
|
16
|
+
logger.info("Running in development mode, enabling CORS for all origins");
|
|
17
|
+
router.use(cors({
|
|
18
|
+
origin: '*', // Permitir todas las solicitudes en desarrollo
|
|
19
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
20
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
router.use((req, res, next) => {
|
|
24
|
+
if (req.body !== undefined) {
|
|
25
|
+
return next(); // Already parsed, no need to parse again.
|
|
26
|
+
}
|
|
27
|
+
return json()(req, res, next);
|
|
28
|
+
});
|
|
29
|
+
const allAuthMiddlewares = getWrappedMiddlewares(() => globalOasTlmConfig.authEnabled, [cookieParser(), authRoutes, authMiddleware]);
|
|
30
|
+
const baseURL = globalOasTlmConfig.baseURL;
|
|
31
|
+
router.use(baseURL, allAuthMiddlewares);
|
|
32
|
+
// WARNING: This path must be the same as the one used in the UI package App.tsx "oas-telemetry-ui"
|
|
33
|
+
router.use(baseURL + "/traces", traceRoutes);
|
|
34
|
+
router.use(baseURL + "/metrics", metricsRoutes);
|
|
35
|
+
router.use(baseURL + "/logs", logRoutes);
|
|
36
|
+
router.use(baseURL + "/ai", getWrappedMiddlewares(() => process.env.OASTLM_AI_OPENAI_API_KEY !== null && process.env.OASTLM_AI_OPENAI_API_KEY !== "", [aiRoutes]));
|
|
37
|
+
router.use(baseURL + "/oas-telemetry-ui", uiRoutes);
|
|
38
|
+
router.use(baseURL + "/utils", utilsRoutes);
|
|
39
|
+
//redirect to the UI when accessing the base URL
|
|
40
|
+
router.get(baseURL, (req, res) => {
|
|
41
|
+
res.redirect(baseURL + "/oas-telemetry-ui");
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* This function wraps the provided middleware functions with a condition callback.
|
|
46
|
+
* If the condition callback returns true, the middleware/router will be executed.
|
|
47
|
+
* If the condition callback returns false, the middleware/router will be skipped.
|
|
48
|
+
*
|
|
49
|
+
* @callback {function} conditionCallback A callback function that returns a boolean to determine if the middleware should be used.
|
|
50
|
+
* @param {Array} middlewares An array of middleware or routers to be wrapped.
|
|
51
|
+
* @returns {Array} An array of wrapped middleware functions.
|
|
52
|
+
*/
|
|
53
|
+
function getWrappedMiddlewares(conditionCallback, middlewares) {
|
|
54
|
+
return middlewares.map(middleware => {
|
|
55
|
+
return function (req, res, next) {
|
|
56
|
+
if (conditionCallback()) {
|
|
57
|
+
if (typeof middleware === 'function') {
|
|
58
|
+
// look for handle property, if it exists, it's a router. If not call middleware
|
|
59
|
+
if (middleware.handle) {
|
|
60
|
+
middleware.handle(req, res, next);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
middleware(req, res, next);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
next();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export function removeCircularRefs(obj) {
|
|
2
|
+
// const seen = new WeakMap(); // Used to keep track of visited objects
|
|
3
|
+
// Replacer function to handle circular references
|
|
4
|
+
function replacer(key, value) {
|
|
5
|
+
if (key === "_spanProcessor") {
|
|
6
|
+
return "oas-telemetry skips this field to avoid circular reference";
|
|
7
|
+
}
|
|
8
|
+
// GENERIC CIRCULAR REFERENCE HANDLING
|
|
9
|
+
// if (typeof value === "object" && value !== null) {
|
|
10
|
+
// // If the object has been visited before, return the name prefixed with "CIRCULAR+"
|
|
11
|
+
// if (seen.has(value)) {
|
|
12
|
+
// return `CIRCULAR${key}`;
|
|
13
|
+
// }
|
|
14
|
+
// seen.set(value, key); // Mark the object as visited with its name
|
|
15
|
+
// }
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
// Convert the object to a string and then parse it back
|
|
19
|
+
// This will trigger the replacer function to handle circular references
|
|
20
|
+
const jsonString = JSON.stringify(obj, replacer);
|
|
21
|
+
return JSON.parse(jsonString);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Recursively converts dot-separated keys in an object to nested objects.
|
|
25
|
+
*
|
|
26
|
+
* @param {any} obj - The object to process.
|
|
27
|
+
* @returns {any} - The object with all dot-separated keys converted to nested objects.
|
|
28
|
+
* @example
|
|
29
|
+
* // Input:
|
|
30
|
+
* // {
|
|
31
|
+
* // "http.method": "GET",
|
|
32
|
+
* // "http.url": "http://example.com",
|
|
33
|
+
* // "nested.obj.key": "value"
|
|
34
|
+
* // }
|
|
35
|
+
* // Output:
|
|
36
|
+
* // {
|
|
37
|
+
* // "http": {
|
|
38
|
+
* // "method": "GET",
|
|
39
|
+
* // "url": "http://example.com"
|
|
40
|
+
* // },
|
|
41
|
+
* // "nested": {
|
|
42
|
+
* // "obj": {
|
|
43
|
+
* // "key": "value"
|
|
44
|
+
* // }
|
|
45
|
+
* // }
|
|
46
|
+
* // }
|
|
47
|
+
*/
|
|
48
|
+
export function convertToNestedObject(obj) {
|
|
49
|
+
const result = {};
|
|
50
|
+
for (const key in obj) {
|
|
51
|
+
const keys = key.split('.');
|
|
52
|
+
let temp = result;
|
|
53
|
+
for (let i = 0; i < keys.length; i++) {
|
|
54
|
+
const currentKey = keys[i];
|
|
55
|
+
if (i === keys.length - 1) {
|
|
56
|
+
// Last key, set the value
|
|
57
|
+
temp[currentKey] = obj[key];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Intermediate key, ensure the object exists
|
|
61
|
+
if (!temp[currentKey]) {
|
|
62
|
+
temp[currentKey] = {};
|
|
63
|
+
}
|
|
64
|
+
temp = temp[currentKey];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Applies nesting to all dot-separated keys within an object.
|
|
72
|
+
*/
|
|
73
|
+
export function applyNesting(obj) {
|
|
74
|
+
for (const key in obj) {
|
|
75
|
+
const value = obj[key];
|
|
76
|
+
if (Array.isArray(value)) {
|
|
77
|
+
obj[key] = value.map(item => typeof item === 'object' && item !== null ? applyNesting(item) : item);
|
|
78
|
+
}
|
|
79
|
+
else if (typeof value === 'object' && value !== null) {
|
|
80
|
+
obj[key] = applyNesting(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return convertToNestedObject(obj);
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
const LOG_LEVELS = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE'];
|
|
4
|
+
const currentLogLevel = (process.env.OASTLM_LOG_LEVEL || 'INFO').toUpperCase();
|
|
5
|
+
const serviceName = process.env.OASTLM_SERVICE_NAME || 'OAS-TLM';
|
|
6
|
+
function log(level, ...messages) {
|
|
7
|
+
if (LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(currentLogLevel)) {
|
|
8
|
+
const timestamp = new Date().toISOString();
|
|
9
|
+
console.log(`${timestamp} [${serviceName}] [${level}]:`, ...messages);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export default {
|
|
13
|
+
debug: (...messages) => log('DEBUG', ...messages),
|
|
14
|
+
info: (...messages) => log('INFO', ...messages),
|
|
15
|
+
log: (...messages) => log('INFO', ...messages), // Alias for info
|
|
16
|
+
warn: (...messages) => log('WARN', ...messages),
|
|
17
|
+
error: (...messages) => log('ERROR', ...messages),
|
|
18
|
+
currentLogLevel
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ResourceMetrics } from '@opentelemetry/sdk-metrics';
|
|
2
|
+
export declare class InMemoryDBMetricsExporter {
|
|
3
|
+
private _metrics;
|
|
4
|
+
private _stopped;
|
|
5
|
+
constructor();
|
|
6
|
+
export(metrics: ResourceMetrics, resultCallback: any): any;
|
|
7
|
+
start(): void;
|
|
8
|
+
stop(): void;
|
|
9
|
+
isRunning(): boolean;
|
|
10
|
+
shutdown(): Promise<void>;
|
|
11
|
+
forceFlush(): Promise<void>;
|
|
12
|
+
find(search: any, callback: any): void;
|
|
13
|
+
reset(): void;
|
|
14
|
+
getFinishedMetrics(): import("@seald-io/nedb").Document<Record<string, any>>[];
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ExportResultCode } from '@opentelemetry/core';
|
|
2
|
+
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
|
|
3
|
+
import { OasTlmExporter, PluginResource } from '../types/index.js';
|
|
4
|
+
export declare class InMemoryExporter implements OasTlmExporter {
|
|
5
|
+
private _spans;
|
|
6
|
+
private _stopped;
|
|
7
|
+
constructor();
|
|
8
|
+
plugins: PluginResource[];
|
|
9
|
+
export(readableSpans: ReadableSpan[], resultCallback: (arg0: {
|
|
10
|
+
code: ExportResultCode;
|
|
11
|
+
error?: Error;
|
|
12
|
+
}) => void): void;
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
isRunning(): boolean;
|
|
16
|
+
shutdown(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Exports any pending spans in the exporter
|
|
19
|
+
*/
|
|
20
|
+
forceFlush(): Promise<void>;
|
|
21
|
+
find(search: any, callback: any): void;
|
|
22
|
+
reset(): void;
|
|
23
|
+
getFinishedSpans(): import("@seald-io/nedb").Document<Record<string, any>>[];
|
|
24
|
+
}
|