@oas-tools/oas-telemetry 0.7.0-alpha.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +35 -17
- package/dist/cjs/config/bootConfig.cjs +3 -1
- package/dist/cjs/docs/openapi.yaml +1399 -0
- package/dist/cjs/routesManager.cjs +1 -1
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +43 -13
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +10 -2
- package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +21 -16
- package/dist/cjs/telemetry/initializeTelemetry.cjs +39 -15
- package/dist/cjs/telemetry/telemetryConfigurator.cjs +6 -9
- package/dist/cjs/telemetry/telemetryRegistry.cjs +11 -8
- package/dist/cjs/tlm-ai/agent.cjs +54 -84
- package/dist/cjs/tlm-ai/aiController.cjs +69 -47
- package/dist/cjs/tlm-ai/aiRoutes.cjs +10 -3
- package/dist/cjs/tlm-ai/aiService.cjs +109 -0
- package/dist/cjs/tlm-ai/tools.cjs +30 -268
- package/dist/cjs/tlm-auth/authController.cjs +9 -9
- package/dist/cjs/tlm-auth/authMiddleware.cjs +1 -1
- package/dist/cjs/tlm-log/logController.cjs +30 -36
- package/dist/cjs/tlm-log/logRoutes.cjs +3 -2
- package/dist/cjs/tlm-metric/metricsController.cjs +15 -8
- package/dist/cjs/tlm-metric/metricsRoutes.cjs +2 -1
- package/dist/cjs/tlm-plugin/pluginController.cjs +11 -1
- package/dist/cjs/tlm-plugin/pluginProcess.cjs +4 -2
- package/dist/cjs/tlm-plugin/pluginService.cjs +3 -0
- package/dist/cjs/tlm-trace/traceController.cjs +16 -9
- package/dist/cjs/tlm-trace/traceRoutes.cjs +2 -1
- package/dist/cjs/tlm-util/utilController.cjs +23 -2
- package/dist/cjs/tlm-util/utilRoutes.cjs +44 -5
- package/dist/cjs/utils/logger.cjs +35 -13
- package/dist/esm/config/bootConfig.js +2 -0
- package/dist/esm/docs/openapi.yaml +1399 -0
- package/dist/esm/routesManager.js +1 -1
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +32 -11
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +10 -2
- package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +20 -13
- package/dist/esm/telemetry/initializeTelemetry.js +22 -14
- package/dist/esm/telemetry/telemetryConfigurator.js +7 -10
- package/dist/esm/telemetry/telemetryRegistry.js +10 -7
- package/dist/esm/tlm-ai/agent.js +37 -78
- package/dist/esm/tlm-ai/aiController.js +56 -39
- package/dist/esm/tlm-ai/aiRoutes.js +11 -4
- package/dist/esm/tlm-ai/aiService.js +94 -0
- package/dist/esm/tlm-ai/tools.js +29 -255
- package/dist/esm/tlm-auth/authController.js +8 -8
- package/dist/esm/tlm-auth/authMiddleware.js +1 -1
- package/dist/esm/tlm-log/logController.js +26 -28
- package/dist/esm/tlm-log/logRoutes.js +4 -3
- package/dist/esm/tlm-metric/metricsController.js +10 -6
- package/dist/esm/tlm-metric/metricsRoutes.js +3 -2
- package/dist/esm/tlm-plugin/pluginController.js +2 -1
- package/dist/esm/tlm-plugin/pluginProcess.js +4 -2
- package/dist/esm/tlm-plugin/pluginService.js +4 -0
- package/dist/esm/tlm-trace/traceController.js +11 -7
- package/dist/esm/tlm-trace/traceRoutes.js +3 -2
- package/dist/esm/tlm-util/utilController.js +22 -0
- package/dist/esm/tlm-util/utilRoutes.js +40 -5
- package/dist/esm/utils/logger.js +35 -12
- package/dist/types/config/bootConfig.d.ts +1 -0
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +7 -1
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +1 -0
- package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +1 -0
- package/dist/types/telemetry/telemetryRegistry.d.ts +22 -6
- package/dist/types/tlm-ai/agent.d.ts +2 -2
- package/dist/types/tlm-ai/aiController.d.ts +5 -4
- package/dist/types/tlm-ai/aiRoutes.d.ts +1 -1
- package/dist/types/tlm-ai/aiService.d.ts +38 -0
- package/dist/types/tlm-ai/tools.d.ts +5 -14
- package/dist/types/tlm-log/logController.d.ts +2 -2
- package/dist/types/tlm-metric/metricsController.d.ts +2 -1
- package/dist/types/tlm-plugin/pluginService.d.ts +2 -0
- package/dist/types/tlm-trace/traceController.d.ts +2 -1
- package/dist/types/tlm-util/utilController.d.ts +1 -0
- package/dist/types/utils/logger.d.ts +5 -5
- package/dist/ui/assets/ApiDocsPage-C_VVPPHa.js +16 -0
- package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +1 -0
- package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +1 -0
- package/dist/ui/assets/LandingPage-CppFBA6K.js +6 -0
- package/dist/ui/assets/LogsPage-9Fq8GArS.js +26 -0
- package/dist/ui/assets/NotFoundPage-B3quk3P1.js +1 -0
- package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +50 -0
- package/dist/ui/assets/PluginPage-DMDSihrZ.js +27 -0
- package/dist/ui/assets/alert-jQ9HCPIf.js +1133 -0
- package/dist/ui/assets/badge-CNq0-mH5.js +1 -0
- package/dist/ui/assets/card-DFAwwhN3.js +1 -0
- package/dist/ui/assets/chevron-down-CPsvsmqj.js +6 -0
- package/dist/ui/assets/chevron-up-Df9jMo1X.js +6 -0
- package/dist/ui/assets/circle-alert-DOPQPvU8.js +6 -0
- package/dist/ui/assets/index-BkD6DijD.js +15 -0
- package/dist/ui/assets/index-CERGVYZK.js +292 -0
- package/dist/ui/assets/index-CSIPf9qw.css +1 -0
- package/dist/ui/assets/input-Dzvg_ZEZ.js +1 -0
- package/dist/ui/assets/label-DuVnkZ4q.js +1 -0
- package/dist/ui/assets/loader-circle-CrvlRy5o.js +6 -0
- package/dist/ui/assets/loginPage-qa4V-B70.js +6 -0
- package/dist/ui/assets/select-DhS8YUtJ.js +1 -0
- package/dist/ui/assets/separator-isK4chBP.js +6 -0
- package/dist/ui/assets/severityOptions-O38dSOfk.js +11 -0
- package/dist/ui/assets/switch-Z3mImG9n.js +1 -0
- package/dist/ui/assets/tabs-_77MUUQe.js +16 -0
- package/dist/ui/assets/upload-C1LT4Gkb.js +16 -0
- package/dist/ui/assets/utilService-DNyqzwj0.js +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +17 -6
- package/dist/ui/assets/index-Bgd7fFFH.js +0 -1743
- package/dist/ui/assets/index-Cz3N1n1Q.css +0 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { agent } from './agent.js';
|
|
3
|
+
class AIService {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.conversations = new Map();
|
|
7
|
+
this.openai = new OpenAI({
|
|
8
|
+
apiKey: this.config.apiKey,
|
|
9
|
+
});
|
|
10
|
+
this.model = config.model;
|
|
11
|
+
this.extraPrompts = config.extraPrompts || [];
|
|
12
|
+
}
|
|
13
|
+
createConversation() {
|
|
14
|
+
const id = Math.random().toString(36).substring(2, 15);
|
|
15
|
+
const conversation = { id, messages: [] };
|
|
16
|
+
this.conversations.set(id, conversation);
|
|
17
|
+
return conversation;
|
|
18
|
+
}
|
|
19
|
+
listConversations() {
|
|
20
|
+
return Array.from(this.conversations.values());
|
|
21
|
+
}
|
|
22
|
+
getConversation(id) {
|
|
23
|
+
return this.conversations.get(id);
|
|
24
|
+
}
|
|
25
|
+
deleteConversation(id) {
|
|
26
|
+
return this.conversations.delete(id);
|
|
27
|
+
}
|
|
28
|
+
async sendMessage(conversationId, content, model) {
|
|
29
|
+
const conversation = this.conversations.get(conversationId);
|
|
30
|
+
if (!conversation)
|
|
31
|
+
throw new Error('Conversation not found');
|
|
32
|
+
conversation.messages.push({
|
|
33
|
+
role: 'system',
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
content: "This is a telemetry chat. Focus your replies strictly on the topics and instructions defined by the system prompts and the available tools. Do not answer outside of these boundaries. Reply in the same language as the question."
|
|
36
|
+
});
|
|
37
|
+
if (this.extraPrompts.length > 0) {
|
|
38
|
+
this.extraPrompts.forEach(prompt => {
|
|
39
|
+
conversation.messages.push({ role: 'system', content: prompt, timestamp: new Date().toISOString() });
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
conversation.messages.push({ role: 'user', content, timestamp: new Date().toISOString() });
|
|
43
|
+
// Set conversation name if not set
|
|
44
|
+
if (!('name' in conversation) || !conversation.name) {
|
|
45
|
+
conversation.name = content.slice(0, 30);
|
|
46
|
+
}
|
|
47
|
+
// Use the agent function with tools
|
|
48
|
+
// Internaly pushes new messages to conversation.messages
|
|
49
|
+
const currentMessagesCount = conversation.messages.length;
|
|
50
|
+
await agent(this.openai, conversation.messages, model || this.model, this.extraPrompts);
|
|
51
|
+
// Return the last assistant message (the one just added by agent)
|
|
52
|
+
const generatedMessages = conversation.messages.slice(currentMessagesCount);
|
|
53
|
+
return generatedMessages;
|
|
54
|
+
}
|
|
55
|
+
listConversationsMinimal() {
|
|
56
|
+
return Array.from(this.conversations.values()).map(c => ({
|
|
57
|
+
id: c.id,
|
|
58
|
+
name: c.name,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
async listModels() {
|
|
62
|
+
const { data } = await this.openai.models.list();
|
|
63
|
+
return data
|
|
64
|
+
.map(m => m.id)
|
|
65
|
+
.sort();
|
|
66
|
+
}
|
|
67
|
+
async isValidModel(modelId) {
|
|
68
|
+
try {
|
|
69
|
+
await this.openai.models.retrieve(modelId);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let aiService;
|
|
78
|
+
export function configureAiService(config) {
|
|
79
|
+
const openAIKey = config.ai?.openAIKey;
|
|
80
|
+
const model = config.ai.openAIModel;
|
|
81
|
+
const extraPrompts = config.ai.extraContextPrompts;
|
|
82
|
+
if (!openAIKey)
|
|
83
|
+
throw new Error('OpenAI API key is required');
|
|
84
|
+
aiService = new AIService({
|
|
85
|
+
apiKey: openAIKey,
|
|
86
|
+
model,
|
|
87
|
+
extraPrompts
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function getAiService() {
|
|
91
|
+
if (!aiService)
|
|
92
|
+
throw new Error('AI Service not configured');
|
|
93
|
+
return aiService;
|
|
94
|
+
}
|
package/dist/esm/tlm-ai/tools.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import logger from '../utils/logger.js';
|
|
3
|
-
import { getKnownMicroservices } from './knownMicroservices.js';
|
|
4
2
|
import { inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter } from '../telemetry/telemetryRegistry.js';
|
|
5
3
|
const getTraces = async (searchInput) => {
|
|
6
4
|
logger.debug("getTraces called with searchInput:", searchInput);
|
|
@@ -27,32 +25,22 @@ const getTraces = async (searchInput) => {
|
|
|
27
25
|
const getLogs = async (startDate, endDate) => {
|
|
28
26
|
logger.debug("getLogs called with startDate:", startDate, "endDate:", endDate);
|
|
29
27
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (err) {
|
|
47
|
-
reject(err);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
logs.push(...docs);
|
|
51
|
-
logger.debug(`Found ${logs.length} logs in the specified range.`);
|
|
52
|
-
resolve();
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
});
|
|
28
|
+
// Timestamps are stored in microseconds in the DB (must multiply by 1000)
|
|
29
|
+
const startEpoch = startDate ? new Date(startDate).getTime() * 1000 : 0;
|
|
30
|
+
const endEpoch = endDate ? new Date(endDate).getTime() * 1000 : Date.now() * 1000;
|
|
31
|
+
logger.debug(`Fetching logs from ${startEpoch} to ${endEpoch}`);
|
|
32
|
+
const nedbQuery = {
|
|
33
|
+
timestamp: {
|
|
34
|
+
$gte: startEpoch,
|
|
35
|
+
$lte: endEpoch
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const logs = (await inMemoryDbLogExporter.find({
|
|
39
|
+
query: nedbQuery,
|
|
40
|
+
messageSearch: null,
|
|
41
|
+
limit: 50 // or any appropriate limit
|
|
42
|
+
})) || [];
|
|
43
|
+
logger.debug(`Found ${logs.length} logs in the specified range.`);
|
|
56
44
|
const simplifiedLogs = getSimplifiedLogs(logs);
|
|
57
45
|
return { logs: simplifiedLogs };
|
|
58
46
|
}
|
|
@@ -61,33 +49,6 @@ const getLogs = async (startDate, endDate) => {
|
|
|
61
49
|
throw error;
|
|
62
50
|
}
|
|
63
51
|
};
|
|
64
|
-
const getMetrics = async (searchInput) => {
|
|
65
|
-
logger.debug("getMetrics called with searchInput:", searchInput);
|
|
66
|
-
try {
|
|
67
|
-
const search = searchInput || {};
|
|
68
|
-
const metrics = await new Promise((resolve, reject) => {
|
|
69
|
-
inMemoryDbMetricExporter.find(search, (err, docs) => {
|
|
70
|
-
if (err)
|
|
71
|
-
reject(err);
|
|
72
|
-
else
|
|
73
|
-
resolve(docs || []);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
const simplifiedMetrics = getSimplifiedMetrics(metrics);
|
|
77
|
-
logger.debug(`Searching for metrics with searchInput: ${JSON.stringify(search)}`);
|
|
78
|
-
logger.debug(`Metrics found: ${JSON.stringify(simplifiedMetrics.length)}`);
|
|
79
|
-
return { metrics: simplifiedMetrics };
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
logger.error('Error fetching metrics:', error);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const getCurrentTimestampInEpoch = () => {
|
|
87
|
-
logger.debug("Getting the current timestamp in epoch format...");
|
|
88
|
-
const now = new Date();
|
|
89
|
-
return { currentTimestampInEpoch: now.getTime(), currentTimestampInEpochSeconds: Math.floor(now.getTime() / 1000) };
|
|
90
|
-
};
|
|
91
52
|
const startTelemetry = () => {
|
|
92
53
|
logger.debug("Starting telemetry...");
|
|
93
54
|
inMemoryDbSpanExporter.enable();
|
|
@@ -114,28 +75,10 @@ const getTelemetryStatus = () => {
|
|
|
114
75
|
metricsEnabled: inMemoryDbMetricExporter.isEnabled()
|
|
115
76
|
};
|
|
116
77
|
};
|
|
117
|
-
const
|
|
118
|
-
logger.debug("
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (!identifiedMicroservice) {
|
|
122
|
-
logger.error(`Agent tried to call an unknown microservice: ${microserviceId}, available microservices: ${knownMicroservices.map(m => m.id).join(', ')}`);
|
|
123
|
-
return {
|
|
124
|
-
microservice: microserviceId,
|
|
125
|
-
response: `I cannot help with that. The microservice ${microserviceId} is not recognized. Available microservices are: ${knownMicroservices.map(m => m.id).join(', ')}`
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const microserviceResponse = await axios.post(identifiedMicroservice.url, {
|
|
129
|
-
question: message
|
|
130
|
-
});
|
|
131
|
-
return {
|
|
132
|
-
microservice: microserviceId,
|
|
133
|
-
response: microserviceResponse.data
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
const getMicroserviceAgents = () => {
|
|
137
|
-
logger.debug("Getting microservice agents...");
|
|
138
|
-
return getKnownMicroservices();
|
|
78
|
+
const getCurrentDate = () => {
|
|
79
|
+
logger.debug("Getting the current date in ISO format...");
|
|
80
|
+
const now = new Date();
|
|
81
|
+
return { currentDateISO: now.toISOString() };
|
|
139
82
|
};
|
|
140
83
|
const tools = [
|
|
141
84
|
{
|
|
@@ -227,7 +170,8 @@ const tools = [
|
|
|
227
170
|
description: `Fetches log data for the microservice.
|
|
228
171
|
Logs provide information about system events, including timestamps, log levels (e.g., info, error), and messages.
|
|
229
172
|
The 'startDate' and 'endDate' parameters define the time range for fetching logs.
|
|
230
|
-
If
|
|
173
|
+
If you need a date, you MUST first call the "getCurrentDate" tool to obtain the current date in ISO format, and then use it as a parameter.
|
|
174
|
+
If you don't provide a range, all logs will be fetched. Providing a specific range improves performance.
|
|
231
175
|
Example 'startDate' and 'endDate':
|
|
232
176
|
{
|
|
233
177
|
"startDate": "2023-10-01T00:00:00Z",
|
|
@@ -247,41 +191,6 @@ const tools = [
|
|
|
247
191
|
}
|
|
248
192
|
}
|
|
249
193
|
},
|
|
250
|
-
{
|
|
251
|
-
type: "function",
|
|
252
|
-
function: {
|
|
253
|
-
name: "getMetrics",
|
|
254
|
-
description: `Fetches metrics data for the microservice.
|
|
255
|
-
Metrics provide performance-related data, such as CPU usage, memory usage, and process-specific metrics.
|
|
256
|
-
The 'searchInput' parameter is an object used to filter metrics based on specific criteria.
|
|
257
|
-
This is a NeDB query using MongoDB-like (NeDB) syntax. If 'searchInput' is null, all metrics will be fetched. Providing specific filters improves performance.
|
|
258
|
-
|
|
259
|
-
Example 'searchInput':
|
|
260
|
-
{
|
|
261
|
-
"timestamp": { "$gte": 1747651105757, "$lte": 1747651200935 }
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
Common filters include timestamps.`,
|
|
265
|
-
parameters: {
|
|
266
|
-
type: "object",
|
|
267
|
-
properties: {
|
|
268
|
-
searchInput: {
|
|
269
|
-
type: "object",
|
|
270
|
-
description: `Optional search criteria for filtering metrics.
|
|
271
|
-
This is a NeDB query using MongoDB-like (NeDB) syntax.
|
|
272
|
-
For example, you can filter by timestamps.
|
|
273
|
-
If null, all metrics will be returned.`,
|
|
274
|
-
properties: {
|
|
275
|
-
"timestamp": { type: "object", properties: { "$gte": { type: "integer" }, "$lte": { type: "integer" } } },
|
|
276
|
-
"cpuUsageData.cpuNumber": { type: "string" },
|
|
277
|
-
"memoryData.used": { type: "integer" }
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
required: ["searchInput"]
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
194
|
{
|
|
286
195
|
type: "function",
|
|
287
196
|
function: {
|
|
@@ -321,50 +230,9 @@ const tools = [
|
|
|
321
230
|
{
|
|
322
231
|
type: "function",
|
|
323
232
|
function: {
|
|
324
|
-
name: "
|
|
325
|
-
description: `
|
|
326
|
-
|
|
327
|
-
parameters: {}
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
type: "function",
|
|
332
|
-
function: {
|
|
333
|
-
name: "talkToExternalMicroserviceAgent",
|
|
334
|
-
description: `Use this function to communicate with external microservice agent.
|
|
335
|
-
if you want to talk to a microservice agent, you must provide the message and the microservice you want to talk to.
|
|
336
|
-
When you call this function, it will send the message to the specified microservice and return the response.
|
|
337
|
-
|
|
338
|
-
Example 'message':
|
|
339
|
-
{
|
|
340
|
-
"message": "What is the status of the service?",
|
|
341
|
-
"microservice": "Reporter"
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
Microservices Availables (by ID):
|
|
345
|
-
${getKnownMicroservices().map(m => m.id).join(", ")}
|
|
346
|
-
`,
|
|
347
|
-
parameters: {
|
|
348
|
-
type: "object",
|
|
349
|
-
properties: {
|
|
350
|
-
message: {
|
|
351
|
-
type: "string",
|
|
352
|
-
description: `The message to be sent to the external microservice agent.`
|
|
353
|
-
},
|
|
354
|
-
microservice: {
|
|
355
|
-
type: "string"
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
required: ["message", "microservice"]
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
},
|
|
362
|
-
{
|
|
363
|
-
type: "function",
|
|
364
|
-
function: {
|
|
365
|
-
name: "getMicroserviceAgents",
|
|
366
|
-
description: `Retrieves the list of available microservice agents.
|
|
367
|
-
This function provides information about the microservices that can be communicated with.`,
|
|
233
|
+
name: "getCurrentDate",
|
|
234
|
+
description: `Returns the current date in ISO 8601 format (e.g., "2024-06-07T12:34:56.789Z").
|
|
235
|
+
Use this tool to obtain the current date when you need to specify a date for other tools, such as "getLogs".`,
|
|
368
236
|
parameters: {}
|
|
369
237
|
}
|
|
370
238
|
}
|
|
@@ -372,107 +240,13 @@ const tools = [
|
|
|
372
240
|
const availableTools = {
|
|
373
241
|
getTraces,
|
|
374
242
|
getLogs,
|
|
375
|
-
getMetrics,
|
|
376
243
|
startTelemetry,
|
|
377
244
|
stopTelemetry,
|
|
378
245
|
resetTelemetry,
|
|
379
246
|
getTelemetryStatus,
|
|
380
|
-
|
|
381
|
-
talkToExternalMicroserviceAgent,
|
|
382
|
-
getMicroserviceAgents
|
|
247
|
+
getCurrentDate
|
|
383
248
|
};
|
|
384
249
|
export { tools, availableTools, };
|
|
385
|
-
function getSimplifiedMetrics(metrics) {
|
|
386
|
-
return metrics.map(resourceMetric => {
|
|
387
|
-
const serviceName = resourceMetric.resource?._memoizedAttributes?.service?.name || 'unknown-service';
|
|
388
|
-
const cpuUtilization = {};
|
|
389
|
-
let cpuCount = 0;
|
|
390
|
-
const memoryUsageMB = { used: 0, free: 0 };
|
|
391
|
-
const memoryUtilizationPercent = { used: 0, free: 0 };
|
|
392
|
-
const networkIO = { transmit: 0, receive: 0 };
|
|
393
|
-
const processCPU = {};
|
|
394
|
-
let processMemoryUsage = 0;
|
|
395
|
-
for (const scopeMetric of resourceMetric.scopeMetrics) {
|
|
396
|
-
for (const metric of scopeMetric.metrics) {
|
|
397
|
-
const name = metric.descriptor.name;
|
|
398
|
-
if (name === 'system.cpu.utilization') {
|
|
399
|
-
const stateSums = {};
|
|
400
|
-
const stateCounts = {};
|
|
401
|
-
for (const dp of metric.dataPoints) {
|
|
402
|
-
const state = dp.attributes?.system?.cpu?.state;
|
|
403
|
-
if (!state)
|
|
404
|
-
continue;
|
|
405
|
-
// @ts-expect-error index signature
|
|
406
|
-
stateSums[state] = (stateSums[state] || 0) + dp.value;
|
|
407
|
-
// @ts-expect-error index signature
|
|
408
|
-
stateCounts[state] = (stateCounts[state] || 0) + 1;
|
|
409
|
-
}
|
|
410
|
-
for (const state in stateSums) {
|
|
411
|
-
// @ts-expect-error index signature
|
|
412
|
-
cpuUtilization[state] = stateSums[state] / stateCounts[state];
|
|
413
|
-
}
|
|
414
|
-
cpuCount = Math.max(...metric.dataPoints.map((dp) => parseInt(dp.attributes?.system?.cpu?.logical_number || 0, 10))) + 1;
|
|
415
|
-
}
|
|
416
|
-
if (name === 'system.memory.usage') {
|
|
417
|
-
for (const dp of metric.dataPoints) {
|
|
418
|
-
const state = dp.attributes?.system?.memory?.state;
|
|
419
|
-
// @ts-expect-error index signature
|
|
420
|
-
if (state)
|
|
421
|
-
memoryUsageMB[state] = dp.value;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
if (name === 'system.memory.utilization') {
|
|
425
|
-
for (const dp of metric.dataPoints) {
|
|
426
|
-
const state = dp.attributes?.system?.memory?.state;
|
|
427
|
-
// @ts-expect-error index signature
|
|
428
|
-
if (state)
|
|
429
|
-
memoryUtilizationPercent[state] = dp.value;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
if (name === 'system.network.io') {
|
|
433
|
-
for (const dp of metric.dataPoints) {
|
|
434
|
-
const direction = dp.attributes?.network?.io?.direction;
|
|
435
|
-
// @ts-expect-error index signature
|
|
436
|
-
if (direction)
|
|
437
|
-
networkIO[direction] += dp.value;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
if (name === 'process.cpu.time') {
|
|
441
|
-
for (const dp of metric.dataPoints) {
|
|
442
|
-
const state = dp.attributes?.process?.cpu?.state;
|
|
443
|
-
// @ts-expect-error index signature
|
|
444
|
-
if (state)
|
|
445
|
-
processCPU[state] = dp.value;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (name === 'process.memory.usage') {
|
|
449
|
-
processMemoryUsage = metric.dataPoints[0]?.value || 0;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
454
|
-
service: serviceName,
|
|
455
|
-
cpu: {
|
|
456
|
-
avgUtilization: cpuUtilization,
|
|
457
|
-
cores: cpuCount,
|
|
458
|
-
},
|
|
459
|
-
memory: {
|
|
460
|
-
usedGB: (memoryUsageMB.used || 0) / (1024 ** 3),
|
|
461
|
-
freeGB: (memoryUsageMB.free || 0) / (1024 ** 3),
|
|
462
|
-
usedPercent: (memoryUtilizationPercent.used || 0) * 100,
|
|
463
|
-
freePercent: (1 - (memoryUtilizationPercent.used || 0)) * 100,
|
|
464
|
-
},
|
|
465
|
-
network: {
|
|
466
|
-
transmittedMB: networkIO.transmit / (1024 ** 2),
|
|
467
|
-
receivedMB: networkIO.receive / (1024 ** 2),
|
|
468
|
-
},
|
|
469
|
-
process: {
|
|
470
|
-
cpuTimeSec: processCPU,
|
|
471
|
-
memoryUsageMB: processMemoryUsage / (1024 ** 2),
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
250
|
function getSimplifiedTraces(spans) {
|
|
477
251
|
return spans.map((span) => {
|
|
478
252
|
return {
|
|
@@ -490,10 +264,10 @@ function getSimplifiedTraces(spans) {
|
|
|
490
264
|
function getSimplifiedLogs(logs) {
|
|
491
265
|
return logs.map((log) => ({
|
|
492
266
|
service: log.resource?.attributes?.service?.name || undefined,
|
|
493
|
-
timestamp: log.timestamp,
|
|
267
|
+
timestamp: new Date(log.timestamp / 1000).toISOString(), // converting microseconds to milliseconds
|
|
268
|
+
severityText: log.severityText,
|
|
494
269
|
message: log.body,
|
|
495
270
|
traceId: log.traceId,
|
|
496
|
-
|
|
497
|
-
source: log.attributes?.source?.source || undefined,
|
|
271
|
+
source: log.attributes?.source || undefined,
|
|
498
272
|
}));
|
|
499
273
|
}
|
|
@@ -16,19 +16,19 @@ export const getLogin = (oasTlmConfig) => (req, res) => {
|
|
|
16
16
|
if (password === oasTlmConfig.auth.password) {
|
|
17
17
|
const accessToken = generateAccessToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.accessTokenMaxAge);
|
|
18
18
|
const refreshToken = generateRefreshToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.refreshTokenMaxAge);
|
|
19
|
-
res.cookie("
|
|
19
|
+
res.cookie("oas-tlm-access-token", accessToken, {
|
|
20
20
|
maxAge: oasTlmConfig.auth.accessTokenMaxAge,
|
|
21
21
|
httpOnly: true,
|
|
22
22
|
secure: process.env.NODE_ENV === "production",
|
|
23
23
|
sameSite: "lax",
|
|
24
24
|
path: "/"
|
|
25
25
|
});
|
|
26
|
-
res.cookie("
|
|
26
|
+
res.cookie("oas-tlm-refresh-token", refreshToken, {
|
|
27
27
|
maxAge: oasTlmConfig.auth.refreshTokenMaxAge,
|
|
28
28
|
httpOnly: true,
|
|
29
29
|
secure: process.env.NODE_ENV === "production",
|
|
30
30
|
sameSite: "lax",
|
|
31
|
-
path: oasTlmConfig.general.baseUrl + "/auth/refresh"
|
|
31
|
+
path: oasTlmConfig.general.baseUrl + "/auth/refresh"
|
|
32
32
|
});
|
|
33
33
|
res.status(200).json({ valid: true, message: "Login successful" });
|
|
34
34
|
return;
|
|
@@ -45,8 +45,8 @@ export const getLogout = (oasTlmConfig) => (req, res) => {
|
|
|
45
45
|
res.status(200).json({ valid: true, message: "Auth disabled" });
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
res.clearCookie('
|
|
49
|
-
res.clearCookie('
|
|
48
|
+
res.clearCookie('oas-tlm-access-token', { path: '/' });
|
|
49
|
+
res.clearCookie('oas-tlm-refresh-token', { path: oasTlmConfig.general.baseUrl + '/auth/refresh' });
|
|
50
50
|
res.status(200).json({ valid: true, message: "Logged out" });
|
|
51
51
|
};
|
|
52
52
|
export const getRefresh = (oasTlmConfig) => (req, res) => {
|
|
@@ -54,7 +54,7 @@ export const getRefresh = (oasTlmConfig) => (req, res) => {
|
|
|
54
54
|
res.status(200).json({ valid: true, message: "Auth disabled" });
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
|
-
const refreshToken = req.cookies
|
|
57
|
+
const refreshToken = req.cookies["oas-tlm-refresh-token"];
|
|
58
58
|
if (!refreshToken) {
|
|
59
59
|
res.status(401).json({ valid: false, message: "No refresh token" });
|
|
60
60
|
return;
|
|
@@ -64,7 +64,7 @@ export const getRefresh = (oasTlmConfig) => (req, res) => {
|
|
|
64
64
|
if (payload.type !== "refresh")
|
|
65
65
|
throw new Error("Invalid token type");
|
|
66
66
|
const accessToken = generateAccessToken(oasTlmConfig.auth.jwtSecret, oasTlmConfig.auth.accessTokenMaxAge);
|
|
67
|
-
res.cookie("
|
|
67
|
+
res.cookie("oas-tlm-access-token", accessToken, {
|
|
68
68
|
maxAge: oasTlmConfig.auth.accessTokenMaxAge,
|
|
69
69
|
httpOnly: true,
|
|
70
70
|
secure: process.env.NODE_ENV === "production",
|
|
@@ -73,7 +73,7 @@ export const getRefresh = (oasTlmConfig) => (req, res) => {
|
|
|
73
73
|
});
|
|
74
74
|
res.status(200).json({ valid: true, message: "Refreshed" });
|
|
75
75
|
}
|
|
76
|
-
catch
|
|
76
|
+
catch {
|
|
77
77
|
res.status(401).json({ valid: false, message: "Invalid refresh token" });
|
|
78
78
|
}
|
|
79
79
|
};
|
|
@@ -4,7 +4,7 @@ export function getAuthMiddleware(oasTlmConfig) {
|
|
|
4
4
|
if (!oasTlmConfig.auth.enabled) {
|
|
5
5
|
return next();
|
|
6
6
|
}
|
|
7
|
-
const token = req.cookies
|
|
7
|
+
const token = req.cookies["oas-tlm-access-token"];
|
|
8
8
|
if (!token) {
|
|
9
9
|
res.status(401).json({ valid: false, message: "No access token" });
|
|
10
10
|
return;
|
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { inMemoryDbLogExporter } from '../telemetry/telemetryRegistry.js';
|
|
2
2
|
import logger from '../utils/logger.js';
|
|
3
3
|
import { convertRegexRecursively } from '../utils/regexUtils.js';
|
|
4
|
-
export const listLogs = async (req, res) => {
|
|
5
|
-
try {
|
|
6
|
-
const logs = inMemoryDbLogExporter.getFinishedLogs();
|
|
7
|
-
res.send({ logsCount: logs.length, logs: logs });
|
|
8
|
-
}
|
|
9
|
-
catch (err) {
|
|
10
|
-
logger.error(err);
|
|
11
|
-
res.status(500).send({ error: 'Failed to list log data' });
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
4
|
export const findLogs = async (req, res) => {
|
|
15
|
-
const body = req.body;
|
|
16
|
-
const messageSearch = body
|
|
17
|
-
const findQuery = body
|
|
18
|
-
|
|
5
|
+
const body = req.body || {};
|
|
6
|
+
const messageSearch = body.textSearch || null;
|
|
7
|
+
const findQuery = body.query || {};
|
|
8
|
+
const limit = parseInt(body.limit) || 50;
|
|
9
|
+
const sortOrder = body.sort || null;
|
|
10
|
+
logger.debug(`findLogs called with query: ${JSON.stringify(findQuery)} and search: ${messageSearch}`, { depth: 3 });
|
|
19
11
|
let processedQuery;
|
|
20
12
|
try {
|
|
21
13
|
processedQuery = convertRegexRecursively(findQuery);
|
|
@@ -26,15 +18,17 @@ export const findLogs = async (req, res) => {
|
|
|
26
18
|
return; // Exit if invalid regex was encountered
|
|
27
19
|
}
|
|
28
20
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
// Use findConfig object
|
|
22
|
+
const findConfig = {
|
|
23
|
+
query: processedQuery,
|
|
24
|
+
messageSearch,
|
|
25
|
+
limit,
|
|
26
|
+
sortOrder
|
|
27
|
+
};
|
|
28
|
+
const docs = await inMemoryDbLogExporter.find(findConfig);
|
|
29
|
+
res.send({
|
|
30
|
+
items: docs,
|
|
35
31
|
});
|
|
36
|
-
const typedResults = results;
|
|
37
|
-
res.send({ logsCount: typedResults.length, logs: typedResults });
|
|
38
32
|
}
|
|
39
33
|
catch (err) {
|
|
40
34
|
logger.error(err);
|
|
@@ -49,7 +43,7 @@ export const insertLogsToDb = async (req, res) => {
|
|
|
49
43
|
const jsonContent = req.body.logs;
|
|
50
44
|
const resetData = req.query.reset === 'true';
|
|
51
45
|
if (!Array.isArray(jsonContent)) {
|
|
52
|
-
res.status(400).send({ error: 'Invalid data format.
|
|
46
|
+
res.status(400).send({ error: 'Invalid data format.' });
|
|
53
47
|
return;
|
|
54
48
|
}
|
|
55
49
|
const cleanedLogs = jsonContent.map((log) => {
|
|
@@ -92,12 +86,16 @@ export const statusLogs = (req, res) => {
|
|
|
92
86
|
const isRunning = inMemoryDbLogExporter.isEnabled() || false;
|
|
93
87
|
res.send({ active: isRunning });
|
|
94
88
|
};
|
|
95
|
-
export const
|
|
96
|
-
const
|
|
97
|
-
if (typeof
|
|
89
|
+
export const setLogRetentionTime = (req, res) => {
|
|
90
|
+
const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
|
|
91
|
+
if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
|
|
98
92
|
res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
|
|
99
93
|
return;
|
|
100
94
|
}
|
|
101
|
-
inMemoryDbLogExporter.retentionTimeInSeconds =
|
|
102
|
-
res.send({ message: `Retention time set to ${
|
|
95
|
+
inMemoryDbLogExporter.retentionTimeInSeconds = retentionTimeInSeconds;
|
|
96
|
+
res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
|
|
97
|
+
};
|
|
98
|
+
export const getLogRetentionTime = (req, res) => {
|
|
99
|
+
const retentionTimeInSeconds = inMemoryDbLogExporter.retentionTimeInSeconds || 0;
|
|
100
|
+
res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
|
|
103
101
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { startLogs, stopLogs, statusLogs, resetLogs,
|
|
2
|
+
import { startLogs, stopLogs, statusLogs, resetLogs, findLogs, insertLogsToDb, setLogRetentionTime, getLogRetentionTime } from './logController.js';
|
|
3
3
|
export const getLogRoutes = () => {
|
|
4
4
|
const router = Router();
|
|
5
5
|
// Logs Control
|
|
@@ -7,8 +7,9 @@ export const getLogRoutes = () => {
|
|
|
7
7
|
router.post('/stop', stopLogs);
|
|
8
8
|
router.get('/status', statusLogs);
|
|
9
9
|
router.post('/reset', resetLogs);
|
|
10
|
-
router.post('/retention-time',
|
|
11
|
-
router.get('/',
|
|
10
|
+
router.post('/retention-time', setLogRetentionTime);
|
|
11
|
+
router.get('/retention-time', getLogRetentionTime);
|
|
12
|
+
router.get('/', findLogs);
|
|
12
13
|
router.post('/', insertLogsToDb);
|
|
13
14
|
router.post('/find', findLogs);
|
|
14
15
|
return router;
|
|
@@ -40,7 +40,7 @@ export const insertMetricsToDb = async (req, res) => {
|
|
|
40
40
|
const jsonContent = req.body.metrics;
|
|
41
41
|
const resetData = req.query.reset === 'true';
|
|
42
42
|
if (!Array.isArray(jsonContent)) {
|
|
43
|
-
res.status(400).send({ error: 'Invalid data format.
|
|
43
|
+
res.status(400).send({ error: 'Invalid data format.' });
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
const cleanedMetrics = jsonContent.map((metric) => {
|
|
@@ -83,12 +83,16 @@ export const statusMetrics = (req, res) => {
|
|
|
83
83
|
const isRunning = inMemoryDbMetricExporter.isEnabled() || false;
|
|
84
84
|
res.send({ active: isRunning });
|
|
85
85
|
};
|
|
86
|
-
export const
|
|
87
|
-
const
|
|
88
|
-
if (typeof
|
|
86
|
+
export const setMetricRetentionTime = (req, res) => {
|
|
87
|
+
const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
|
|
88
|
+
if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
|
|
89
89
|
res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
inMemoryDbMetricExporter.retentionTimeInSeconds =
|
|
93
|
-
res.send({ message: `Retention time set to ${
|
|
92
|
+
inMemoryDbMetricExporter.retentionTimeInSeconds = retentionTimeInSeconds;
|
|
93
|
+
res.send({ message: `Retention time set to ${retentionTimeInSeconds} seconds.` });
|
|
94
|
+
};
|
|
95
|
+
export const getMetricRetentionTime = (req, res) => {
|
|
96
|
+
const retentionTimeInSeconds = inMemoryDbMetricExporter.retentionTimeInSeconds || 0;
|
|
97
|
+
res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
|
|
94
98
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics,
|
|
2
|
+
import { listMetrics, findMetrics, resetMetrics, insertMetricsToDb, startMetrics, stopMetrics, statusMetrics, setMetricRetentionTime, getMetricRetentionTime } from './metricsController.js';
|
|
3
3
|
export const getMetricsRoutes = () => {
|
|
4
4
|
const router = Router();
|
|
5
5
|
// Metrics Control
|
|
@@ -7,7 +7,8 @@ export const getMetricsRoutes = () => {
|
|
|
7
7
|
router.post('/stop', stopMetrics);
|
|
8
8
|
router.get('/status', statusMetrics);
|
|
9
9
|
router.post('/reset', resetMetrics);
|
|
10
|
-
router.post('/retention-time',
|
|
10
|
+
router.post('/retention-time', setMetricRetentionTime);
|
|
11
|
+
router.get('/retention-time', getMetricRetentionTime);
|
|
11
12
|
router.get('/', listMetrics);
|
|
12
13
|
router.post('/', insertMetricsToDb);
|
|
13
14
|
router.post('/find', findMetrics);
|