@oas-tools/oas-telemetry 0.1.10 → 0.2.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/LICENSE +200 -200
- package/README.md +133 -95
- package/dist/exporters/InMemoryDbExporter.cjs +72 -12
- package/dist/index.cjs +42 -27
- package/dist/telemetry.cjs +1 -24
- package/dist/ui.cjs +818 -645
- package/package.json +63 -51
- package/src/exporters/InMemoryDbExporter.js +154 -89
- package/src/index.js +197 -191
- package/src/telemetry.js +25 -47
- package/src/ui.js +887 -717
- package/src/ui/detail.html +0 -441
- package/src/ui/main.html +0 -266
|
@@ -12,13 +12,15 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
12
12
|
class InMemoryExporter {
|
|
13
13
|
constructor() {
|
|
14
14
|
this._spans = new _nedb.default();
|
|
15
|
-
this._stopped =
|
|
15
|
+
this._stopped = true;
|
|
16
16
|
}
|
|
17
17
|
export(readableSpans, resultCallback) {
|
|
18
18
|
try {
|
|
19
19
|
if (!this._stopped) {
|
|
20
|
-
//
|
|
21
|
-
const cleanSpans = readableSpans.map(
|
|
20
|
+
// Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
|
|
21
|
+
const cleanSpans = readableSpans.map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
|
|
22
|
+
.map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
|
|
23
|
+
.filter(span => !span.attributes?.http?.target?.includes("/telemetry")); // to avoid telemetry spans
|
|
22
24
|
|
|
23
25
|
// Insert spans into the in-memory database
|
|
24
26
|
this._spans.insert(cleanSpans, (err, newDoc) => {
|
|
@@ -50,8 +52,8 @@ class InMemoryExporter {
|
|
|
50
52
|
this._spans = new _nedb.default();
|
|
51
53
|
return this.forceFlush();
|
|
52
54
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Exports any pending spans in the exporter
|
|
55
|
+
/**
|
|
56
|
+
* Exports any pending spans in the exporter
|
|
55
57
|
*/
|
|
56
58
|
forceFlush() {
|
|
57
59
|
return Promise.resolve();
|
|
@@ -64,7 +66,7 @@ class InMemoryExporter {
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
exports.InMemoryExporter = InMemoryExporter;
|
|
67
|
-
function
|
|
69
|
+
function removeCircularRefs(obj) {
|
|
68
70
|
const seen = new WeakMap(); // Used to keep track of visited objects
|
|
69
71
|
|
|
70
72
|
// Replacer function to handle circular references
|
|
@@ -86,10 +88,68 @@ function removeCircular(obj) {
|
|
|
86
88
|
// Convert the object to a string and then parse it back
|
|
87
89
|
// This will trigger the replacer function to handle circular references
|
|
88
90
|
const jsonString = JSON.stringify(obj, replacer);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
return JSON.parse(jsonString);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Recursively converts dot-separated keys in an object to nested objects.
|
|
96
|
+
*
|
|
97
|
+
* @param {Object} obj - The object to process.
|
|
98
|
+
* @returns {Object} - The object with all dot-separated keys converted to nested objects.
|
|
99
|
+
* @example
|
|
100
|
+
* // Input:
|
|
101
|
+
* // {
|
|
102
|
+
* // "http.method": "GET",
|
|
103
|
+
* // "http.url": "http://example.com",
|
|
104
|
+
* // "nested.obj.key": "value"
|
|
105
|
+
* // }
|
|
106
|
+
* // Output:
|
|
107
|
+
* // {
|
|
108
|
+
* // "http": {
|
|
109
|
+
* // "method": "GET",
|
|
110
|
+
* // "url": "http://example.com"
|
|
111
|
+
* // },
|
|
112
|
+
* // "nested": {
|
|
113
|
+
* // "obj": {
|
|
114
|
+
* // "key": "value"
|
|
115
|
+
* // }
|
|
116
|
+
* // }
|
|
117
|
+
* // }
|
|
118
|
+
*/
|
|
119
|
+
function convertToNestedObject(obj) {
|
|
120
|
+
const result = {};
|
|
121
|
+
for (const key in obj) {
|
|
122
|
+
const keys = key.split('.');
|
|
123
|
+
let temp = result;
|
|
124
|
+
for (let i = 0; i < keys.length; i++) {
|
|
125
|
+
const currentKey = keys[i];
|
|
126
|
+
if (i === keys.length - 1) {
|
|
127
|
+
// Last key, set the value
|
|
128
|
+
temp[currentKey] = obj[key];
|
|
129
|
+
} else {
|
|
130
|
+
// Intermediate key, ensure the object exists
|
|
131
|
+
if (!temp[currentKey]) {
|
|
132
|
+
temp[currentKey] = {};
|
|
133
|
+
}
|
|
134
|
+
temp = temp[currentKey];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Applies nesting to all dot-separated keys within an object.
|
|
143
|
+
*
|
|
144
|
+
* @param {Object} obj - The object to apply nesting to.
|
|
145
|
+
* @returns {Object} - The transformed object with nested structures.
|
|
146
|
+
*/
|
|
147
|
+
function applyNesting(obj) {
|
|
148
|
+
// Recursively apply convertToNestedObject to each level of the object
|
|
149
|
+
for (const key in obj) {
|
|
150
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
151
|
+
obj[key] = applyNesting(obj[key]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return convertToNestedObject(obj);
|
|
95
155
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -14,9 +14,6 @@ var _ui = _interopRequireDefault(require("./ui.cjs"));
|
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
// telemetryMiddleware.js
|
|
16
16
|
|
|
17
|
-
let telemetryStatus = {
|
|
18
|
-
active: true
|
|
19
|
-
};
|
|
20
17
|
let baseURL = '/telemetry';
|
|
21
18
|
let telemetryConfig = {
|
|
22
19
|
exporter: _telemetry.inMemoryExporter,
|
|
@@ -34,14 +31,17 @@ function oasTelemetry(tlConfig) {
|
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
const router = (0, _express.Router)();
|
|
34
|
+
|
|
35
|
+
//const baseURL = telemetryConfig.baseURL;
|
|
36
|
+
|
|
37
37
|
router.get(baseURL, mainPage);
|
|
38
38
|
router.get(baseURL + "/detail/*", detailPage);
|
|
39
39
|
router.get(baseURL + "/spec", specLoader);
|
|
40
40
|
router.get(baseURL + "/api", apiPage);
|
|
41
|
-
router.get(baseURL + "/reset", resetTelemetry);
|
|
42
41
|
router.get(baseURL + "/start", startTelemetry);
|
|
43
42
|
router.get(baseURL + "/stop", stopTelemetry);
|
|
44
43
|
router.get(baseURL + "/status", statusTelemetry);
|
|
44
|
+
router.get(baseURL + "/reset", resetTelemetry);
|
|
45
45
|
router.get(baseURL + "/list", listTelemetry);
|
|
46
46
|
router.post(baseURL + "/find", findTelemetry);
|
|
47
47
|
router.get(baseURL + "/heapStats", heapStats);
|
|
@@ -63,9 +63,11 @@ const apiPage = (req, res) => {
|
|
|
63
63
|
res.send(text);
|
|
64
64
|
};
|
|
65
65
|
const mainPage = (req, res) => {
|
|
66
|
+
res.set('Content-Type', 'text/html');
|
|
66
67
|
res.send((0, _ui.default)().main);
|
|
67
68
|
};
|
|
68
69
|
const detailPage = (req, res) => {
|
|
70
|
+
res.set('Content-Type', 'text/html');
|
|
69
71
|
res.send((0, _ui.default)().detail);
|
|
70
72
|
};
|
|
71
73
|
const specLoader = (req, res) => {
|
|
@@ -81,10 +83,10 @@ const specLoader = (req, res) => {
|
|
|
81
83
|
res.setHeader('Content-Type', 'application/json');
|
|
82
84
|
res.send(json);
|
|
83
85
|
} catch (e) {
|
|
84
|
-
console.
|
|
86
|
+
console.error(`ERROR loading spec file ${telemetryConfig.specFileName}: ${e}`);
|
|
85
87
|
}
|
|
86
88
|
} else {
|
|
87
|
-
if (
|
|
89
|
+
if (telemetryConfig.spec) {
|
|
88
90
|
let spec = false;
|
|
89
91
|
try {
|
|
90
92
|
spec = JSON.parse(telemetryConfig.spec);
|
|
@@ -92,7 +94,7 @@ const specLoader = (req, res) => {
|
|
|
92
94
|
try {
|
|
93
95
|
spec = JSON.stringify(_jsYaml.default.load(telemetryConfig.spec), null, 2);
|
|
94
96
|
} catch (ey) {
|
|
95
|
-
console.
|
|
97
|
+
console.error(`Error parsing spec: ${ej} - ${ey}`);
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
if (!spec) {
|
|
@@ -101,26 +103,22 @@ const specLoader = (req, res) => {
|
|
|
101
103
|
res.setHeader('Content-Type', 'application/json');
|
|
102
104
|
res.send(spec);
|
|
103
105
|
}
|
|
104
|
-
} else if (typeof telemetryConfig.spec === 'object') {
|
|
105
|
-
res.setHeader('Content-Type', 'application/json');
|
|
106
|
-
res.send(telemetryConfig.spec);
|
|
107
|
-
} else {
|
|
108
|
-
res.status(404);
|
|
109
106
|
}
|
|
110
107
|
}
|
|
111
108
|
};
|
|
112
109
|
const startTelemetry = (req, res) => {
|
|
113
110
|
telemetryConfig.exporter.start();
|
|
114
|
-
telemetryStatus.active = true;
|
|
115
111
|
res.send('Telemetry started');
|
|
116
112
|
};
|
|
117
113
|
const stopTelemetry = (req, res) => {
|
|
118
114
|
telemetryConfig.exporter.stop();
|
|
119
|
-
telemetryStatus.active = false;
|
|
120
115
|
res.send('Telemetry stopped');
|
|
121
116
|
};
|
|
122
117
|
const statusTelemetry = (req, res) => {
|
|
123
|
-
|
|
118
|
+
const status = !telemetryConfig.exporter._stopped || false;
|
|
119
|
+
res.send({
|
|
120
|
+
active: status
|
|
121
|
+
});
|
|
124
122
|
};
|
|
125
123
|
const resetTelemetry = (req, res) => {
|
|
126
124
|
telemetryConfig.exporter.reset();
|
|
@@ -142,22 +140,39 @@ const listTelemetry = (req, res) => {
|
|
|
142
140
|
};
|
|
143
141
|
const findTelemetry = (req, res) => {
|
|
144
142
|
const spansDB = telemetryConfig.exporter.getFinishedSpans();
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
const body = req.body;
|
|
144
|
+
const search = body?.search ? body.search : {};
|
|
145
|
+
if (body?.flags?.containsRegex) {
|
|
146
|
+
try {
|
|
147
|
+
body.config?.regexIds?.forEach(regexId => {
|
|
148
|
+
search[regexId] = new RegExp(search[regexId]);
|
|
149
|
+
});
|
|
150
|
+
} catch (e) {
|
|
151
|
+
console.error(e);
|
|
152
|
+
res.status(404).send({
|
|
153
|
+
spansCount: 0,
|
|
154
|
+
spans: [],
|
|
155
|
+
error: e
|
|
152
156
|
});
|
|
153
157
|
return;
|
|
154
158
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
spansDB.find(search, (err, docs) => {
|
|
160
|
+
if (err) {
|
|
161
|
+
console.error(err);
|
|
162
|
+
res.status(404).send({
|
|
163
|
+
spansCount: 0,
|
|
164
|
+
spans: [],
|
|
165
|
+
error: err
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const spans = docs;
|
|
170
|
+
res.send({
|
|
171
|
+
spansCount: spans.length,
|
|
172
|
+
spans: spans
|
|
173
|
+
});
|
|
159
174
|
});
|
|
160
|
-
}
|
|
175
|
+
}
|
|
161
176
|
};
|
|
162
177
|
const heapStats = (req, res) => {
|
|
163
178
|
var heapStats = _v.default.getHeapStatistics();
|
package/dist/telemetry.cjs
CHANGED
|
@@ -12,35 +12,12 @@ var _instrumentationHttp = require("@opentelemetry/instrumentation-http");
|
|
|
12
12
|
var _InMemoryDbExporter = require("./exporters/InMemoryDbExporter.cjs");
|
|
13
13
|
// import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
14
14
|
|
|
15
|
-
// initialize the SDK and register with the OpenTelemetry API
|
|
16
|
-
// import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
17
|
-
// import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
18
|
-
|
|
19
15
|
// Create an in-memory span exporter
|
|
20
16
|
const inMemoryExporter = exports.inMemoryExporter = new _InMemoryDbExporter.InMemoryExporter();
|
|
21
|
-
// configure the SDK to export telemetry data to the console
|
|
22
|
-
// enable all auto-instrumentations from the meta package
|
|
23
17
|
const traceExporter = inMemoryExporter;
|
|
24
18
|
const sdk = new _sdkNode.NodeSDK({
|
|
25
19
|
resource: new _resources.Resource(),
|
|
26
20
|
traceExporter,
|
|
27
21
|
instrumentations: [new _instrumentationHttp.HttpInstrumentation()]
|
|
28
22
|
});
|
|
29
|
-
sdk.start();
|
|
30
|
-
// Create a tracer provider
|
|
31
|
-
// const tracerProvider = new NodeTracerProvider({
|
|
32
|
-
// resource: new Resource({
|
|
33
|
-
// [SemanticResourceAttributes.SERVICE_NAME]: 'basic-service',
|
|
34
|
-
// }),
|
|
35
|
-
// instrumentations: [new HttpInstrumentation()],
|
|
36
|
-
// });
|
|
37
|
-
|
|
38
|
-
// // maybe BatchSpanProcessorBase
|
|
39
|
-
// tracerProvider.addSpanProcessor(new SimpleSpanProcessor(inMemoryExporter));
|
|
40
|
-
|
|
41
|
-
// // Register the tracer provider
|
|
42
|
-
// tracerProvider.register();
|
|
43
|
-
|
|
44
|
-
//WARNING: this is configured with batchSpanProcessor, it takes 5 seconds to flush the spans
|
|
45
|
-
// import {startTracesInstrumentation} from '@restsense/agent/api'
|
|
46
|
-
// startTracesInstrumentation(inMemoryExporter,resource);
|
|
23
|
+
sdk.start();
|