@oas-tools/oas-telemetry 0.7.1 → 0.8.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +17 -3
- package/README.md +1 -2
- package/dist/cjs/config/bootConfig.cjs +16 -14
- package/dist/cjs/config/config.cjs +120 -125
- package/dist/cjs/config/config.types.cjs +1 -4
- package/dist/cjs/docs/openapi.yaml +158 -4
- package/dist/cjs/index.cjs +27 -30
- package/dist/cjs/routesManager.cjs +62 -70
- package/dist/cjs/telemetry/custom-implementations/exporters/DiskLogExporter.cjs +121 -0
- package/dist/cjs/telemetry/custom-implementations/exporters/DiskMetricExporter.cjs +101 -0
- package/dist/cjs/telemetry/custom-implementations/exporters/DiskTraceExporter.cjs +103 -0
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +194 -190
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +147 -99
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +143 -116
- package/dist/cjs/telemetry/custom-implementations/exporters/MultiMetricExporter.cjs +57 -0
- package/dist/cjs/telemetry/custom-implementations/instrumentations/logsInstrumentation.cjs +92 -0
- package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Chunk.cjs +159 -0
- package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Series.cjs +168 -0
- package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.cjs +392 -0
- package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/types.cjs +2 -0
- package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/utils.cjs +77 -0
- package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.cjs +65 -63
- package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.cjs +63 -62
- package/dist/cjs/telemetry/custom-implementations/utils/circular.cjs +47 -47
- package/dist/cjs/telemetry/custom-implementations/wrappers.cjs +209 -138
- package/dist/cjs/telemetry/initializeTelemetry.cjs +35 -91
- package/dist/cjs/telemetry/persistence/DiskImporter.cjs +85 -0
- package/dist/cjs/telemetry/persistence/DiskUtils.cjs +61 -0
- package/dist/cjs/telemetry/persistence/DiskWriter.cjs +66 -0
- package/dist/cjs/telemetry/telemetryConfigurator.cjs +139 -72
- package/dist/cjs/telemetry/telemetryRegistry.cjs +45 -31
- package/dist/cjs/tlm-ai/agent.cjs +49 -64
- package/dist/cjs/tlm-ai/aiController.cjs +54 -76
- package/dist/cjs/tlm-ai/aiRoutes.cjs +17 -20
- package/dist/cjs/tlm-ai/aiService.cjs +91 -95
- package/dist/cjs/tlm-ai/tools.cjs +177 -174
- package/dist/cjs/tlm-auth/authController.cjs +80 -123
- package/dist/cjs/tlm-auth/authMiddleware.cjs +25 -30
- package/dist/cjs/tlm-auth/authRoutes.cjs +11 -14
- package/dist/cjs/tlm-log/logController.cjs +135 -116
- package/dist/cjs/tlm-log/logRoutes.cjs +19 -20
- package/dist/cjs/tlm-log/logService.cjs +29 -0
- package/dist/cjs/tlm-metric/metricsController.cjs +154 -122
- package/dist/cjs/tlm-metric/metricsRoutes.cjs +22 -20
- package/dist/cjs/tlm-metric/metricsService.cjs +26 -0
- package/dist/cjs/tlm-plugin/pluginController.cjs +128 -140
- package/dist/cjs/tlm-plugin/pluginProcess.cjs +89 -94
- package/dist/cjs/tlm-plugin/pluginRoutes.cjs +11 -14
- package/dist/cjs/tlm-plugin/pluginService.cjs +73 -74
- package/dist/cjs/tlm-trace/traceController.cjs +140 -123
- package/dist/cjs/tlm-trace/traceRoutes.cjs +19 -20
- package/dist/cjs/tlm-trace/traceService.cjs +29 -0
- package/dist/cjs/tlm-ui/uiRoutes.cjs +63 -32
- package/dist/cjs/tlm-util/utilController.cjs +68 -70
- package/dist/cjs/tlm-util/utilRoutes.cjs +51 -63
- package/dist/cjs/types/index.cjs +2 -5
- package/dist/cjs/utils/logger.cjs +38 -43
- package/dist/cjs/utils/regexUtils.cjs +22 -22
- package/dist/esm/config/bootConfig.js +5 -2
- package/dist/esm/config/config.js +9 -2
- package/dist/esm/docs/openapi.yaml +158 -4
- package/dist/esm/index.js +9 -8
- package/dist/esm/routesManager.js +6 -10
- package/dist/esm/telemetry/custom-implementations/exporters/DiskLogExporter.js +114 -0
- package/dist/esm/telemetry/custom-implementations/exporters/DiskMetricExporter.js +94 -0
- package/dist/esm/telemetry/custom-implementations/exporters/DiskTraceExporter.js +96 -0
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +38 -7
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +107 -48
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +60 -29
- package/dist/esm/telemetry/custom-implementations/exporters/MultiMetricExporter.js +53 -0
- package/dist/esm/telemetry/custom-implementations/instrumentations/logsInstrumentation.js +85 -0
- package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Chunk.js +155 -0
- package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Series.js +164 -0
- package/dist/esm/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.js +385 -0
- package/dist/esm/telemetry/custom-implementations/metrics/tsdb/types.js +1 -0
- package/dist/esm/telemetry/custom-implementations/metrics/tsdb/utils.js +74 -0
- package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.js +2 -1
- package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.js +1 -1
- package/dist/esm/telemetry/custom-implementations/wrappers.js +77 -6
- package/dist/esm/telemetry/initializeTelemetry.js +27 -69
- package/dist/esm/telemetry/persistence/DiskImporter.js +78 -0
- package/dist/esm/telemetry/persistence/DiskUtils.js +51 -0
- package/dist/esm/telemetry/persistence/DiskWriter.js +59 -0
- package/dist/esm/telemetry/telemetryConfigurator.js +110 -39
- package/dist/esm/telemetry/telemetryRegistry.js +12 -1
- package/dist/esm/tlm-ai/agent.js +5 -3
- package/dist/esm/tlm-ai/aiController.js +3 -3
- package/dist/esm/tlm-ai/aiService.js +6 -2
- package/dist/esm/tlm-ai/tools.js +5 -9
- package/dist/esm/tlm-auth/authController.js +3 -2
- package/dist/esm/tlm-log/logController.js +62 -18
- package/dist/esm/tlm-log/logRoutes.js +3 -1
- package/dist/esm/tlm-log/logService.js +25 -0
- package/dist/esm/tlm-metric/metricsController.js +116 -50
- package/dist/esm/tlm-metric/metricsRoutes.js +8 -3
- package/dist/esm/tlm-metric/metricsService.js +22 -0
- package/dist/esm/tlm-plugin/pluginController.js +6 -11
- package/dist/esm/tlm-plugin/pluginService.js +2 -4
- package/dist/esm/tlm-trace/traceController.js +87 -36
- package/dist/esm/tlm-trace/traceRoutes.js +3 -1
- package/dist/esm/tlm-trace/traceService.js +25 -0
- package/dist/esm/tlm-ui/uiRoutes.js +5 -5
- package/dist/esm/tlm-util/utilController.js +3 -9
- package/dist/esm/tlm-util/utilRoutes.js +2 -2
- package/dist/types/config/bootConfig.d.ts +3 -0
- package/dist/types/config/config.d.ts +48 -7
- package/dist/types/config/config.types.d.ts +7 -0
- package/dist/types/index.d.ts +2 -3
- package/dist/types/telemetry/custom-implementations/exporters/DiskLogExporter.d.ts +24 -0
- package/dist/types/telemetry/custom-implementations/exporters/DiskMetricExporter.d.ts +23 -0
- package/dist/types/telemetry/custom-implementations/exporters/DiskTraceExporter.d.ts +23 -0
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +3 -1
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +56 -15
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +8 -4
- package/dist/types/telemetry/custom-implementations/exporters/MultiMetricExporter.d.ts +9 -0
- package/dist/types/telemetry/custom-implementations/instrumentations/logsInstrumentation.d.ts +23 -0
- package/dist/types/telemetry/custom-implementations/metrics/tsdb/Chunk.d.ts +49 -0
- package/dist/types/telemetry/custom-implementations/metrics/tsdb/Series.d.ts +67 -0
- package/dist/types/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.d.ts +69 -0
- package/dist/types/telemetry/custom-implementations/metrics/tsdb/types.d.ts +68 -0
- package/dist/types/telemetry/custom-implementations/metrics/tsdb/utils.d.ts +21 -0
- package/dist/types/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.d.ts +2 -2
- package/dist/types/telemetry/custom-implementations/wrappers.d.ts +2 -1
- package/dist/types/telemetry/persistence/DiskImporter.d.ts +17 -0
- package/dist/types/telemetry/persistence/DiskUtils.d.ts +11 -0
- package/dist/types/telemetry/persistence/DiskWriter.d.ts +21 -0
- package/dist/types/telemetry/telemetryConfigurator.d.ts +1 -1
- package/dist/types/telemetry/telemetryRegistry.d.ts +8 -0
- package/dist/types/tlm-ai/agent.d.ts +1 -1
- package/dist/types/tlm-ai/aiService.d.ts +1 -1
- package/dist/types/tlm-log/logController.d.ts +2 -0
- package/dist/types/tlm-log/logService.d.ts +4 -0
- package/dist/types/tlm-metric/metricsController.d.ts +11 -2
- package/dist/types/tlm-metric/metricsService.d.ts +6 -0
- package/dist/types/tlm-trace/traceController.d.ts +9 -7
- package/dist/types/tlm-trace/traceService.d.ts +4 -0
- package/dist/types/types/index.d.ts +2 -2
- package/dist/ui/assets/{ApiDocsPage-C_VVPPHa.js → ApiDocsPage-DTCgVbW2.js} +2 -2
- package/dist/ui/assets/CollapsibleCard-lWgfsaAn.js +1 -0
- package/dist/ui/assets/DevToolsPage-DEhf8CBy.js +1 -0
- package/dist/ui/assets/LandingPage-CfEHCDxY.js +6 -0
- package/dist/ui/assets/LogsPage-DFDKRuGH.js +1 -0
- package/dist/ui/assets/{NotFoundPage-B3quk3P1.js → NotFoundPage-DCy0DcV7.js} +1 -1
- package/dist/ui/assets/PluginCreatePage-BawZ5_-h.js +50 -0
- package/dist/ui/assets/PluginPage-D3FmgU7d.js +27 -0
- package/dist/ui/assets/TraceSpansPage-D0_L45Rb.js +6 -0
- package/dist/ui/assets/VirtualizedListPanel-q605n9He.js +16 -0
- package/dist/ui/assets/alert-DBAFshSi.js +1133 -0
- package/dist/ui/assets/badge-DGNBtnxU.js +1 -0
- package/dist/ui/assets/{chevron-down-CPsvsmqj.js → chevron-down-CFEqYzGC.js} +1 -1
- package/dist/ui/assets/{chevron-up-Df9jMo1X.js → chevron-up-lDnFwAJq.js} +1 -1
- package/dist/ui/assets/{circle-alert-DOPQPvU8.js → circle-alert-BpYUuRs7.js} +1 -1
- package/dist/ui/assets/dialog-1dRyI6SC.js +15 -0
- package/dist/ui/assets/index-C7RfU6hR.js +1 -0
- package/dist/ui/assets/index-C9dDYIpd.js +305 -0
- package/dist/ui/assets/index-D6f1KjWV.css +1 -0
- package/dist/ui/assets/info-CuJQWoBU.js +6 -0
- package/dist/ui/assets/{input-Dzvg_ZEZ.js → input-BLXaar0X.js} +1 -1
- package/dist/ui/assets/label-DfAcltsl.js +1 -0
- package/dist/ui/assets/{loader-circle-CrvlRy5o.js → loader-circle-B7oLyPsi.js} +1 -1
- package/dist/ui/assets/{loginPage-qa4V-B70.js → loginPage-DswZvOJ-.js} +1 -1
- package/dist/ui/assets/metrics-page-BhtXrfUW.js +31 -0
- package/dist/ui/assets/metrics-page-D1GxaB_c.css +1 -0
- package/dist/ui/assets/popover-IDker85U.js +11 -0
- package/dist/ui/assets/select-B8y5IidE.js +6 -0
- package/dist/ui/assets/separator-B6EzrxYY.js +6 -0
- package/dist/ui/assets/severityOptions-DtCsaAZK.js +11 -0
- package/dist/ui/assets/square-pen-D_oecB1x.js +6 -0
- package/dist/ui/assets/switch-Dqo0XkRD.js +1 -0
- package/dist/ui/assets/trace-DJq1miYa.js +1 -0
- package/dist/ui/assets/upload-prIohEdY.js +11 -0
- package/dist/ui/assets/{utilService-DNyqzwj0.js → utilService-C8TJKLqs.js} +1 -1
- package/dist/ui/assets/wand-sparkles-OgXuzsSx.js +6 -0
- package/dist/ui/index.html +2 -2
- package/package.json +44 -49
- package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +0 -1
- package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +0 -1
- package/dist/ui/assets/LandingPage-CppFBA6K.js +0 -6
- package/dist/ui/assets/LogsPage-9Fq8GArS.js +0 -26
- package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +0 -50
- package/dist/ui/assets/PluginPage-DMDSihrZ.js +0 -27
- package/dist/ui/assets/alert-jQ9HCPIf.js +0 -1133
- package/dist/ui/assets/badge-CNq0-mH5.js +0 -1
- package/dist/ui/assets/card-DFAwwhN3.js +0 -1
- package/dist/ui/assets/index-BkD6DijD.js +0 -15
- package/dist/ui/assets/index-CERGVYZK.js +0 -292
- package/dist/ui/assets/index-CSIPf9qw.css +0 -1
- package/dist/ui/assets/label-DuVnkZ4q.js +0 -1
- package/dist/ui/assets/select-DhS8YUtJ.js +0 -1
- package/dist/ui/assets/separator-isK4chBP.js +0 -6
- package/dist/ui/assets/severityOptions-O38dSOfk.js +0 -11
- package/dist/ui/assets/switch-Z3mImG9n.js +0 -1
- package/dist/ui/assets/tabs-_77MUUQe.js +0 -16
- package/dist/ui/assets/upload-C1LT4Gkb.js +0 -16
|
@@ -7,9 +7,19 @@ import { Enabler } from '../wrappers.js';
|
|
|
7
7
|
import logger from '../../../utils/logger.js';
|
|
8
8
|
import { pluginService } from '../../../tlm-plugin/pluginService.js';
|
|
9
9
|
export class InMemoryDbLogExporter extends Enabler {
|
|
10
|
+
_db = null;
|
|
11
|
+
_miniSearch = null;
|
|
12
|
+
_retentionTimeInSeconds;
|
|
13
|
+
_initialized = false;
|
|
10
14
|
constructor(retentionTimeInSeconds = 3600) {
|
|
11
15
|
super();
|
|
12
16
|
this._retentionTimeInSeconds = retentionTimeInSeconds;
|
|
17
|
+
this._startCleanupJob();
|
|
18
|
+
}
|
|
19
|
+
_ensureInitialized() {
|
|
20
|
+
if (this._initialized)
|
|
21
|
+
return;
|
|
22
|
+
this._initialized = true;
|
|
13
23
|
this._db = new Datastore({ timestampData: true });
|
|
14
24
|
this._db.ensureIndex({ fieldName: 'createdAt' });
|
|
15
25
|
this._miniSearch = new MiniSearch({
|
|
@@ -17,7 +27,7 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
17
27
|
storeFields: ['_id'],
|
|
18
28
|
idField: '_id',
|
|
19
29
|
});
|
|
20
|
-
|
|
30
|
+
logger.info(`[LogExporter] In-memory storage created`);
|
|
21
31
|
}
|
|
22
32
|
/*
|
|
23
33
|
* SUPER WARNING:
|
|
@@ -31,6 +41,7 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
31
41
|
* @param resultCallback
|
|
32
42
|
*/
|
|
33
43
|
export(logs, resultCallback) {
|
|
44
|
+
this._ensureInitialized();
|
|
34
45
|
const logsToInsert = logs.map(logRecord => {
|
|
35
46
|
// Remove circular references first, then apply nesting, then export info
|
|
36
47
|
const formattedLog = this._formatLogRecord(logRecord);
|
|
@@ -48,7 +59,17 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
48
59
|
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
49
60
|
}
|
|
50
61
|
reset() {
|
|
51
|
-
this.
|
|
62
|
+
this._ensureInitialized();
|
|
63
|
+
// Remove all logs from the in-memory database.
|
|
64
|
+
this._db.remove({}, { multi: true }, (err) => {
|
|
65
|
+
if (err) {
|
|
66
|
+
logger.error(`[LogExporter] Error during reset: ${err.message}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
logger.info(`[LogExporter] Reset - all logs cleared`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// Clear mini search index
|
|
52
73
|
this._miniSearch = new MiniSearch({
|
|
53
74
|
fields: ['body'],
|
|
54
75
|
storeFields: ['_id'],
|
|
@@ -61,8 +82,10 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
61
82
|
async shutdown() {
|
|
62
83
|
this._db = null;
|
|
63
84
|
this._miniSearch = null;
|
|
85
|
+
this._initialized = false;
|
|
64
86
|
}
|
|
65
87
|
async find(findConfig) {
|
|
88
|
+
this._ensureInitialized();
|
|
66
89
|
const { query, messageSearch, limit, sortOrder } = findConfig;
|
|
67
90
|
const finalQuery = { ...query };
|
|
68
91
|
const effectiveSortOrder = sortOrder || { timestamp: -1 };
|
|
@@ -73,10 +96,13 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
73
96
|
finalQuery._id = { $in: ids };
|
|
74
97
|
}
|
|
75
98
|
const docs = await new Promise((resolve, reject) => {
|
|
76
|
-
this._db.find(finalQuery)
|
|
77
|
-
.sort(effectiveSortOrder)
|
|
78
|
-
|
|
79
|
-
|
|
99
|
+
let query_exec = this._db.find(finalQuery)
|
|
100
|
+
.sort(effectiveSortOrder);
|
|
101
|
+
// Only apply limit if provided
|
|
102
|
+
if (limit !== undefined) {
|
|
103
|
+
query_exec = query_exec.limit(limit);
|
|
104
|
+
}
|
|
105
|
+
query_exec.exec((err, docs) => {
|
|
80
106
|
if (err)
|
|
81
107
|
reject(err);
|
|
82
108
|
else
|
|
@@ -103,6 +129,7 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
103
129
|
});
|
|
104
130
|
}
|
|
105
131
|
getFinishedLogs() {
|
|
132
|
+
this._ensureInitialized();
|
|
106
133
|
return this._db.getAllData();
|
|
107
134
|
}
|
|
108
135
|
/**
|
|
@@ -136,6 +163,8 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
136
163
|
return this._retentionTimeInSeconds;
|
|
137
164
|
}
|
|
138
165
|
_insertLogs(logsToInsert, resultCallback) {
|
|
166
|
+
if (!this._db)
|
|
167
|
+
return resultCallback({ code: ExportResultCode.FAILED });
|
|
139
168
|
this._db.insert(logsToInsert, (err, newDocs) => {
|
|
140
169
|
if (err) {
|
|
141
170
|
console.dir(err);
|
|
@@ -143,7 +172,7 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
143
172
|
return;
|
|
144
173
|
}
|
|
145
174
|
// console.dir(newDocs, { depth: 3 });
|
|
146
|
-
newDocs.forEach((doc) => this._miniSearch
|
|
175
|
+
newDocs.forEach((doc) => this._miniSearch?.add(doc));
|
|
147
176
|
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
148
177
|
});
|
|
149
178
|
return;
|
|
@@ -151,6 +180,8 @@ export class InMemoryDbLogExporter extends Enabler {
|
|
|
151
180
|
_startCleanupJob() {
|
|
152
181
|
const interval = 1000;
|
|
153
182
|
setInterval(() => {
|
|
183
|
+
if (!this._db)
|
|
184
|
+
return; // Safety check
|
|
154
185
|
const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
|
|
155
186
|
this._db.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
|
|
156
187
|
if (err) {
|
|
@@ -1,32 +1,55 @@
|
|
|
1
1
|
import { ExportResultCode } from '@opentelemetry/core';
|
|
2
|
-
import dataStore from '@seald-io/nedb';
|
|
3
|
-
import { applyNesting } from '../utils/circular.js';
|
|
4
2
|
import { Enabler } from '../wrappers.js';
|
|
5
3
|
import logger from '../../../utils/logger.js';
|
|
6
4
|
import { pluginService } from '../../../tlm-plugin/pluginService.js';
|
|
5
|
+
import { rawToOtel } from '../metrics/tsdb/utils.js';
|
|
6
|
+
import { SeriesRegistry } from '../metrics/tsdb/SeriesRegistry.js';
|
|
7
|
+
/**
|
|
8
|
+
* In-Memory TSDB Metric Exporter
|
|
9
|
+
* Series identified by: scope + metricName + attributes
|
|
10
|
+
*/
|
|
7
11
|
export class InMemoryDbMetricExporter extends Enabler {
|
|
8
|
-
|
|
12
|
+
static DEFAULT_CONFIG = {
|
|
13
|
+
retentionTimeInSeconds: 3600,
|
|
14
|
+
chunkSize: 120,
|
|
15
|
+
maxChunks: 60
|
|
16
|
+
};
|
|
17
|
+
registry;
|
|
18
|
+
config;
|
|
19
|
+
cachedResource = null;
|
|
20
|
+
_initialized = false;
|
|
21
|
+
_ensureInitialized() {
|
|
22
|
+
if (this._initialized)
|
|
23
|
+
return;
|
|
24
|
+
this._initialized = true;
|
|
25
|
+
logger.info(`[MetricExporter] In-memory storage created`);
|
|
26
|
+
}
|
|
27
|
+
get rawDataDB() {
|
|
28
|
+
// For debug/inspection only - metrics are stored in registry chunks
|
|
29
|
+
const stats = this.registry.getStats();
|
|
30
|
+
return stats.totalSamples > 0 ? [`[${stats.totalSamples} samples in registry]`] : [];
|
|
31
|
+
}
|
|
32
|
+
constructor(config) {
|
|
9
33
|
super();
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
12
|
-
this._metrics.ensureIndex({ fieldName: 'createdAt' });
|
|
34
|
+
this.config = { ...InMemoryDbMetricExporter.DEFAULT_CONFIG, ...config };
|
|
35
|
+
this.registry = new SeriesRegistry(this.config.chunkSize, this.config.maxChunks);
|
|
13
36
|
this._startCleanupJob();
|
|
14
37
|
}
|
|
15
|
-
export(
|
|
38
|
+
export(resourceMetrics, resultCallback) {
|
|
39
|
+
this._ensureInitialized();
|
|
16
40
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
41
|
+
// Cache resource (unique per exporter instance)
|
|
42
|
+
if (!this.cachedResource) {
|
|
43
|
+
this.cachedResource = resourceMetrics.resource;
|
|
44
|
+
}
|
|
45
|
+
const scopeMetrics = resourceMetrics.scopeMetrics;
|
|
46
|
+
// Broadcast to plugins
|
|
47
|
+
scopeMetrics?.forEach((metric) => {
|
|
20
48
|
pluginService.broadcastMetric(metric);
|
|
21
49
|
});
|
|
22
|
-
//
|
|
23
|
-
if (this.isEnabled()) {
|
|
24
|
-
this.
|
|
25
|
-
if (err) {
|
|
26
|
-
logger.error('Insertion Error:', err);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
50
|
+
// Store if enabled - use new storeScopeMetrics method
|
|
51
|
+
if (this.isEnabled() && scopeMetrics) {
|
|
52
|
+
this.registry.storeScopeMetrics(scopeMetrics);
|
|
30
53
|
}
|
|
31
54
|
setTimeout(() => resultCallback({ code: ExportResultCode.SUCCESS }), 0);
|
|
32
55
|
}
|
|
@@ -40,48 +63,84 @@ export class InMemoryDbMetricExporter extends Enabler {
|
|
|
40
63
|
}
|
|
41
64
|
shutdown() {
|
|
42
65
|
this._enabled = false;
|
|
43
|
-
this.
|
|
66
|
+
this.registry.reset();
|
|
44
67
|
return this.forceFlush();
|
|
45
68
|
}
|
|
69
|
+
reset() {
|
|
70
|
+
this._ensureInitialized();
|
|
71
|
+
this.registry.reset();
|
|
72
|
+
logger.info(`[MetricExporter] Reset - all metrics cleared`);
|
|
73
|
+
}
|
|
46
74
|
forceFlush() {
|
|
47
75
|
return Promise.resolve();
|
|
48
76
|
}
|
|
49
|
-
|
|
50
|
-
this.
|
|
77
|
+
getCachedResource() {
|
|
78
|
+
return this.cachedResource;
|
|
51
79
|
}
|
|
52
|
-
|
|
53
|
-
this.
|
|
80
|
+
set retentionTimeInSeconds(value) {
|
|
81
|
+
this.config.retentionTimeInSeconds = value;
|
|
82
|
+
logger.info(`Retention time set to ${value} seconds`);
|
|
83
|
+
}
|
|
84
|
+
get retentionTimeInSeconds() {
|
|
85
|
+
return this.config.retentionTimeInSeconds;
|
|
86
|
+
}
|
|
87
|
+
getStats() {
|
|
88
|
+
this._ensureInitialized();
|
|
89
|
+
return this.registry.getStats();
|
|
90
|
+
}
|
|
91
|
+
_startCleanupJob() {
|
|
92
|
+
setInterval(() => {
|
|
93
|
+
const retentionTimeNs = this.config.retentionTimeInSeconds * 1_000_000_000;
|
|
94
|
+
const result = this.registry.evictOldData(retentionTimeNs);
|
|
95
|
+
if (result.evictedChunks > 0 || result.evictedSeries > 0) {
|
|
96
|
+
logger.debug(`Cleanup: evicted ${result.evictedChunks} chunks, ${result.evictedSeries} series`);
|
|
97
|
+
}
|
|
98
|
+
}, 5000);
|
|
54
99
|
}
|
|
55
|
-
|
|
56
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Find metrics by scope+metric queries with filters
|
|
102
|
+
* Supports both raw and otel formats
|
|
103
|
+
*/
|
|
104
|
+
find(request) {
|
|
105
|
+
this._ensureInitialized();
|
|
106
|
+
const format = request.format || 'raw';
|
|
107
|
+
// Get raw results from registry using new unified query method
|
|
108
|
+
const rawResults = this.registry.query(request.scopeMetrics, request.from, request.to);
|
|
109
|
+
// Convert format if needed
|
|
110
|
+
if (format === 'otel') {
|
|
111
|
+
return { results: rawToOtel(rawResults) };
|
|
112
|
+
}
|
|
113
|
+
return { results: rawResults };
|
|
57
114
|
}
|
|
58
115
|
/**
|
|
59
|
-
*
|
|
60
|
-
* @param metrics - The metrics to insert.
|
|
61
|
-
* @param callback - The callback to execute after insertion.
|
|
116
|
+
* Export all metrics as line-delimited JSON (one chunk per line)
|
|
62
117
|
*/
|
|
63
|
-
|
|
64
|
-
this.
|
|
118
|
+
exportToLineDelimitedJson() {
|
|
119
|
+
return this.registry.serializeToLineDelimitedJson();
|
|
65
120
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Import metrics from line-delimited JSON format
|
|
123
|
+
*/
|
|
124
|
+
importFromLineDelimitedJson(lineDelimitedJsonData) {
|
|
125
|
+
this.registry.deserializeFromLineDelimitedJson(lineDelimitedJsonData);
|
|
69
126
|
}
|
|
70
|
-
|
|
71
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Insert metrics in OpenTelemetry (OTEL) format directly into the registry
|
|
129
|
+
* @param scopeMetrics Array of ScopeMetrics (OTEL format)
|
|
130
|
+
*/
|
|
131
|
+
insertOtel(scopeMetrics) {
|
|
132
|
+
this._ensureInitialized();
|
|
133
|
+
// Store only in registry (chunks) - no duplication
|
|
134
|
+
if (this.isEnabled()) {
|
|
135
|
+
this.registry.storeScopeMetrics(scopeMetrics);
|
|
136
|
+
}
|
|
72
137
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
else if (numRemoved > 0) {
|
|
82
|
-
logger.debug(`TTL cleanup: removed ${numRemoved} expired metrics`);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}, interval);
|
|
138
|
+
/**
|
|
139
|
+
* Insert metrics in raw format (MetricQueryResult[]), converts to OTEL and delegates to insertOtel
|
|
140
|
+
* @param rawScopeMetrics Array of MetricQueryResult (raw format)
|
|
141
|
+
*/
|
|
142
|
+
insertRaw(rawScopeMetrics) {
|
|
143
|
+
const otelScopeMetrics = rawToOtel(rawScopeMetrics);
|
|
144
|
+
this.insertOtel(otelScopeMetrics);
|
|
86
145
|
}
|
|
87
146
|
}
|
|
@@ -5,17 +5,22 @@ import { applyNesting, removeCircularRefs } from '../utils/circular.js';
|
|
|
5
5
|
import { Enabler } from '../wrappers.js';
|
|
6
6
|
import { pluginService } from '../../../tlm-plugin/pluginService.js';
|
|
7
7
|
export class InMemoryDbSpanExporter extends Enabler {
|
|
8
|
+
_spans = null;
|
|
9
|
+
_retentionTimeInSeconds;
|
|
10
|
+
_initialized = false;
|
|
8
11
|
constructor(retentionTimeInSeconds = 3600) {
|
|
9
12
|
super();
|
|
10
|
-
this._baseUrl = '/telemetry'; // Default base URL, can be overridden by the config
|
|
11
13
|
this._retentionTimeInSeconds = retentionTimeInSeconds;
|
|
12
|
-
this._spans = new dataStore({ timestampData: true });
|
|
13
|
-
this._spans.ensureIndex({ fieldName: 'createdAt' });
|
|
14
14
|
this._startCleanupJob();
|
|
15
15
|
}
|
|
16
16
|
;
|
|
17
|
-
|
|
18
|
-
this.
|
|
17
|
+
_ensureInitialized() {
|
|
18
|
+
if (this._initialized)
|
|
19
|
+
return;
|
|
20
|
+
this._initialized = true;
|
|
21
|
+
this._spans = new dataStore({ timestampData: true });
|
|
22
|
+
this._spans.ensureIndex({ fieldName: 'createdAt' });
|
|
23
|
+
logger.info(`[SpanExporter] In-memory storage created`);
|
|
19
24
|
}
|
|
20
25
|
set retentionTimeInSeconds(retentionTimeInSeconds) {
|
|
21
26
|
this._retentionTimeInSeconds = retentionTimeInSeconds;
|
|
@@ -25,32 +30,26 @@ export class InMemoryDbSpanExporter extends Enabler {
|
|
|
25
30
|
return this._retentionTimeInSeconds;
|
|
26
31
|
}
|
|
27
32
|
export(readableSpans, resultCallback) {
|
|
33
|
+
this._ensureInitialized();
|
|
28
34
|
logger.debug('InMemoryDbSpanExporter.export called with spans: ', readableSpans.length);
|
|
29
35
|
try {
|
|
30
36
|
// Prepare spans to be inserted into the in-memory database (remove circular references and convert to nested objects)
|
|
31
37
|
const cleanSpans = readableSpans
|
|
32
38
|
.map(nestedSpan => removeCircularRefs(nestedSpan)) // to avoid JSON parsing error
|
|
33
|
-
.map(span => applyNesting(span)) // to avoid dot notation in keys (neDB does not support dot notation in keys)
|
|
34
|
-
.filter(span => {
|
|
35
|
-
const target = span?.attributes?.http?.target;
|
|
36
|
-
// Exclude spans where target includes 'telemetry' but NOT 'telemetry/utils/generate-log' or 'telemetry/utils/generate-wait'
|
|
37
|
-
if (target && target.includes(this._baseUrl)) {
|
|
38
|
-
return (target.includes("generate"));
|
|
39
|
-
}
|
|
40
|
-
return true;
|
|
41
|
-
});
|
|
39
|
+
.map(span => applyNesting(span)); // to avoid dot notation in keys (neDB does not support dot notation in keys)
|
|
42
40
|
cleanSpans.forEach(span => {
|
|
43
41
|
pluginService.broadcastTrace(span);
|
|
44
42
|
});
|
|
45
|
-
//
|
|
46
43
|
if (this.isEnabled()) {
|
|
47
44
|
// Insert spans into the in-memory database
|
|
48
|
-
this._spans
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
if (this._spans) {
|
|
46
|
+
this._spans.insert(cleanSpans, (err, _newDoc) => {
|
|
47
|
+
if (err) {
|
|
48
|
+
logger.error(err);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
54
53
|
}
|
|
55
54
|
return resultCallback({ code: ExportResultCode.SUCCESS });
|
|
56
55
|
}
|
|
@@ -64,10 +63,21 @@ export class InMemoryDbSpanExporter extends Enabler {
|
|
|
64
63
|
}
|
|
65
64
|
;
|
|
66
65
|
shutdown() {
|
|
67
|
-
this._spans =
|
|
66
|
+
this._spans = null;
|
|
68
67
|
return this.forceFlush();
|
|
69
68
|
}
|
|
70
69
|
;
|
|
70
|
+
reset() {
|
|
71
|
+
this._ensureInitialized();
|
|
72
|
+
this._spans.remove({}, { multi: true }, (err) => {
|
|
73
|
+
if (err) {
|
|
74
|
+
logger.error(`[SpanExporter] Error during reset: ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
logger.info(`[SpanExporter] Reset - all spans cleared`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
71
81
|
/**
|
|
72
82
|
* Exports any pending spans in the exporter
|
|
73
83
|
*/
|
|
@@ -75,15 +85,30 @@ export class InMemoryDbSpanExporter extends Enabler {
|
|
|
75
85
|
return Promise.resolve();
|
|
76
86
|
}
|
|
77
87
|
;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
async find(findConfig) {
|
|
89
|
+
this._ensureInitialized();
|
|
90
|
+
const { query, limit, sortOrder } = findConfig;
|
|
91
|
+
const effectiveSortOrder = sortOrder || { timestamp: -1 };
|
|
92
|
+
const docs = await new Promise((resolve, reject) => {
|
|
93
|
+
let query_exec = this._spans.find(query)
|
|
94
|
+
.sort(effectiveSortOrder);
|
|
95
|
+
// Only apply limit if provided
|
|
96
|
+
if (limit !== undefined) {
|
|
97
|
+
query_exec = query_exec.limit(limit);
|
|
98
|
+
}
|
|
99
|
+
query_exec.exec((err, docs) => {
|
|
100
|
+
if (err)
|
|
101
|
+
reject(err);
|
|
102
|
+
else
|
|
103
|
+
resolve(docs);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return docs;
|
|
84
107
|
}
|
|
85
|
-
;
|
|
86
108
|
getFinishedSpans() {
|
|
109
|
+
this._ensureInitialized();
|
|
110
|
+
if (!this._spans)
|
|
111
|
+
return [];
|
|
87
112
|
return this._spans.getAllData();
|
|
88
113
|
}
|
|
89
114
|
;
|
|
@@ -93,11 +118,17 @@ export class InMemoryDbSpanExporter extends Enabler {
|
|
|
93
118
|
* @param callback - The callback to execute after insertion.
|
|
94
119
|
*/
|
|
95
120
|
insert(spans, callback) {
|
|
121
|
+
this._ensureInitialized();
|
|
122
|
+
if (!this._spans) {
|
|
123
|
+
return callback(new Error('Spans database not initialized'), []);
|
|
124
|
+
}
|
|
96
125
|
this._spans.insert(spans, callback);
|
|
97
126
|
}
|
|
98
127
|
_startCleanupJob() {
|
|
99
128
|
const interval = 1000;
|
|
100
129
|
setInterval(() => {
|
|
130
|
+
if (!this._spans)
|
|
131
|
+
return; // Safety check - not initialized yet
|
|
101
132
|
const expirationDate = new Date(Date.now() - this._retentionTimeInSeconds * 1000);
|
|
102
133
|
this._spans.remove({ createdAt: { $lt: expirationDate } }, { multi: true }, (err, numRemoved) => {
|
|
103
134
|
if (err) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ExportResultCode } from '@opentelemetry/core';
|
|
2
|
+
export class MultiMetricExporter {
|
|
3
|
+
exporters;
|
|
4
|
+
constructor(exporters) {
|
|
5
|
+
this.exporters = exporters;
|
|
6
|
+
}
|
|
7
|
+
export(resourceMetrics, resultCallback) {
|
|
8
|
+
if (!this.exporters.length) {
|
|
9
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let pending = this.exporters.length;
|
|
13
|
+
let failed = false;
|
|
14
|
+
let firstError;
|
|
15
|
+
this.exporters.forEach((exporter) => {
|
|
16
|
+
try {
|
|
17
|
+
exporter.export(resourceMetrics, (result) => {
|
|
18
|
+
if (result.code === ExportResultCode.FAILED) {
|
|
19
|
+
failed = true;
|
|
20
|
+
if (result.error && !firstError) {
|
|
21
|
+
firstError = result.error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
pending -= 1;
|
|
25
|
+
if (pending === 0) {
|
|
26
|
+
resultCallback(failed
|
|
27
|
+
? { code: ExportResultCode.FAILED, error: firstError }
|
|
28
|
+
: { code: ExportResultCode.SUCCESS });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
failed = true;
|
|
34
|
+
if (!firstError) {
|
|
35
|
+
firstError = error instanceof Error ? error : new Error(String(error));
|
|
36
|
+
}
|
|
37
|
+
pending -= 1;
|
|
38
|
+
if (pending === 0) {
|
|
39
|
+
resultCallback({ code: ExportResultCode.FAILED, error: firstError });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async shutdown() {
|
|
45
|
+
await Promise.all(this.exporters.map((exporter) => exporter.shutdown()));
|
|
46
|
+
}
|
|
47
|
+
async forceFlush() {
|
|
48
|
+
await Promise.all(this.exporters
|
|
49
|
+
.map((exporter) => exporter.forceFlush)
|
|
50
|
+
.filter((forceFlush) => typeof forceFlush === 'function')
|
|
51
|
+
.map((forceFlush) => forceFlush()));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { InstrumentationBase } from '@opentelemetry/instrumentation';
|
|
2
|
+
import { SeverityNumber } from '@opentelemetry/api-logs';
|
|
3
|
+
import util from 'util';
|
|
4
|
+
import { originalConsoleMethods } from '../../telemetryRegistry.js';
|
|
5
|
+
export class LogsInstrumentation extends InstrumentationBase {
|
|
6
|
+
_loggerProvider;
|
|
7
|
+
_otelLogger;
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
super('@oas-telemetry/logs-instrumentation', '1.0.0', config);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* No-op: this instrumentation does not patch modules loaded via require().
|
|
13
|
+
*/
|
|
14
|
+
init() {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Called by the SDK when the LoggerProvider is available.
|
|
19
|
+
* This is the correct way for an instrumentation to receive a logger provider.
|
|
20
|
+
*/
|
|
21
|
+
setLoggerProvider(provider) {
|
|
22
|
+
this._loggerProvider = provider;
|
|
23
|
+
this._otelLogger = provider.getLogger(this.instrumentationName);
|
|
24
|
+
// If already enabled, re-apply patches with the new logger provider.
|
|
25
|
+
if (this.isEnabled()) {
|
|
26
|
+
this._unpatchConsole();
|
|
27
|
+
this._patchConsole();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Called by the SDK after all providers have been registered.
|
|
32
|
+
*/
|
|
33
|
+
enable() {
|
|
34
|
+
super.enable();
|
|
35
|
+
// Fallback: if no logger provider has been set yet, we do nothing.
|
|
36
|
+
if (!this._loggerProvider) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this._patchConsole();
|
|
40
|
+
}
|
|
41
|
+
disable() {
|
|
42
|
+
super.disable();
|
|
43
|
+
this._unpatchConsole();
|
|
44
|
+
}
|
|
45
|
+
_patchConsole() {
|
|
46
|
+
if (!this._otelLogger)
|
|
47
|
+
return;
|
|
48
|
+
Object.keys(originalConsoleMethods).forEach((method) => {
|
|
49
|
+
const original = originalConsoleMethods[method];
|
|
50
|
+
console[method] = (...args) => {
|
|
51
|
+
const { number, text } = getSeverityForMethod(method);
|
|
52
|
+
this._otelLogger.emit({
|
|
53
|
+
severityNumber: number,
|
|
54
|
+
severityText: text,
|
|
55
|
+
body: util.format(...args),
|
|
56
|
+
attributes: {
|
|
57
|
+
source: `console.${method}`,
|
|
58
|
+
library: this.instrumentationName,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
original(...args);
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
_unpatchConsole() {
|
|
66
|
+
Object.keys(originalConsoleMethods).forEach((method) => {
|
|
67
|
+
console[method] = originalConsoleMethods[method];
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getSeverityForMethod(method) {
|
|
72
|
+
switch (method) {
|
|
73
|
+
case "log":
|
|
74
|
+
case "info":
|
|
75
|
+
return { number: SeverityNumber.INFO, text: "INFO" };
|
|
76
|
+
case "debug":
|
|
77
|
+
return { number: SeverityNumber.DEBUG, text: "DEBUG" };
|
|
78
|
+
case "warn":
|
|
79
|
+
return { number: SeverityNumber.WARN, text: "WARN" };
|
|
80
|
+
case "error":
|
|
81
|
+
return { number: SeverityNumber.ERROR, text: "ERROR" };
|
|
82
|
+
default:
|
|
83
|
+
return { number: SeverityNumber.INFO, text: "INFO" };
|
|
84
|
+
}
|
|
85
|
+
}
|