@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.
@@ -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 = false;
15
+ this._stopped = true;
16
16
  }
17
17
  export(readableSpans, resultCallback) {
18
18
  try {
19
19
  if (!this._stopped) {
20
- // Remove circular references
21
- const cleanSpans = readableSpans.map(span => removeCircular(span));
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 removeCircular(obj) {
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
- const spanNoDotsInKeys = jsonString.replace(/[^"]*":/g, match => {
90
- // Replace all dots in the key with underscores (e.g. "http.method" -> "http_method")
91
- const newMatch = match.replace(/\./g, "_dot_");
92
- return newMatch;
93
- });
94
- return JSON.parse(spanNoDotsInKeys);
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.log(`ERROR loading spec file ${telemetryConfig.specFileName}: ${e}`);
86
+ console.error(`ERROR loading spec file ${telemetryConfig.specFileName}: ${e}`);
85
87
  }
86
88
  } else {
87
- if (typeof telemetryConfig.spec === 'string' || telemetryConfig.spec instanceof String) {
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.log(`Error parsing spec: ${ej} - ${ey}`);
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
- res.send(telemetryStatus);
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 search = req.body;
146
- spansDB.find(search, (err, docs) => {
147
- if (err) {
148
- console.error(err);
149
- res.send({
150
- spansCount: "error",
151
- spans: []
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
- const spans = docs;
156
- res.send({
157
- spansCount: spans.length,
158
- spans: spans
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();
@@ -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();