@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.
- package/.env.example +26 -0
- package/NOTICE +8 -0
- package/README.md +76 -18
- package/dist/cjs/exporters/InMemoryDBMetricsExporter.cjs +10 -2
- package/dist/cjs/exporters/InMemoryDbExporter.cjs +16 -1
- package/dist/cjs/exporters/InMemoryLogRecordExporter.cjs +51 -15
- package/dist/cjs/tlm-log/logController.cjs +69 -3
- package/dist/cjs/tlm-log/logRoutes.cjs +6 -1
- package/dist/cjs/tlm-metric/metricsController.cjs +69 -3
- package/dist/cjs/tlm-metric/metricsRoutes.cjs +4 -0
- package/dist/cjs/tlm-trace/traceController.cjs +51 -2
- package/dist/cjs/tlm-trace/traceRoutes.cjs +1 -0
- package/dist/cjs/tlm-util/utilRoutes.cjs +16 -0
- package/dist/cjs/tlmRoutes.cjs +3 -1
- package/dist/esm/exporters/InMemoryDBMetricsExporter.js +10 -2
- package/dist/esm/exporters/InMemoryDbExporter.js +16 -1
- package/dist/esm/exporters/InMemoryLogRecordExporter.js +46 -11
- package/dist/esm/tlm-log/logController.js +48 -1
- package/dist/esm/tlm-log/logRoutes.js +7 -2
- package/dist/esm/tlm-metric/metricsController.js +48 -1
- package/dist/esm/tlm-metric/metricsRoutes.js +5 -1
- package/dist/esm/tlm-trace/traceController.js +35 -0
- package/dist/esm/tlm-trace/traceRoutes.js +2 -1
- package/dist/esm/tlm-util/utilRoutes.js +11 -0
- package/dist/esm/tlmRoutes.js +1 -1
- package/dist/types/exporters/InMemoryDBMetricsExporter.d.ts +6 -0
- package/dist/types/exporters/InMemoryDbExporter.d.ts +6 -0
- package/dist/types/exporters/InMemoryLogRecordExporter.d.ts +7 -1
- package/dist/types/tlm-log/logController.d.ts +4 -0
- package/dist/types/tlm-metric/metricsController.d.ts +4 -0
- package/dist/types/tlm-trace/traceController.d.ts +1 -0
- 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
|
|
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/
|
|
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
|
-
|
|
20
|
-
const cleanMetrics = (0, _circular.applyNesting)(
|
|
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 =>
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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;
|
package/dist/cjs/tlmRoutes.cjs
CHANGED
|
@@ -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)(
|
|
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
|
-
|
|
13
|
-
const cleanMetrics = applyNesting(
|
|
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 =>
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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 =
|
|
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;
|
package/dist/esm/tlmRoutes.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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",
|