@oas-tools/oas-telemetry 0.7.0-alpha.1 → 0.7.0-alpha.2

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 (32) hide show
  1. package/.env.example +26 -0
  2. package/NOTICE +8 -0
  3. package/README.md +76 -18
  4. package/dist/cjs/exporters/InMemoryDBMetricsExporter.cjs +10 -2
  5. package/dist/cjs/exporters/InMemoryDbExporter.cjs +16 -1
  6. package/dist/cjs/exporters/InMemoryLogRecordExporter.cjs +51 -15
  7. package/dist/cjs/tlm-log/logController.cjs +69 -3
  8. package/dist/cjs/tlm-log/logRoutes.cjs +6 -1
  9. package/dist/cjs/tlm-metric/metricsController.cjs +69 -3
  10. package/dist/cjs/tlm-metric/metricsRoutes.cjs +4 -0
  11. package/dist/cjs/tlm-trace/traceController.cjs +51 -2
  12. package/dist/cjs/tlm-trace/traceRoutes.cjs +1 -0
  13. package/dist/cjs/tlm-util/utilRoutes.cjs +16 -0
  14. package/dist/cjs/tlmRoutes.cjs +3 -1
  15. package/dist/esm/exporters/InMemoryDBMetricsExporter.js +10 -2
  16. package/dist/esm/exporters/InMemoryDbExporter.js +16 -1
  17. package/dist/esm/exporters/InMemoryLogRecordExporter.js +46 -11
  18. package/dist/esm/tlm-log/logController.js +48 -1
  19. package/dist/esm/tlm-log/logRoutes.js +7 -2
  20. package/dist/esm/tlm-metric/metricsController.js +48 -1
  21. package/dist/esm/tlm-metric/metricsRoutes.js +5 -1
  22. package/dist/esm/tlm-trace/traceController.js +35 -0
  23. package/dist/esm/tlm-trace/traceRoutes.js +2 -1
  24. package/dist/esm/tlm-util/utilRoutes.js +11 -0
  25. package/dist/esm/tlmRoutes.js +1 -1
  26. package/dist/types/exporters/InMemoryDBMetricsExporter.d.ts +6 -0
  27. package/dist/types/exporters/InMemoryDbExporter.d.ts +6 -0
  28. package/dist/types/exporters/InMemoryLogRecordExporter.d.ts +7 -1
  29. package/dist/types/tlm-log/logController.d.ts +4 -0
  30. package/dist/types/tlm-metric/metricsController.d.ts +4 -0
  31. package/dist/types/tlm-trace/traceController.d.ts +1 -0
  32. package/package.json +3 -1
package/.env.example ADDED
@@ -0,0 +1,26 @@
1
+ # All environment variables are OPTIONAL, defaults are provided below
2
+
3
+
4
+ # DEVELOPMENT ----------------------------------------------------------
5
+ # This is JUST for development purposes, to test the react app
6
+ # ----------------------------------------------------------------------
7
+
8
+ # production, development
9
+ OASTLM_ENV=production
10
+
11
+
12
+ # PRODUCTION -----------------------------------------------------------
13
+ # This variables can be configured in the .env file of the project importing our library
14
+ # ----------------------------------------------------------------------
15
+
16
+ # Disable the module, no Telemetry collected, empty middleware exported
17
+ OASTLM_MODULE_DISABLED=false
18
+
19
+ OASTLM_AI_OPENAI_API_KEY=
20
+ OASTLM_AI_OPENAI_MODEL_NAME=gpt-4o-mini
21
+
22
+ # Used in OASTLM logger
23
+ OASTLM_SERVICE_NAME=OAS-TLM
24
+ # LEVELS: DEBUG, INFO, WARNING, ERROR, NONE
25
+ # Note: "NONE" will disable all OASTLM logging
26
+ OASTLM_LOG_LEVEL=INFO
package/NOTICE ADDED
@@ -0,0 +1,8 @@
1
+ This software includes code derived from the OpenTelemetry JavaScript SDK
2
+ (https://github.com/open-telemetry/opentelemetry-js).
3
+
4
+ Some components, such as custom exporters, are based on the structure and patterns
5
+ defined by OpenTelemetry, with modifications and adaptations specific to this project.
6
+
7
+ Original code licensed under the Apache License, Version 2.0.
8
+ Copyright © The OpenTelemetry Authors.
package/README.md CHANGED
@@ -10,6 +10,16 @@ Overall, **OAS Telemetry** will serve as a valuable tool for developers looking
10
10
 
11
11
  The package now supports both **ES Module (ESM)** and **CommonJS (CJS)** formats, making it compatible with a wide range of applications. The ESM build targets the **ES2020** standard. Furthermore, **OAS Telemetry** provides a range of plugins to extend its functionality. See the [Telemetry Plugins](#telemetry-plugins) section for more information.
12
12
 
13
+ > ⚠️ **Warning: Early Development Notice**
14
+ >
15
+ > **OAS Telemetry** is a functional and working package, but it is currently at version 0 and remains under active development. Features, APIs, and behavior are subject to change at any time. Please review the following current status before use:
16
+ >
17
+ > - **UI:** Migration to React is in progress. Most views are placeholders except for the AI agent, which is fully functional and can answer questions about metrics, traces, and logs.
18
+ > - **Traces:** Semi-stable. Currently supports HTTP instrumentation; future updates will add propagators.
19
+ > - **Logs:** Semi-stable. Supports fast search by message content and Mongo-like search (similar to traces).
20
+ > - **Metrics:** Not stable. Currently uses OpenTelemetry host metrics. This area is subject to significant change to improve memory usage and data handling.
21
+ > - **Configuration:** Not stable. All configuration options will be available as parameters at initialization and via environment variables (see the `.env.example` file). The configuration system is under active development and will change significantly in future releases.
22
+
13
23
  ## Usage
14
24
 
15
25
  This section provides an overview of how to install and integrate **OAS Telemetry** into your EXISTING **Express.js** application. If you want to create a new example express application with **OAS Telemetry** integrated, refer to the [Full Examples](#full-examples) section at the end of this document.
@@ -20,6 +30,8 @@ First, install the package using npm:
20
30
  npm install @oas-tools/oas-telemetry
21
31
  ```
22
32
 
33
+ Then add the .env file to your project root directory. This file contains the environment variables used by the telemetry middleware. You can find an example of the .env file in the [`.env.example`](.env.example) file in the root of the repository. The `.env` file is optional, but it is needed for the ai chat.
34
+
23
35
  You can integrate the middleware into your Express application. The `spec` option is the OpenAPI Specification (OAS) content in JSON or YAML format. While this configuration is optional, it is recommended for the UI to function correctly.
24
36
 
25
37
  ### Using ES Modules (ESM)
@@ -32,6 +44,7 @@ import oasTelemetry from '@oas-tools/oas-telemetry';
32
44
  import { readFileSync } from 'fs';
33
45
 
34
46
  // ...rest of your code here creating an express app
47
+ // NOTE: Do not add express.json() before oasTelemetry, or set its limit to at least "10mb" to avoid issues with large files.
35
48
 
36
49
  app.use(oasTelemetry({
37
50
  spec: readFileSync('./spec/oas.yaml', { encoding: 'utf8', flag: 'r' })
@@ -48,6 +61,7 @@ const oasTelemetry = require('@oas-tools/oas-telemetry');
48
61
  const { readFileSync } = require('fs');
49
62
 
50
63
  // ...rest of your code here creating an express app
64
+ // NOTE: Do not add express.json() before oasTelemetry, or set its limit to at least "10mb" to avoid issues with large files.
51
65
 
52
66
  app.use(oasTelemetry({
53
67
  spec: readFileSync('./spec/oas.yaml', { encoding: 'utf8', flag: 'r' })
@@ -81,9 +95,64 @@ app.use(oasTelemetry(customTelemetryConfig));
81
95
 
82
96
  You can access the telemetry UI in the endpoint `/telemetry` (or `/custom-telemetry` if you set the `baseURL` option). This UI provides a user-friendly interface to interact with the telemetry data collected by the middleware.
83
97
 
98
+ ## Rest API Endpoints Overview
99
+
100
+ ### Authentication Endpoints
101
+
102
+ - `POST /login`: Log in to the system.
103
+ - `GET /logout`: Log out of the system.
104
+ - `GET /check`: Check authentication status.
105
+
106
+ ### Metrics Endpoints
107
+
108
+ - `GET /metrics`: List all metrics.
109
+ - `POST /metrics`: Insert metrics into the database.
110
+ - `POST /metrics/find`: Search metrics.
111
+ - `GET /metrics/start`: Start metrics data collection.
112
+ - `GET /metrics/stop`: Stop metrics data collection.
113
+ - `GET /metrics/status`: Get metrics status.
114
+ - `GET /metrics/reset`: Reset metrics data.
115
+
116
+ ### Logs Endpoints
117
+
118
+ - `GET /logs`: List all logs.
119
+ - `POST /logs`: Insert logs into the database.
120
+ - `POST /logs/find`: Search logs.
121
+ - `GET /logs/start`: Start logs data collection.
122
+ - `GET /logs/stop`: Stop logs data collection.
123
+ - `GET /logs/status`: Get logs status.
124
+ - `GET /logs/reset`: Reset logs data.
125
+
126
+ ### Traces Endpoints
127
+
128
+ - `GET /traces`: List all traces.
129
+ - `POST /traces`: Insert traces into the database.
130
+ - `POST /traces/find`: Search traces.
131
+ - `GET /traces/start`: Start traces data collection.
132
+ - `GET /traces/stop`: Stop traces data collection.
133
+ - `GET /traces/status`: Get traces status.
134
+ - `GET /traces/reset`: Reset traces data.
135
+
136
+ ### AI Endpoints
137
+
138
+ - `POST /ai/chat`: Interact with the AI agent.
139
+ - `POST /ai/microservices`: Configure known microservices.
140
+ - `GET /ai/microservices`: Retrieve the list of known microservices.
141
+
142
+ ### UI Endpoints
143
+
144
+ - `GET *`: Serve the telemetry UI.
145
+
146
+ ### Utility Endpoints
147
+
148
+ - `GET /utils/spec`: Load the OpenAPI specification.
149
+ - `GET /utils/heapStats`: Show v8 heap statistics.
150
+ - `GET /utils/generateLog`: Generate a log message.
151
+ - `GET /utils/wait/:seconds?`: Wait for a specified number of seconds.
152
+
84
153
  ## Metrics Development (Temporary)
85
154
 
86
- This feature is currently in development. The following endpoints are available under <baseURL>/metrics (e.g., /telemetry/metrics):
155
+ This feature is currently in development. The following endpoints are available under \<baseURL>/metrics (e.g., /telemetry/metrics):
87
156
 
88
157
  - GET /
89
158
  - POST /find
@@ -91,6 +160,9 @@ This feature is currently in development. The following endpoints are available
91
160
 
92
161
  Expect an array of metrics objects. Each object includes data like a timestamp, cpuUsageData, processCpuUsageData, memoryData, and processMemoryData. Example snippet for one CPU core (followed by others in the array):
93
162
 
163
+ > **Warning**
164
+ > Stored metrics data now uses `@opentelemetry/host-metrics`, which is still in development. The data structure may change in future releases.
165
+
94
166
  ```json
95
167
  {
96
168
  "timestamp": 1741717005911,
@@ -128,20 +200,6 @@ Expect an array of metrics objects. Each object includes data like a timestamp,
128
200
 
129
201
  The shape of these objects may change as development continues.
130
202
 
131
- ## API Telemetry Endpoints
132
-
133
- OAS Telemetry middleware adds the following endpoints to your Express application:
134
-
135
- - /telemetry/start: Start telemetry data collection.
136
- - /telemetry/stop: Stop telemetry data collection.
137
- - /telemetry/status: Get status of telemetry.
138
- - /telemetry/reset: Reset telemetry data.
139
- - /telemetry/list: List all telemetry data.
140
- - /telemetry/find (POST): Search telemetry data.
141
- - /telemetry/heapStats: Shows v8 heapStats.
142
- - /telemetry/plugins: List all plugins.
143
- - /telemetry/plugins (POST): Add a plugin.
144
-
145
203
  ## Telemetry Plugins
146
204
 
147
205
  OAS Telemetry supports a range of plugins to extend its functionality, allowing developers to tailor telemetry data collection, alerting, and reporting to meet specific requirements. Plugins enable additional features, such as integration with alerting systems, custom data exporters, and data visualization tools.
@@ -157,13 +215,13 @@ This flexibility makes it easy to incorporate a wide variety of plugins in your
157
215
 
158
216
  ## Accessing Telemetry Data
159
217
 
160
- Using OAS Telemetry, you can access telemetry data through the UI, the `/telemetry/list` endpoint, or the `/telemetry/find` endpoint with a POST request using a MongoDB search syntax.
218
+ Using OAS Telemetry, you can access telemetry data through the UI (WIP), the `/telemetry/traces` endpoint, or the `/telemetry/traces/find` endpoint with a POST request using a MongoDB search syntax.
161
219
 
162
220
  Note: if authentication is enabled, you must provide the correct credentials to access the telemetry data.
163
221
 
164
222
  ### Simple Search Example
165
223
 
166
- To perform a simple search, send a POST request to the `/telemetry/find` endpoint with the following JSON payload:
224
+ To perform a simple search, send a POST request to the `/telemetry/traces/find` endpoint with the following JSON payload:
167
225
 
168
226
  ```json
169
227
  {
@@ -180,7 +238,7 @@ To perform a simple search, send a POST request to the `/telemetry/find` endpoin
180
238
 
181
239
  ### Complex Search Example
182
240
 
183
- For more complex searches using regex, additional parsing on the server and extra attributes in the POST request are required. Send a POST request to the `/telemetry/find` endpoint with the following JSON payload:
241
+ For more complex searches using regex, additional parsing on the server and extra attributes in the POST request are required. Send a POST request to the `/telemetry/traces/find` endpoint with the following JSON payload:
184
242
 
185
243
  ```json
186
244
  {
@@ -16,8 +16,8 @@ class InMemoryDBMetricsExporter {
16
16
  export(metrics, resultCallback) {
17
17
  try {
18
18
  if (!this._stopped) {
19
- // metrics = metrics?.scopeMetrics;
20
- const cleanMetrics = (0, _circular.applyNesting)(metrics);
19
+ const scopeMetrics = metrics?.scopeMetrics;
20
+ const cleanMetrics = (0, _circular.applyNesting)(scopeMetrics);
21
21
  this._metrics.insert(cleanMetrics, (err, _newDoc) => {
22
22
  if (err) {
23
23
  console.error('Insertion Error:', err);
@@ -62,5 +62,13 @@ class InMemoryDBMetricsExporter {
62
62
  getFinishedMetrics() {
63
63
  return this._metrics.getAllData();
64
64
  }
65
+ /**
66
+ * Inserts metrics into the in-memory database.
67
+ * @param metrics - The metrics to insert.
68
+ * @param callback - The callback to execute after insertion.
69
+ */
70
+ insert(metrics, callback) {
71
+ this._metrics.insert(metrics, callback);
72
+ }
65
73
  }
66
74
  exports.InMemoryDBMetricsExporter = InMemoryDBMetricsExporter;
@@ -8,6 +8,7 @@ var _core = require("@opentelemetry/core");
8
8
  var _nedb = _interopRequireDefault(require("@seald-io/nedb"));
9
9
  var _logger = _interopRequireDefault(require("../utils/logger.cjs"));
10
10
  var _circular = require("../utils/circular.cjs");
11
+ var _config = require("../config.cjs");
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  //import in memory database
13
14
 
@@ -24,7 +25,13 @@ class InMemoryExporter {
24
25
  // Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
25
26
  const cleanSpans = readableSpans.map(nestedSpan => (0, _circular.removeCircularRefs)(nestedSpan)) // to avoid JSON parsing error
26
27
  .map(span => (0, _circular.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
+ .filter(span => {
29
+ const target = span?.attributes?.http?.target; // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils'
30
+ if (target && target.includes(_config.globalOasTlmConfig.baseURL)) {
31
+ return target.includes(_config.globalOasTlmConfig.baseURL + '/utils');
32
+ }
33
+ return true;
34
+ });
28
35
  // Insert spans into the in-memory database
29
36
  this._spans.insert(cleanSpans, (err, _newDoc) => {
30
37
  // p = {name, plugin
@@ -83,5 +90,13 @@ class InMemoryExporter {
83
90
  getFinishedSpans() {
84
91
  return this._spans.getAllData();
85
92
  }
93
+ /**
94
+ * Inserts spans into the in-memory database.
95
+ * @param spans - The spans to insert.
96
+ * @param callback - The callback to execute after insertion.
97
+ */
98
+ insert(spans, callback) {
99
+ this._spans.insert(spans, callback);
100
+ }
86
101
  }
87
102
  exports.InMemoryExporter = InMemoryExporter;
@@ -17,6 +17,7 @@ class InMemoryLogRecordExporter {
17
17
  storeFields: ['_id'],
18
18
  idField: '_id'
19
19
  });
20
+ this._stopped = false;
20
21
  }
21
22
  /*
22
23
  * SUPER WARNING:
@@ -30,6 +31,12 @@ class InMemoryLogRecordExporter {
30
31
  * @param resultCallback
31
32
  */
32
33
  export(logs, resultCallback) {
34
+ if (this._stopped) {
35
+ resultCallback({
36
+ code: _core.ExportResultCode.SUCCESS
37
+ });
38
+ return;
39
+ }
33
40
  const logsToInsert = logs.map(logRecord => {
34
41
  // Remove circular references first, then apply nesting, then export info
35
42
  const formattedLog = this._formatLogRecord(logRecord);
@@ -37,20 +44,7 @@ class InMemoryLogRecordExporter {
37
44
  const nestedLog = (0, _circular.applyNesting)(cleanedLog);
38
45
  return nestedLog;
39
46
  });
40
- this._db.insert(logsToInsert, (err, newDocs) => {
41
- if (err) {
42
- console.dir(err);
43
- resultCallback({
44
- code: _core.ExportResultCode.FAILED
45
- });
46
- return;
47
- }
48
- // console.dir(newDocs, { depth: 3 });
49
- newDocs.forEach(doc => this._miniSearch.add(doc));
50
- resultCallback({
51
- code: _core.ExportResultCode.SUCCESS
52
- });
53
- });
47
+ this._insertLogs(logsToInsert, resultCallback);
54
48
  }
55
49
  reset() {
56
50
  this._db = new _nedb.default();
@@ -81,7 +75,32 @@ class InMemoryLogRecordExporter {
81
75
  }
82
76
  this._db.find(query, callback);
83
77
  }
84
- getFinishedSpans() {
78
+ insert(data, callback) {
79
+ this._insertLogs(data, result => {
80
+ if (result.code === _core.ExportResultCode.SUCCESS) {
81
+ this._db.find({}, (err, docs) => {
82
+ if (err) {
83
+ console.dir(err);
84
+ callback(err, []);
85
+ return;
86
+ }
87
+ callback(null, docs);
88
+ });
89
+ } else {
90
+ callback(new Error('Failed to insert logs'), []);
91
+ }
92
+ });
93
+ }
94
+ start() {
95
+ this._stopped = false;
96
+ }
97
+ stop() {
98
+ this._stopped = true;
99
+ }
100
+ isRunning() {
101
+ return !this._stopped;
102
+ }
103
+ getFinishedLogs() {
85
104
  return this._db.getAllData();
86
105
  }
87
106
  /**
@@ -106,5 +125,22 @@ class InMemoryLogRecordExporter {
106
125
  attributes: logRecord.attributes
107
126
  };
108
127
  }
128
+ _insertLogs(logsToInsert, resultCallback) {
129
+ this._db.insert(logsToInsert, (err, newDocs) => {
130
+ if (err) {
131
+ console.dir(err);
132
+ resultCallback({
133
+ code: _core.ExportResultCode.FAILED
134
+ });
135
+ return;
136
+ }
137
+ // console.dir(newDocs, { depth: 3 });
138
+ newDocs.forEach(doc => this._miniSearch.add(doc));
139
+ resultCallback({
140
+ code: _core.ExportResultCode.SUCCESS
141
+ });
142
+ });
143
+ return;
144
+ }
109
145
  }
110
146
  exports.InMemoryLogRecordExporter = InMemoryLogRecordExporter;
@@ -3,11 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.resetLogs = exports.listLogs = exports.findLogs = void 0;
6
+ exports.stopLogs = exports.statusLogs = exports.startLogs = exports.resetLogs = exports.listLogs = exports.insertLogsToDb = exports.findLogs = void 0;
7
7
  var _config = require("../config.cjs");
8
+ const _excluded = ["_id"];
9
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
8
11
  const listLogs = async (req, res) => {
9
12
  try {
10
- const logs = _config.globalOasTlmConfig.logExporter.getFinishedSpans();
13
+ const logs = _config.globalOasTlmConfig.logExporter.getFinishedLogs();
11
14
  res.send({
12
15
  logsCount: logs.length,
13
16
  logs: logs
@@ -52,4 +55,67 @@ const resetLogs = (req, res) => {
52
55
  _config.globalOasTlmConfig.logExporter.reset();
53
56
  res.send('Logs reset');
54
57
  };
55
- exports.resetLogs = resetLogs;
58
+ exports.resetLogs = resetLogs;
59
+ const insertLogsToDb = async (req, res) => {
60
+ const jsonContent = req.body.logs;
61
+ const resetData = req.query.reset === 'true';
62
+ if (!Array.isArray(jsonContent)) {
63
+ res.status(400).send({
64
+ error: 'Invalid data format. Expected an array of JSON objects.'
65
+ });
66
+ return;
67
+ }
68
+ const cleanedLogs = jsonContent.map(log => {
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
+ const {
71
+ _id
72
+ } = log,
73
+ rest = _objectWithoutProperties(log, _excluded); // Remove _id if it exists
74
+ return rest; // Return the cleaned log object
75
+ });
76
+ try {
77
+ let message = '';
78
+ if (resetData) {
79
+ _config.globalOasTlmConfig.logExporter.reset();
80
+ message += 'Logs Database reset. ';
81
+ }
82
+ await new Promise((resolve, reject) => {
83
+ _config.globalOasTlmConfig.logExporter.insert(cleanedLogs, (err, newDocs) => {
84
+ if (err) {
85
+ console.error('Error inserting logs:', err);
86
+ return reject(err);
87
+ }
88
+ resolve(newDocs);
89
+ });
90
+ });
91
+ message += `Inserted ${cleanedLogs.length} logs.`;
92
+ res.send({
93
+ message,
94
+ InsertedLogsCount: cleanedLogs.length
95
+ });
96
+ } catch (err) {
97
+ console.error(err);
98
+ res.status(500).send({
99
+ error: 'Failed to reset and insert data',
100
+ details: err.message
101
+ });
102
+ }
103
+ };
104
+ exports.insertLogsToDb = insertLogsToDb;
105
+ const startLogs = (req, res) => {
106
+ _config.globalOasTlmConfig.logExporter.start();
107
+ res.send('Log collection started');
108
+ };
109
+ exports.startLogs = startLogs;
110
+ const stopLogs = (req, res) => {
111
+ _config.globalOasTlmConfig.logExporter.stop();
112
+ res.send('Log collection stopped');
113
+ };
114
+ exports.stopLogs = stopLogs;
115
+ const statusLogs = (req, res) => {
116
+ const isRunning = _config.globalOasTlmConfig.logExporter.isRunning() || false;
117
+ res.send({
118
+ active: isRunning
119
+ });
120
+ };
121
+ exports.statusLogs = statusLogs;
@@ -7,7 +7,12 @@ exports.logRoutes = exports.default = void 0;
7
7
  var _express = require("express");
8
8
  var _logController = require("./logController.cjs");
9
9
  const logRoutes = exports.logRoutes = (0, _express.Router)();
10
+ // Logs Control
11
+ logRoutes.get('/start', _logController.startLogs);
12
+ logRoutes.get('/stop', _logController.stopLogs);
13
+ logRoutes.get('/status', _logController.statusLogs);
14
+ logRoutes.get('/reset', _logController.resetLogs);
10
15
  logRoutes.get('/', _logController.listLogs);
16
+ logRoutes.post('/', _logController.insertLogsToDb);
11
17
  logRoutes.post('/find', _logController.findLogs);
12
- logRoutes.get('/reset', _logController.resetLogs);
13
18
  var _default = exports.default = logRoutes;
@@ -3,11 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.resetMetrics = exports.listMetrics = exports.findMetrics = void 0;
6
+ exports.stopMetrics = exports.statusMetrics = exports.startMetrics = exports.resetMetrics = exports.listMetrics = exports.insertMetricsToDb = exports.findMetrics = void 0;
7
7
  var _config = require("../config.cjs");
8
+ const _excluded = ["_id"];
9
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
8
11
  const listMetrics = async (req, res) => {
9
12
  try {
10
- const metrics = await _config.globalOasTlmConfig.metricsExporter.getFinishedMetrics();
13
+ const metrics = _config.globalOasTlmConfig.metricsExporter.getFinishedMetrics();
11
14
  res.send({
12
15
  metricsCount: metrics.length,
13
16
  metrics: metrics
@@ -45,4 +48,67 @@ const resetMetrics = (req, res) => {
45
48
  _config.globalOasTlmConfig.metricsExporter.reset();
46
49
  res.send('Metrics reset');
47
50
  };
48
- exports.resetMetrics = resetMetrics;
51
+ exports.resetMetrics = resetMetrics;
52
+ const insertMetricsToDb = async (req, res) => {
53
+ const jsonContent = req.body.metrics;
54
+ const resetData = req.query.reset === 'true';
55
+ if (!Array.isArray(jsonContent)) {
56
+ res.status(400).send({
57
+ error: 'Invalid data format. Expected an array of JSON objects.'
58
+ });
59
+ return;
60
+ }
61
+ const cleanedMetrics = jsonContent.map(metric => {
62
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
63
+ const {
64
+ _id
65
+ } = metric,
66
+ rest = _objectWithoutProperties(metric, _excluded); // Remove _id if it exists
67
+ return rest; // Return the cleaned metric object
68
+ });
69
+ try {
70
+ let message = '';
71
+ if (resetData) {
72
+ _config.globalOasTlmConfig.metricsExporter.reset();
73
+ message += 'Metrics Database reset. ';
74
+ }
75
+ await new Promise((resolve, reject) => {
76
+ _config.globalOasTlmConfig.metricsExporter.insert(cleanedMetrics, (err, newDocs) => {
77
+ if (err) {
78
+ console.error('Error inserting metrics:', err);
79
+ return reject(err);
80
+ }
81
+ resolve(newDocs);
82
+ });
83
+ });
84
+ message += `Inserted ${cleanedMetrics.length} metrics.`;
85
+ res.send({
86
+ message,
87
+ InsertedMetricsCount: cleanedMetrics.length
88
+ });
89
+ } catch (err) {
90
+ console.error(err);
91
+ res.status(500).send({
92
+ error: 'Failed to reset and insert data',
93
+ details: err.message
94
+ });
95
+ }
96
+ };
97
+ exports.insertMetricsToDb = insertMetricsToDb;
98
+ const startMetrics = (req, res) => {
99
+ _config.globalOasTlmConfig.metricsExporter.start();
100
+ res.send('Metrics collection started');
101
+ };
102
+ exports.startMetrics = startMetrics;
103
+ const stopMetrics = (req, res) => {
104
+ _config.globalOasTlmConfig.metricsExporter.stop();
105
+ res.send('Metrics collection stopped');
106
+ };
107
+ exports.stopMetrics = stopMetrics;
108
+ const statusMetrics = (req, res) => {
109
+ const isRunning = _config.globalOasTlmConfig.metricsExporter.isRunning() || false;
110
+ res.send({
111
+ active: isRunning
112
+ });
113
+ };
114
+ exports.statusMetrics = statusMetrics;
@@ -9,6 +9,10 @@ var _metricsController = require("./metricsController.cjs");
9
9
  const metricsRoutes = exports.metricsRoutes = (0, _express.Router)();
10
10
  // Metrics Control
11
11
  metricsRoutes.get('/', _metricsController.listMetrics);
12
+ metricsRoutes.post('/', _metricsController.insertMetricsToDb);
12
13
  metricsRoutes.post('/find', _metricsController.findMetrics);
13
14
  metricsRoutes.get('/reset', _metricsController.resetMetrics);
15
+ metricsRoutes.get('/start', _metricsController.startMetrics);
16
+ metricsRoutes.get('/stop', _metricsController.stopMetrics);
17
+ metricsRoutes.get('/status', _metricsController.statusMetrics);
14
18
  var _default = exports.default = metricsRoutes;
@@ -3,8 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.stopTelemetry = exports.statusTelemetry = exports.startTelemetry = exports.resetTelemetry = exports.listTelemetry = exports.findTelemetry = void 0;
6
+ exports.stopTelemetry = exports.statusTelemetry = exports.startTelemetry = exports.resetTelemetry = exports.listTelemetry = exports.insertTracesToDb = exports.findTelemetry = void 0;
7
7
  var _config = require("../config.cjs");
8
+ const _excluded = ["_id"];
9
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
8
11
  const startTelemetry = (req, res) => {
9
12
  _config.globalOasTlmConfig.dynamicSpanExporter.exporter.start();
10
13
  res.send('Telemetry started');
@@ -78,4 +81,50 @@ const findTelemetry = (req, res) => {
78
81
  });
79
82
  });
80
83
  };
81
- exports.findTelemetry = findTelemetry;
84
+ exports.findTelemetry = findTelemetry;
85
+ const insertTracesToDb = async (req, res) => {
86
+ const jsonContent = req.body.spans;
87
+ const resetData = req.query.reset === 'true';
88
+ if (!Array.isArray(jsonContent)) {
89
+ res.status(400).send({
90
+ error: 'Invalid data format. Expected an array of JSON objects.'
91
+ });
92
+ return;
93
+ }
94
+ const cleanedTraces = jsonContent.map(trace => {
95
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
96
+ const {
97
+ _id
98
+ } = trace,
99
+ rest = _objectWithoutProperties(trace, _excluded); // Remove _id if it exists
100
+ return rest; // Return the cleaned trace object
101
+ });
102
+ try {
103
+ let message = '';
104
+ if (resetData) {
105
+ _config.globalOasTlmConfig.dynamicSpanExporter.exporter.reset();
106
+ message += 'Traces Database reset. ';
107
+ }
108
+ await new Promise((resolve, reject) => {
109
+ _config.globalOasTlmConfig.dynamicSpanExporter.exporter.insert(cleanedTraces, (err, newDocs) => {
110
+ if (err) {
111
+ console.error('Error inserting traces:', err);
112
+ return reject(err);
113
+ }
114
+ resolve(newDocs);
115
+ });
116
+ });
117
+ message += `Inserted ${cleanedTraces.length} traces.`;
118
+ res.send({
119
+ message,
120
+ InsertedTracesCount: cleanedTraces.length
121
+ });
122
+ } catch (err) {
123
+ console.error(err);
124
+ res.status(500).send({
125
+ error: 'Failed to reset and insert data',
126
+ details: err.message
127
+ });
128
+ }
129
+ };
130
+ exports.insertTracesToDb = insertTracesToDb;
@@ -8,6 +8,7 @@ var _express = require("express");
8
8
  var _traceController = require("./traceController.cjs");
9
9
  const traceRoutes = exports.traceRoutes = (0, _express.Router)();
10
10
  traceRoutes.get('/', _traceController.listTelemetry);
11
+ traceRoutes.post('/', _traceController.insertTracesToDb);
11
12
  traceRoutes.post('/find', _traceController.findTelemetry);
12
13
  // Telemetry Control
13
14
  traceRoutes.get('/start', _traceController.startTelemetry);
@@ -9,4 +9,20 @@ var _utilController = require("./utilController.cjs");
9
9
  const utilsRoutes = exports.utilsRoutes = (0, _express.Router)();
10
10
  utilsRoutes.get('/spec', _utilController.specLoader);
11
11
  utilsRoutes.get('/heapStats', _utilController.heapStats);
12
+ utilsRoutes.get('/generateLog', (req, res) => {
13
+ const log = req.query.log || 'Default log message';
14
+ console.log('Generated log:', log);
15
+ res.send({
16
+ message: 'Log generated',
17
+ log: log
18
+ });
19
+ });
20
+ utilsRoutes.get('/wait/:seconds?', async (req, res) => {
21
+ const seconds = parseInt(req.params.seconds ?? "1", 10);
22
+ const waitTime = isNaN(seconds) ? 1 : seconds;
23
+ await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
24
+ res.send({
25
+ waited: waitTime
26
+ });
27
+ });
12
28
  var _default = exports.default = utilsRoutes;
@@ -32,7 +32,9 @@ const configureRoutes = router => {
32
32
  if (req.body !== undefined) {
33
33
  return next(); // Already parsed, no need to parse again.
34
34
  }
35
- return (0, _express.json)()(req, res, next);
35
+ return (0, _express.json)({
36
+ limit: '10mb'
37
+ })(req, res, next);
36
38
  });
37
39
  const allAuthMiddlewares = getWrappedMiddlewares(() => _config.globalOasTlmConfig.authEnabled, [(0, _cookieParser.default)(), _authRoutes.default, _authMiddleware.authMiddleware]);
38
40
  const baseURL = _config.globalOasTlmConfig.baseURL;
@@ -9,8 +9,8 @@ export class InMemoryDBMetricsExporter {
9
9
  export(metrics, resultCallback) {
10
10
  try {
11
11
  if (!this._stopped) {
12
- // metrics = metrics?.scopeMetrics;
13
- const cleanMetrics = applyNesting(metrics);
12
+ const scopeMetrics = metrics?.scopeMetrics;
13
+ const cleanMetrics = applyNesting(scopeMetrics);
14
14
  this._metrics.insert(cleanMetrics, (err, _newDoc) => {
15
15
  if (err) {
16
16
  console.error('Insertion Error:', err);
@@ -54,4 +54,12 @@ export class InMemoryDBMetricsExporter {
54
54
  getFinishedMetrics() {
55
55
  return this._metrics.getAllData();
56
56
  }
57
+ /**
58
+ * Inserts metrics into the in-memory database.
59
+ * @param metrics - The metrics to insert.
60
+ * @param callback - The callback to execute after insertion.
61
+ */
62
+ insert(metrics, callback) {
63
+ this._metrics.insert(metrics, callback);
64
+ }
57
65
  }
@@ -3,6 +3,7 @@ import { ExportResultCode } from '@opentelemetry/core';
3
3
  import dataStore from '@seald-io/nedb';
4
4
  import logger from '../utils/logger.js';
5
5
  import { applyNesting, removeCircularRefs } from '../utils/circular.js';
6
+ import { globalOasTlmConfig } from '../config.js';
6
7
  export class InMemoryExporter {
7
8
  constructor() {
8
9
  // Overrided by dynamic exporter
@@ -18,7 +19,13 @@ export class InMemoryExporter {
18
19
  const cleanSpans = readableSpans
19
20
  .map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
20
21
  .map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
21
- .filter(span => !span?.attributes?.http?.target?.includes("/telemetry")); // to avoid telemetry spans
22
+ .filter(span => {
23
+ const target = span?.attributes?.http?.target; // Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils'
24
+ if (target && target.includes(globalOasTlmConfig.baseURL)) {
25
+ return target.includes(globalOasTlmConfig.baseURL + '/utils');
26
+ }
27
+ return true;
28
+ });
22
29
  // Insert spans into the in-memory database
23
30
  this._spans.insert(cleanSpans, (err, _newDoc) => {
24
31
  // p = {name, plugin
@@ -84,4 +91,12 @@ export class InMemoryExporter {
84
91
  return this._spans.getAllData();
85
92
  }
86
93
  ;
94
+ /**
95
+ * Inserts spans into the in-memory database.
96
+ * @param spans - The spans to insert.
97
+ * @param callback - The callback to execute after insertion.
98
+ */
99
+ insert(spans, callback) {
100
+ this._spans.insert(spans, callback);
101
+ }
87
102
  }
@@ -11,6 +11,7 @@ export class InMemoryLogRecordExporter {
11
11
  storeFields: ['_id'],
12
12
  idField: '_id',
13
13
  });
14
+ this._stopped = false;
14
15
  }
15
16
  /*
16
17
  * SUPER WARNING:
@@ -24,6 +25,10 @@ export class InMemoryLogRecordExporter {
24
25
  * @param resultCallback
25
26
  */
26
27
  export(logs, resultCallback) {
28
+ if (this._stopped) {
29
+ resultCallback({ code: ExportResultCode.SUCCESS });
30
+ return;
31
+ }
27
32
  const logsToInsert = logs.map(logRecord => {
28
33
  // Remove circular references first, then apply nesting, then export info
29
34
  const formattedLog = this._formatLogRecord(logRecord);
@@ -31,16 +36,7 @@ export class InMemoryLogRecordExporter {
31
36
  const nestedLog = applyNesting(cleanedLog);
32
37
  return nestedLog;
33
38
  });
34
- this._db.insert(logsToInsert, (err, newDocs) => {
35
- if (err) {
36
- console.dir(err);
37
- resultCallback({ code: ExportResultCode.FAILED });
38
- return;
39
- }
40
- // console.dir(newDocs, { depth: 3 });
41
- newDocs.forEach((doc) => this._miniSearch.add(doc));
42
- resultCallback({ code: ExportResultCode.SUCCESS });
43
- });
39
+ this._insertLogs(logsToInsert, resultCallback);
44
40
  }
45
41
  reset() {
46
42
  this._db = new Datastore();
@@ -67,7 +63,33 @@ export class InMemoryLogRecordExporter {
67
63
  }
68
64
  this._db.find(query, callback);
69
65
  }
70
- getFinishedSpans() {
66
+ insert(data, callback) {
67
+ this._insertLogs(data, (result) => {
68
+ if (result.code === ExportResultCode.SUCCESS) {
69
+ this._db.find({}, (err, docs) => {
70
+ if (err) {
71
+ console.dir(err);
72
+ callback(err, []);
73
+ return;
74
+ }
75
+ callback(null, docs);
76
+ });
77
+ }
78
+ else {
79
+ callback(new Error('Failed to insert logs'), []);
80
+ }
81
+ });
82
+ }
83
+ start() {
84
+ this._stopped = false;
85
+ }
86
+ stop() {
87
+ this._stopped = true;
88
+ }
89
+ isRunning() {
90
+ return !this._stopped;
91
+ }
92
+ getFinishedLogs() {
71
93
  return this._db.getAllData();
72
94
  }
73
95
  /**
@@ -92,4 +114,17 @@ export class InMemoryLogRecordExporter {
92
114
  attributes: logRecord.attributes,
93
115
  };
94
116
  }
117
+ _insertLogs(logsToInsert, resultCallback) {
118
+ this._db.insert(logsToInsert, (err, newDocs) => {
119
+ if (err) {
120
+ console.dir(err);
121
+ resultCallback({ code: ExportResultCode.FAILED });
122
+ return;
123
+ }
124
+ // console.dir(newDocs, { depth: 3 });
125
+ newDocs.forEach((doc) => this._miniSearch.add(doc));
126
+ resultCallback({ code: ExportResultCode.SUCCESS });
127
+ });
128
+ return;
129
+ }
95
130
  }
@@ -1,7 +1,7 @@
1
1
  import { globalOasTlmConfig } from '../config.js';
2
2
  export const listLogs = async (req, res) => {
3
3
  try {
4
- const logs = globalOasTlmConfig.logExporter.getFinishedSpans();
4
+ const logs = globalOasTlmConfig.logExporter.getFinishedLogs();
5
5
  res.send({ logsCount: logs.length, logs: logs });
6
6
  }
7
7
  catch (err) {
@@ -34,3 +34,50 @@ export const resetLogs = (req, res) => {
34
34
  globalOasTlmConfig.logExporter.reset();
35
35
  res.send('Logs reset');
36
36
  };
37
+ export const insertLogsToDb = async (req, res) => {
38
+ const jsonContent = req.body.logs;
39
+ const resetData = req.query.reset === 'true';
40
+ if (!Array.isArray(jsonContent)) {
41
+ res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
42
+ return;
43
+ }
44
+ const cleanedLogs = jsonContent.map((log) => {
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ const { _id, ...rest } = log; // Remove _id if it exists
47
+ return rest; // Return the cleaned log object
48
+ });
49
+ try {
50
+ let message = '';
51
+ if (resetData) {
52
+ globalOasTlmConfig.logExporter.reset();
53
+ message += 'Logs Database reset. ';
54
+ }
55
+ await new Promise((resolve, reject) => {
56
+ globalOasTlmConfig.logExporter.insert(cleanedLogs, (err, newDocs) => {
57
+ if (err) {
58
+ console.error('Error inserting logs:', err);
59
+ return reject(err);
60
+ }
61
+ resolve(newDocs);
62
+ });
63
+ });
64
+ message += `Inserted ${cleanedLogs.length} logs.`;
65
+ res.send({ message, InsertedLogsCount: cleanedLogs.length });
66
+ }
67
+ catch (err) {
68
+ console.error(err);
69
+ res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
70
+ }
71
+ };
72
+ export const startLogs = (req, res) => {
73
+ globalOasTlmConfig.logExporter.start();
74
+ res.send('Log collection started');
75
+ };
76
+ export const stopLogs = (req, res) => {
77
+ globalOasTlmConfig.logExporter.stop();
78
+ res.send('Log collection stopped');
79
+ };
80
+ export const statusLogs = (req, res) => {
81
+ const isRunning = globalOasTlmConfig.logExporter.isRunning() || false;
82
+ res.send({ active: isRunning });
83
+ };
@@ -1,7 +1,12 @@
1
1
  import { Router } from 'express';
2
- import { listLogs, findLogs, resetLogs } from './logController.js';
2
+ import { startLogs, stopLogs, statusLogs, resetLogs, listLogs, findLogs, insertLogsToDb } from './logController.js';
3
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);
4
9
  logRoutes.get('/', listLogs);
10
+ logRoutes.post('/', insertLogsToDb);
5
11
  logRoutes.post('/find', findLogs);
6
- logRoutes.get('/reset', resetLogs);
7
12
  export default logRoutes;
@@ -1,7 +1,7 @@
1
1
  import { globalOasTlmConfig } from '../config.js';
2
2
  export const listMetrics = async (req, res) => {
3
3
  try {
4
- const metrics = await globalOasTlmConfig.metricsExporter.getFinishedMetrics();
4
+ const metrics = globalOasTlmConfig.metricsExporter.getFinishedMetrics();
5
5
  res.send({ metricsCount: metrics.length, metrics: metrics });
6
6
  }
7
7
  catch (err) {
@@ -26,3 +26,50 @@ export const resetMetrics = (req, res) => {
26
26
  globalOasTlmConfig.metricsExporter.reset();
27
27
  res.send('Metrics reset');
28
28
  };
29
+ export const insertMetricsToDb = async (req, res) => {
30
+ const jsonContent = req.body.metrics;
31
+ const resetData = req.query.reset === 'true';
32
+ if (!Array.isArray(jsonContent)) {
33
+ res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
34
+ return;
35
+ }
36
+ const cleanedMetrics = jsonContent.map((metric) => {
37
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
+ const { _id, ...rest } = metric; // Remove _id if it exists
39
+ return rest; // Return the cleaned metric object
40
+ });
41
+ try {
42
+ let message = '';
43
+ if (resetData) {
44
+ globalOasTlmConfig.metricsExporter.reset();
45
+ message += 'Metrics Database reset. ';
46
+ }
47
+ await new Promise((resolve, reject) => {
48
+ globalOasTlmConfig.metricsExporter.insert(cleanedMetrics, (err, newDocs) => {
49
+ if (err) {
50
+ console.error('Error inserting metrics:', err);
51
+ return reject(err);
52
+ }
53
+ resolve(newDocs);
54
+ });
55
+ });
56
+ message += `Inserted ${cleanedMetrics.length} metrics.`;
57
+ res.send({ message, InsertedMetricsCount: cleanedMetrics.length });
58
+ }
59
+ catch (err) {
60
+ console.error(err);
61
+ res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
62
+ }
63
+ };
64
+ export const startMetrics = (req, res) => {
65
+ globalOasTlmConfig.metricsExporter.start();
66
+ res.send('Metrics collection started');
67
+ };
68
+ export const stopMetrics = (req, res) => {
69
+ globalOasTlmConfig.metricsExporter.stop();
70
+ res.send('Metrics collection stopped');
71
+ };
72
+ export const statusMetrics = (req, res) => {
73
+ const isRunning = globalOasTlmConfig.metricsExporter.isRunning() || false;
74
+ res.send({ active: isRunning });
75
+ };
@@ -1,8 +1,12 @@
1
1
  import { Router } from 'express';
2
- import { listMetrics, findMetrics, resetMetrics } from './metricsController.js';
2
+ import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics } from './metricsController.js';
3
3
  export const metricsRoutes = Router();
4
4
  // Metrics Control
5
5
  metricsRoutes.get('/', listMetrics);
6
+ metricsRoutes.post('/', insertMetricsToDb);
6
7
  metricsRoutes.post('/find', findMetrics);
7
8
  metricsRoutes.get('/reset', resetMetrics);
9
+ metricsRoutes.get('/start', startMetrics);
10
+ metricsRoutes.get('/stop', stopMetrics);
11
+ metricsRoutes.get('/status', statusMetrics);
8
12
  export default metricsRoutes;
@@ -52,3 +52,38 @@ export const findTelemetry = (req, res) => {
52
52
  res.send({ spansCount: spans.length, spans: spans });
53
53
  });
54
54
  };
55
+ export const insertTracesToDb = async (req, res) => {
56
+ const jsonContent = req.body.spans;
57
+ const resetData = req.query.reset === 'true';
58
+ if (!Array.isArray(jsonContent)) {
59
+ res.status(400).send({ error: 'Invalid data format. Expected an array of JSON objects.' });
60
+ return;
61
+ }
62
+ const cleanedTraces = jsonContent.map((trace) => {
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ const { _id, ...rest } = trace; // Remove _id if it exists
65
+ return rest; // Return the cleaned trace object
66
+ });
67
+ try {
68
+ let message = '';
69
+ if (resetData) {
70
+ globalOasTlmConfig.dynamicSpanExporter.exporter.reset();
71
+ message += 'Traces Database reset. ';
72
+ }
73
+ await new Promise((resolve, reject) => {
74
+ globalOasTlmConfig.dynamicSpanExporter.exporter.insert(cleanedTraces, (err, newDocs) => {
75
+ if (err) {
76
+ console.error('Error inserting traces:', err);
77
+ return reject(err);
78
+ }
79
+ resolve(newDocs);
80
+ });
81
+ });
82
+ message += `Inserted ${cleanedTraces.length} traces.`;
83
+ res.send({ message, InsertedTracesCount: cleanedTraces.length });
84
+ }
85
+ catch (err) {
86
+ console.error(err);
87
+ res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
88
+ }
89
+ };
@@ -1,7 +1,8 @@
1
1
  import { Router } from 'express';
2
- import { startTelemetry, stopTelemetry, statusTelemetry, resetTelemetry, listTelemetry, findTelemetry } from './traceController.js';
2
+ import { startTelemetry, stopTelemetry, statusTelemetry, resetTelemetry, listTelemetry, findTelemetry, insertTracesToDb } from './traceController.js';
3
3
  export const traceRoutes = Router();
4
4
  traceRoutes.get('/', listTelemetry);
5
+ traceRoutes.post('/', insertTracesToDb);
5
6
  traceRoutes.post('/find', findTelemetry);
6
7
  // Telemetry Control
7
8
  traceRoutes.get('/start', startTelemetry);
@@ -3,4 +3,15 @@ import { specLoader, heapStats } from './utilController.js';
3
3
  export const utilsRoutes = Router();
4
4
  utilsRoutes.get('/spec', specLoader);
5
5
  utilsRoutes.get('/heapStats', heapStats);
6
+ utilsRoutes.get('/generateLog', (req, res) => {
7
+ const log = req.query.log || 'Default log message';
8
+ console.log('Generated log:', log);
9
+ res.send({ message: 'Log generated', log: log });
10
+ });
11
+ utilsRoutes.get('/wait/:seconds?', async (req, res) => {
12
+ const seconds = parseInt(req.params.seconds ?? "1", 10);
13
+ const waitTime = isNaN(seconds) ? 1 : seconds;
14
+ await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
15
+ res.send({ waited: waitTime });
16
+ });
6
17
  export default utilsRoutes;
@@ -24,7 +24,7 @@ export const configureRoutes = (router) => {
24
24
  if (req.body !== undefined) {
25
25
  return next(); // Already parsed, no need to parse again.
26
26
  }
27
- return json()(req, res, next);
27
+ return json({ limit: '10mb' })(req, res, next);
28
28
  });
29
29
  const allAuthMiddlewares = getWrappedMiddlewares(() => globalOasTlmConfig.authEnabled, [cookieParser(), authRoutes, authMiddleware]);
30
30
  const baseURL = globalOasTlmConfig.baseURL;
@@ -12,4 +12,10 @@ export declare class InMemoryDBMetricsExporter {
12
12
  find(search: any, callback: any): void;
13
13
  reset(): void;
14
14
  getFinishedMetrics(): import("@seald-io/nedb").Document<Record<string, any>>[];
15
+ /**
16
+ * Inserts metrics into the in-memory database.
17
+ * @param metrics - The metrics to insert.
18
+ * @param callback - The callback to execute after insertion.
19
+ */
20
+ insert(metrics: any[], callback: (err: any, newDocs: any[]) => void): void;
15
21
  }
@@ -21,4 +21,10 @@ export declare class InMemoryExporter implements OasTlmExporter {
21
21
  find(search: any, callback: any): void;
22
22
  reset(): void;
23
23
  getFinishedSpans(): import("@seald-io/nedb").Document<Record<string, any>>[];
24
+ /**
25
+ * Inserts spans into the in-memory database.
26
+ * @param spans - The spans to insert.
27
+ * @param callback - The callback to execute after insertion.
28
+ */
29
+ insert(spans: any[], callback: (err: any, newDocs: any[]) => void): void;
24
30
  }
@@ -3,6 +3,7 @@ import { ReadableLogRecord, LogRecordExporter } from '@opentelemetry/sdk-logs';
3
3
  export declare class InMemoryLogRecordExporter implements LogRecordExporter {
4
4
  private _db;
5
5
  private _miniSearch;
6
+ private _stopped;
6
7
  constructor();
7
8
  /**
8
9
  * Export logs.
@@ -16,7 +17,11 @@ export declare class InMemoryLogRecordExporter implements LogRecordExporter {
16
17
  */
17
18
  shutdown(): Promise<void>;
18
19
  find(query: any, messageSearch: string | null, callback: (err: any, docs: any) => void): void;
19
- getFinishedSpans(): any[];
20
+ insert(data: any[], callback: (err: any, newDocs: any[]) => void): void;
21
+ start(): void;
22
+ stop(): void;
23
+ isRunning(): boolean;
24
+ getFinishedLogs(): any[];
20
25
  /**
21
26
  * @copyright The OpenTelemetry Authors
22
27
  * @license Apache-2.0
@@ -24,4 +29,5 @@ export declare class InMemoryLogRecordExporter implements LogRecordExporter {
24
29
  * @param logRecord
25
30
  */
26
31
  private _formatLogRecord;
32
+ private _insertLogs;
27
33
  }
@@ -2,3 +2,7 @@ import { Request, Response } from 'express';
2
2
  export declare const listLogs: (req: Request, res: Response) => Promise<void>;
3
3
  export declare const findLogs: (req: Request, res: Response) => Promise<void>;
4
4
  export declare const resetLogs: (req: Request, res: Response) => void;
5
+ export declare const insertLogsToDb: (req: Request, res: Response) => Promise<void>;
6
+ export declare const startLogs: (req: Request, res: Response) => void;
7
+ export declare const stopLogs: (req: Request, res: Response) => void;
8
+ export declare const statusLogs: (req: Request, res: Response) => void;
@@ -2,3 +2,7 @@ import { Request, Response } from 'express';
2
2
  export declare const listMetrics: (req: Request, res: Response) => Promise<void>;
3
3
  export declare const findMetrics: (req: Request, res: Response) => void;
4
4
  export declare const resetMetrics: (req: Request, res: Response) => void;
5
+ export declare const insertMetricsToDb: (req: Request, res: Response) => Promise<void>;
6
+ export declare const startMetrics: (req: Request, res: Response) => void;
7
+ export declare const stopMetrics: (req: Request, res: Response) => void;
8
+ export declare const statusMetrics: (req: Request, res: Response) => void;
@@ -5,3 +5,4 @@ export declare const statusTelemetry: (req: Request, res: Response) => void;
5
5
  export declare const resetTelemetry: (req: Request, res: Response) => void;
6
6
  export declare const listTelemetry: (req: Request, res: Response) => Promise<void>;
7
7
  export declare const findTelemetry: (req: Request, res: Response) => void;
8
+ export declare const insertTracesToDb: (req: Request, res: Response) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oas-tools/oas-telemetry",
3
- "version": "0.7.0-alpha.1",
3
+ "version": "0.7.0-alpha.2",
4
4
  "description": "Automatically collects and stores telemetry data in memory using OpenTelemetry. Just import the Express middleware to access it through the built-in REST API or web UI—no extra setup required.",
5
5
  "author": "Manuel Otero",
6
6
  "contributors": [
@@ -19,6 +19,8 @@
19
19
  "lint": "eslint --ext .ts . --fix"
20
20
  },
21
21
  "files": [
22
+ ".env.example",
23
+ "NOTICE",
22
24
  "dist"
23
25
  ],
24
26
  "main": "./dist/cjs/index.cjs",