@oas-tools/oas-telemetry 0.3.0 → 0.5.0

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 (39) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +151 -133
  3. package/dist/client.cjs +14 -0
  4. package/dist/config.cjs +27 -0
  5. package/dist/controllers/pluginController.cjs +118 -0
  6. package/dist/controllers/telemetryController.cjs +92 -0
  7. package/dist/controllers/uiController.cjs +78 -0
  8. package/dist/exporters/InMemoryDbExporter.cjs +45 -42
  9. package/dist/exporters/consoleExporter.cjs +52 -0
  10. package/dist/exporters/dynamicExporter.cjs +64 -0
  11. package/dist/index.cjs +61 -248
  12. package/dist/middleware/auth.cjs +17 -0
  13. package/dist/middleware/authMiddleware.cjs +19 -0
  14. package/dist/openTelemetry.cjs +20 -0
  15. package/dist/routes/authRoutes.cjs +79 -0
  16. package/dist/routes/telemetryRoutes.cjs +31 -0
  17. package/{src/ui.js → dist/services/uiService.cjs} +1140 -813
  18. package/dist/telemetry.cjs +0 -0
  19. package/dist/types/exporters/InMemoryDbExporter.d.ts +16 -0
  20. package/dist/types/index.d.ts +1 -0
  21. package/dist/types/telemetry.d.ts +2 -0
  22. package/dist/types/ui.d.ts +4 -0
  23. package/dist/ui.cjs +0 -0
  24. package/package.json +75 -71
  25. package/src/config.js +19 -0
  26. package/src/controllers/pluginController.js +115 -0
  27. package/src/controllers/telemetryController.js +68 -0
  28. package/src/controllers/uiController.js +69 -0
  29. package/src/dev/ui/login.html +32 -0
  30. package/src/exporters/InMemoryDbExporter.js +180 -175
  31. package/src/exporters/consoleExporter.js +47 -0
  32. package/src/exporters/dynamicExporter.js +62 -0
  33. package/src/index.js +85 -307
  34. package/src/middleware/authMiddleware.js +14 -0
  35. package/src/openTelemetry.js +22 -0
  36. package/src/routes/authRoutes.js +53 -0
  37. package/src/routes/telemetryRoutes.js +38 -0
  38. package/src/services/uiService.js +1520 -0
  39. package/src/telemetry.js +0 -25
@@ -1,176 +1,181 @@
1
- import { ExportResultCode } from '@opentelemetry/core';
2
-
3
- let dbglog = ()=>{};
4
-
5
- if(process.env.OTDEBUG == "true")
6
- dbglog = console.log;
7
-
8
- //import in memory database
9
- import dataStore from 'nedb';
10
-
11
- export class InMemoryExporter {
12
- constructor() {
13
- this._spans = new dataStore();
14
- this._stopped = true;
15
- };
16
-
17
- static plugins = [];
18
-
19
- export(readableSpans, resultCallback) {
20
- try {
21
- if (!this._stopped) {
22
- // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
23
- const cleanSpans = readableSpans
24
- .map(nestedSpan => removeCircularRefs(nestedSpan))// to avoid JSON parsing error
25
- .map(span => applyNesting(span))// to avoid dot notation in keys (neDB does not support dot notation in keys)
26
- .filter(span => !span.attributes?.http?.target?.includes("/telemetry"));// to avoid telemetry spans
27
-
28
- // Insert spans into the in-memory database
29
- this._spans.insert(cleanSpans, (err, newDoc) => {
30
- InMemoryExporter.plugins.forEach((p,i)=>{
31
- cleanSpans.forEach((t)=>{
32
- dbglog(`Sending trace <${t._id}> to plugin (Plugin #${i}) <${p.name}>`);
33
- dbglog(`Trace: \n<${JSON.stringify(t,null,2)}`);
34
- //TODO: This should be called newSpan instead of newTrace
35
- p.newTrace(t);
36
- });
37
- });
38
- if (err) {
39
- console.error(err);
40
- return;
41
- }
42
- });
43
-
44
- }
45
- setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
46
- } catch (error) {
47
- console.error('Error exporting spans\n' + error.message + '\n' + error.stack);
48
- return resultCallback({
49
- code: ExportResultCode.FAILED,
50
- error: new Error('Error exporting spans\n' + error.message + '\n' + error.stack),
51
- })
52
- }
53
- };
54
- start() {
55
- this._stopped = false;
56
- };
57
- stop() {
58
- this._stopped = true;
59
- };
60
- shutdown() {
61
- this._stopped = true;
62
- this._spans = new dataStore();
63
- return this.forceFlush();
64
- };
65
- /**
66
- * Exports any pending spans in the exporter
67
- */
68
- forceFlush() {
69
- return Promise.resolve();
70
- };
71
- reset() {
72
- this._spans = new dataStore();
73
- };
74
- getFinishedSpans() {
75
- return this._spans;
76
- };
77
- activatePlugin(plugin){
78
- dbglog(`Activating plugin <${plugin.getName()}>...`);
79
- InMemoryExporter.plugins.push(plugin);
80
- dbglog(`Plugin <${plugin.getName()}> active (Total active plugins: ${InMemoryExporter.plugins.length})`);
81
- }
82
- }
83
-
84
- function removeCircularRefs(obj) {
85
- const seen = new WeakMap(); // Used to keep track of visited objects
86
-
87
-
88
- // Replacer function to handle circular references
89
- function replacer(key, value) {
90
- if (key === "_spanProcessor") {
91
- return "oas-telemetry skips this field to avoid circular reference";
92
- }
93
- // GENERIC CIRCULAR REFERENCE HANDLING
94
- // if (typeof value === "object" && value !== null) {
95
- // // If the object has been visited before, return the name prefixed with "CIRCULAR+"
96
- // if (seen.has(value)) {
97
- // return `CIRCULAR${key}`;
98
- // }
99
- // seen.set(value, key); // Mark the object as visited with its name
100
- // }
101
- return value;
102
- }
103
-
104
- // Convert the object to a string and then parse it back
105
- // This will trigger the replacer function to handle circular references
106
- const jsonString = JSON.stringify(obj, replacer);
107
- return JSON.parse(jsonString);
108
- }
109
-
110
- /**
111
- * Recursively converts dot-separated keys in an object to nested objects.
112
- *
113
- * @param {Object} obj - The object to process.
114
- * @returns {Object} - The object with all dot-separated keys converted to nested objects.
115
- * @example
116
- * // Input:
117
- * // {
118
- * // "http.method": "GET",
119
- * // "http.url": "http://example.com",
120
- * // "nested.obj.key": "value"
121
- * // }
122
- * // Output:
123
- * // {
124
- * // "http": {
125
- * // "method": "GET",
126
- * // "url": "http://example.com"
127
- * // },
128
- * // "nested": {
129
- * // "obj": {
130
- * // "key": "value"
131
- * // }
132
- * // }
133
- * // }
134
- */
135
- function convertToNestedObject(obj) {
136
- const result = {};
137
-
138
- for (const key in obj) {
139
- const keys = key.split('.');
140
- let temp = result;
141
-
142
- for (let i = 0; i < keys.length; i++) {
143
- const currentKey = keys[i];
144
-
145
- if (i === keys.length - 1) {
146
- // Last key, set the value
147
- temp[currentKey] = obj[key];
148
- } else {
149
- // Intermediate key, ensure the object exists
150
- if (!temp[currentKey]) {
151
- temp[currentKey] = {};
152
- }
153
- temp = temp[currentKey];
154
- }
155
- }
156
- }
157
-
158
- return result;
159
- }
160
-
161
- /**
162
- * Applies nesting to all dot-separated keys within an object.
163
- *
164
- * @param {Object} obj - The object to apply nesting to.
165
- * @returns {Object} - The transformed object with nested structures.
166
- */
167
- function applyNesting(obj) {
168
- // Recursively apply convertToNestedObject to each level of the object
169
- for (const key in obj) {
170
- if (typeof obj[key] === 'object' && obj[key] !== null) {
171
- obj[key] = applyNesting(obj[key]);
172
- }
173
- }
174
-
175
- return convertToNestedObject(obj);
1
+ import { ExportResultCode } from '@opentelemetry/core';
2
+
3
+ let dbglog = () => { };
4
+
5
+ if (process.env.OTDEBUG == "true")
6
+ dbglog = console.log;
7
+
8
+ //import in memory database
9
+ import dataStore from 'nedb';
10
+
11
+ export class InMemoryExporter {
12
+ constructor() {
13
+ this._spans = new dataStore();
14
+ this._stopped = true;
15
+ };
16
+
17
+ // Overrided by dynamic exporter
18
+ plugins = [];
19
+
20
+ export(readableSpans, resultCallback) {
21
+ try {
22
+ if (!this._stopped) {
23
+ // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
24
+ const cleanSpans = readableSpans
25
+ .map(nestedSpan => removeCircularRefs(nestedSpan))// to avoid JSON parsing error
26
+ .map(span => applyNesting(span))// to avoid dot notation in keys (neDB does not support dot notation in keys)
27
+ .filter(span => !span.attributes?.http?.target?.includes("/telemetry"));// to avoid telemetry spans
28
+ // Insert spans into the in-memory database
29
+ this._spans.insert(cleanSpans, (err, newDoc) => {
30
+ // p = {name, plugin
31
+ this.plugins.forEach((pluginResource, i) => {
32
+ cleanSpans.forEach((t) => {
33
+ dbglog(`Sending trace <${t._id}> to plugin (Plugin #${i}) <${pluginResource.name}>`);
34
+ dbglog(`Trace: \n<${JSON.stringify(t, null, 2)}`);
35
+ //TODO: This should be called newSpan instead of newTrace
36
+ pluginResource.plugin.newTrace(t);
37
+ });
38
+ });
39
+ if (err) {
40
+ console.error(err);
41
+ return;
42
+ }
43
+ });
44
+
45
+ }
46
+ setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
47
+ } catch (error) {
48
+ console.error('Error exporting spans\n' + error.message + '\n' + error.stack);
49
+ return resultCallback({
50
+ code: ExportResultCode.FAILED,
51
+ error: new Error('Error exporting spans\n' + error.message + '\n' + error.stack),
52
+ })
53
+ }
54
+ };
55
+ start() {
56
+ this._stopped = false;
57
+ };
58
+ stop() {
59
+ this._stopped = true;
60
+ };
61
+
62
+ isRunning() {
63
+ return !this._stopped;
64
+ };
65
+ shutdown() {
66
+ this._stopped = true;
67
+ this._spans = new dataStore();
68
+ return this.forceFlush();
69
+ };
70
+ /**
71
+ * Exports any pending spans in the exporter
72
+ */
73
+ forceFlush() {
74
+ return Promise.resolve();
75
+ };
76
+ //err,docs
77
+ find(search, callback) {
78
+ this._spans.find(search, callback);
79
+ }
80
+ reset() {
81
+ this._spans = new dataStore();
82
+ };
83
+ getFinishedSpans() {
84
+ return this._spans.getAllData();
85
+ };
86
+
87
+ }
88
+
89
+ function removeCircularRefs(obj) {
90
+ const seen = new WeakMap(); // Used to keep track of visited objects
91
+
92
+
93
+ // Replacer function to handle circular references
94
+ function replacer(key, value) {
95
+ if (key === "_spanProcessor") {
96
+ return "oas-telemetry skips this field to avoid circular reference";
97
+ }
98
+ // GENERIC CIRCULAR REFERENCE HANDLING
99
+ // if (typeof value === "object" && value !== null) {
100
+ // // If the object has been visited before, return the name prefixed with "CIRCULAR+"
101
+ // if (seen.has(value)) {
102
+ // return `CIRCULAR${key}`;
103
+ // }
104
+ // seen.set(value, key); // Mark the object as visited with its name
105
+ // }
106
+ return value;
107
+ }
108
+
109
+ // Convert the object to a string and then parse it back
110
+ // This will trigger the replacer function to handle circular references
111
+ const jsonString = JSON.stringify(obj, replacer);
112
+ return JSON.parse(jsonString);
113
+ }
114
+
115
+ /**
116
+ * Recursively converts dot-separated keys in an object to nested objects.
117
+ *
118
+ * @param {Object} obj - The object to process.
119
+ * @returns {Object} - The object with all dot-separated keys converted to nested objects.
120
+ * @example
121
+ * // Input:
122
+ * // {
123
+ * // "http.method": "GET",
124
+ * // "http.url": "http://example.com",
125
+ * // "nested.obj.key": "value"
126
+ * // }
127
+ * // Output:
128
+ * // {
129
+ * // "http": {
130
+ * // "method": "GET",
131
+ * // "url": "http://example.com"
132
+ * // },
133
+ * // "nested": {
134
+ * // "obj": {
135
+ * // "key": "value"
136
+ * // }
137
+ * // }
138
+ * // }
139
+ */
140
+ function convertToNestedObject(obj) {
141
+ const result = {};
142
+
143
+ for (const key in obj) {
144
+ const keys = key.split('.');
145
+ let temp = result;
146
+
147
+ for (let i = 0; i < keys.length; i++) {
148
+ const currentKey = keys[i];
149
+
150
+ if (i === keys.length - 1) {
151
+ // Last key, set the value
152
+ temp[currentKey] = obj[key];
153
+ } else {
154
+ // Intermediate key, ensure the object exists
155
+ if (!temp[currentKey]) {
156
+ temp[currentKey] = {};
157
+ }
158
+ temp = temp[currentKey];
159
+ }
160
+ }
161
+ }
162
+
163
+ return result;
164
+ }
165
+
166
+ /**
167
+ * Applies nesting to all dot-separated keys within an object.
168
+ *
169
+ * @param {Object} obj - The object to apply nesting to.
170
+ * @returns {Object} - The transformed object with nested structures.
171
+ */
172
+ function applyNesting(obj) {
173
+ // Recursively apply convertToNestedObject to each level of the object
174
+ for (const key in obj) {
175
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
176
+ obj[key] = applyNesting(obj[key]);
177
+ }
178
+ }
179
+
180
+ return convertToNestedObject(obj);
176
181
  }
@@ -0,0 +1,47 @@
1
+ export class ConsoleExporter {
2
+
3
+ // PLUGIN SYSTEM -----------------------------------------------------------
4
+ plugins = [];
5
+
6
+ // OPEN TELEMETRY EXPORTER INTERFACE ---------------------------------------
7
+ export(readableSpans, resultCallback) {
8
+ console.log('ConsoleExporter | Received spans: ', readableSpans.length);
9
+ setTimeout(() => resultCallback({ code: 0 }), 0);
10
+ }
11
+
12
+ shutdown() {
13
+ return this.forceFlush();
14
+ }
15
+
16
+ forceFlush() {
17
+ return Promise.resolve();
18
+ }
19
+
20
+ // OAS-TOOLS OAS-TELEMETRY EXPORTER INTERFACE ---------------------------------------
21
+
22
+ start() {
23
+ console.log("Exporter started");
24
+ }
25
+
26
+ stop() {
27
+ console.log("Exporter stopped");
28
+ }
29
+
30
+ reset() {
31
+ console.log("Exporter reset");
32
+ }
33
+
34
+ isRunning() {
35
+ return true;
36
+ }
37
+
38
+ find(search, callback) {
39
+ console.log("Getting finished spans");
40
+ callback(null, []);
41
+ return [];
42
+ }
43
+
44
+ async getFinishedSpans() {
45
+ return [];
46
+ }
47
+ }
@@ -0,0 +1,62 @@
1
+ import { ConsoleExporter } from "./consoleExporter.js";
2
+
3
+
4
+ /**
5
+ * DynamicExporter is a class that can be used to dynamically change the exporter used by OpenTelemetry.
6
+ * This is useful when you want to change the exporter at runtime.
7
+ * Links start, stop and export methods to the Real exporter.
8
+ */
9
+ export class DynamicExporter {
10
+
11
+ exporter;
12
+ /**
13
+ * @returns {Array<PluginResource>} Returns the list of plugins registered in the exporter
14
+ */
15
+ getPlugins() {
16
+ return this.exporter.plugins;
17
+ }
18
+
19
+ /**
20
+ * Registers a plugin in the exporter
21
+ * @param {PluginResource} pluginResource The plugin to be registered
22
+ * @returns {void}
23
+ */
24
+ pushPlugin(pluginResource) {
25
+ if (!this.exporter.plugins) {
26
+ this.exporter.plugins = [];
27
+ }
28
+ this.exporter.plugins.push(pluginResource);
29
+ }
30
+ activatePlugin(pluginId) {
31
+ let plugins = this.exporter.plugins;
32
+ if (plugins) {
33
+ // plugin.active = true;
34
+ plugins.forEach(plugin => {
35
+ if (plugin.id === pluginId) {
36
+ plugin.active = true;
37
+ }
38
+ });
39
+ }
40
+ }
41
+
42
+
43
+ constructor() {
44
+ let defaultExporter = new ConsoleExporter();
45
+ this.exporter = defaultExporter;
46
+ this.export = (readableSpans, resultCallback) => defaultExporter.export(readableSpans, resultCallback);
47
+ this.shutdown = () => defaultExporter.shutdown();
48
+ this.forceFlush = () => defaultExporter.forceFlush();
49
+ }
50
+
51
+ changeExporter(newExporter) {
52
+ this.exporter = newExporter;
53
+ // OpenTelemetry methods
54
+ this.export = (readableSpans, resultCallback) => newExporter.export(readableSpans, resultCallback);
55
+ this.shutdown = () => newExporter.shutdown();
56
+ this.forceFlush = () => newExporter.forceFlush();
57
+ // Other methods should be called directly from the exporter: globalOasTlmConfig.dynamicExporter.exporter.method()
58
+ }
59
+
60
+ }
61
+
62
+ export default DynamicExporter;