@oas-tools/oas-telemetry 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +200 -200
- package/README.md +133 -133
- package/dist/exporters/InMemoryDbExporter.cjs +72 -12
- package/dist/index.cjs +35 -21
- package/dist/ui.cjs +648 -539
- package/package.json +63 -63
- package/src/exporters/InMemoryDbExporter.js +154 -89
- package/src/index.js +197 -190
- package/src/telemetry.js +25 -25
- package/src/ui.js +887 -777
package/dist/ui.cjs
CHANGED
|
@@ -5,608 +5,685 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = ui;
|
|
7
7
|
function ui() {
|
|
8
|
-
/*
|
|
9
|
-
Sources:
|
|
10
|
-
ui/detail.html
|
|
11
|
-
ui/main.html
|
|
12
|
-
Parsing:
|
|
13
|
-
` --> \`
|
|
14
|
-
$ --> \$
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
8
|
return {
|
|
18
9
|
main: `
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
10
|
+
<!DOCTYPE html>
|
|
11
|
+
<html lang="en">
|
|
12
|
+
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="UTF-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
+
<title>OAS - Telemetry</title>
|
|
17
|
+
<style>
|
|
18
|
+
body {
|
|
19
|
+
font-family: Arial, sans-serif;
|
|
20
|
+
margin: 20px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
table {
|
|
24
|
+
width: 100%;
|
|
25
|
+
border-collapse: collapse;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
th,
|
|
29
|
+
td {
|
|
30
|
+
border: 1px solid #dddddd;
|
|
31
|
+
padding: 8px;
|
|
32
|
+
text-align: left;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
th {
|
|
36
|
+
background-color: #f2f2f2;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#telemetryStatusSpan {
|
|
40
|
+
color: rgb(0, 123, 6);
|
|
41
|
+
font-size: x-small;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
|
|
46
|
+
<body>
|
|
47
|
+
<h1>Telemetry <span id="telemetryStatusSpan"></span></h1>
|
|
48
|
+
|
|
49
|
+
<label><input type="checkbox" id="toggleTelemetry"> Allow Server Telemetry</label>
|
|
50
|
+
<br><br>
|
|
51
|
+
<button onclick="fetch('/telemetry/reset');showTelemetryStatus();">Reset Telemetry Data</button>
|
|
52
|
+
<br><br>
|
|
53
|
+
<label><input type="checkbox" id="autoUpdate"> Allow Client Auto Update</label>
|
|
54
|
+
<br><br>
|
|
55
|
+
<table id="apiTable">
|
|
56
|
+
<thead>
|
|
57
|
+
<tr>
|
|
58
|
+
<th onclick="sortTable(0)">Path</th>
|
|
59
|
+
<th onclick="sortTable(1)">Method</th>
|
|
60
|
+
<th onclick="sortTable(2)">Status</th>
|
|
61
|
+
<th onclick="sortTable(3)">Description</th>
|
|
62
|
+
<th onclick="sortTable(4)" style="text-align: center;">Request <br> Count</th>
|
|
63
|
+
<th onclick="sortTable(5)" style="text-align: center;">Average response time<br> (sec)</th>
|
|
64
|
+
<th style="text-align: center;">Auto<br>Update</th>
|
|
65
|
+
</tr>
|
|
66
|
+
</thead>
|
|
67
|
+
<tbody>
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
let LOG = false;
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
let intervalTimer = {
|
|
76
|
+
disabled: true,
|
|
77
|
+
period: 2000,
|
|
78
|
+
subscribers: [],
|
|
79
|
+
start: function () {
|
|
80
|
+
this.interval = setInterval(() => this.tick(), this.period);
|
|
81
|
+
},
|
|
82
|
+
tick: function () {
|
|
83
|
+
if (this.disabled) return;
|
|
84
|
+
log("tick");
|
|
85
|
+
this.subscribers.forEach(callback => callback());//execute all the callbacks
|
|
86
|
+
},
|
|
87
|
+
subscribe: function (callback) {
|
|
88
|
+
this.subscribers.push(callback);
|
|
89
|
+
},
|
|
90
|
+
unsubscribe: function (callback) {
|
|
91
|
+
this.subscribers.pop(callback)
|
|
46
92
|
}
|
|
47
|
-
</style>
|
|
48
|
-
</head>
|
|
49
|
-
<body>
|
|
50
|
-
<h1>Telemetry <span id="telemetryStatusSpan"></span></h1>
|
|
51
|
-
<table id="apiTable">
|
|
52
|
-
<thead>
|
|
53
|
-
<tr>
|
|
54
|
-
<th onclick="sortTable(0)">Path</th>
|
|
55
|
-
<th onclick="sortTable(1)">Method</th>
|
|
56
|
-
<th onclick="sortTable(2)">Status</th>
|
|
57
|
-
<th onclick="sortTable(3)">Description</th>
|
|
58
|
-
<th onclick="sortTable(4)" style="text-align: center;">Request <br>Count</th>
|
|
59
|
-
<th onclick="sortTable(5)" style="text-align: center;">Average response time<br> (sec)</th>
|
|
60
|
-
|
|
61
|
-
</tr>
|
|
62
|
-
</thead>
|
|
63
|
-
<tbody>
|
|
64
|
-
</tbody>
|
|
65
|
-
</table>
|
|
66
|
-
<br/>
|
|
67
|
-
<button onclick="fetch('/telemetry/start');loadTelemetryStatus();">Start</button>
|
|
68
|
-
<button onclick="fetch('/telemetry/stop');loadTelemetryStatus();">Stop</button>
|
|
69
|
-
<button onclick="fetch('/telemetry/reset');loadTelemetryStatus();">Reset</button>
|
|
70
|
-
<script>
|
|
71
|
-
|
|
72
|
-
let LOG=false;
|
|
73
|
-
|
|
74
|
-
function log(s){
|
|
75
|
-
if(LOG)
|
|
76
|
-
console.log(s);
|
|
77
93
|
}
|
|
78
|
-
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
function log(s) {
|
|
100
|
+
if (LOG) console.log(s);
|
|
101
|
+
}
|
|
102
|
+
|
|
79
103
|
async function fetchSpec() {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function loadTelemetryStatus(){
|
|
94
|
-
log("TEST");
|
|
95
|
-
const tss = document.getElementById("telemetryStatusSpan");
|
|
96
|
-
const response = await fetch("/telemetry/status");
|
|
97
|
-
if (!response.ok) {
|
|
98
|
-
throw new Error("ERROR getting the Status");
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
tStatus = await response.json();
|
|
102
|
-
|
|
103
|
-
log(tStatus);
|
|
104
|
-
if(tStatus.active){
|
|
105
|
-
tss.textContent = "active";
|
|
106
|
-
tss.style.color = "#009900";
|
|
107
|
-
}
|
|
108
|
-
else{
|
|
109
|
-
tss.textContent = "stoped";
|
|
110
|
-
tss.style.color = "#666666";
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch("/telemetry/spec");
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error("ERROR getting the Spec");
|
|
108
|
+
}
|
|
109
|
+
apiSpec = await response.json();
|
|
110
|
+
return apiSpec;
|
|
111
|
+
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error("ERROR getting the Spec :", error);
|
|
114
|
+
return null;
|
|
111
115
|
}
|
|
112
|
-
|
|
113
116
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
function getPathRegEx(path){
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
function getPathRegEx(path) {
|
|
118
120
|
let pathComponents = path.split("/");
|
|
119
121
|
let pathRegExpStr = "^"
|
|
120
|
-
|
|
121
|
-
pathComponents.forEach(c =>{
|
|
122
|
-
if(c != "") {
|
|
122
|
+
|
|
123
|
+
pathComponents.forEach(c => {
|
|
124
|
+
if (c != "") {
|
|
123
125
|
pathRegExpStr += "/";
|
|
124
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
126
|
+
if (c.charAt(0) == "{" && c.charAt(c.length - 1) == "}") {
|
|
125
127
|
pathRegExpStr += "(.*)";
|
|
126
|
-
}else{
|
|
128
|
+
} else {
|
|
127
129
|
pathRegExpStr += c;
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
});
|
|
131
|
-
|
|
133
|
+
|
|
132
134
|
pathRegExpStr += "\$";
|
|
133
|
-
|
|
134
|
-
return
|
|
135
|
+
|
|
136
|
+
return pathRegExpStr;
|
|
135
137
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
async function fetchTracesByParsing(path,method,status) {
|
|
138
|
+
|
|
139
|
+
async function fetchTracesByFind(path, method, status) {
|
|
139
140
|
try {
|
|
140
|
-
log(\`
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
log(\`Fetching traces for <\${path}> - \${method} - \${status} \`);
|
|
142
|
+
const body = {
|
|
143
|
+
"flags": { "containsRegex": true },
|
|
144
|
+
"config": { "regexIds": ["attributes.http.target"] },
|
|
145
|
+
"search": {
|
|
146
|
+
"attributes.http.target": getPathRegEx(path),
|
|
147
|
+
"attributes.http.method": method.toUpperCase(),
|
|
148
|
+
"attributes.http.status_code": parseInt(status)
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
log("body: " + JSON.stringify(body, null, 2));
|
|
152
|
+
//response is to the post at /telemetry/find
|
|
153
|
+
const response = await fetch("/telemetry/find", {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: {
|
|
156
|
+
"Content-Type": "application/json"
|
|
157
|
+
},
|
|
158
|
+
body: JSON.stringify(body)
|
|
159
|
+
});
|
|
160
|
+
|
|
144
161
|
if (!response.ok) {
|
|
145
|
-
|
|
162
|
+
throw new Error("ERROR getting the Traces.");
|
|
146
163
|
}
|
|
147
|
-
|
|
164
|
+
|
|
148
165
|
const responseJSON = await response.json();
|
|
149
166
|
const traces = responseJSON.spans;
|
|
150
|
-
|
|
151
|
-
log(\`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return traces.filter((t)=>{
|
|
155
|
-
return (
|
|
156
|
-
(getPathRegEx(path).test(t.attributes.http_dot_target)) &&
|
|
157
|
-
(t.attributes.http_dot_method.toUpperCase().includes(method.toUpperCase())) &&
|
|
158
|
-
(t.attributes.http_dot_status_code == status)
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
|
-
|
|
167
|
+
|
|
168
|
+
log(\`Fetched \${traces.length} traces.\`);
|
|
169
|
+
return traces;
|
|
170
|
+
|
|
162
171
|
} catch (error) {
|
|
163
172
|
console.error("ERROR getting the Traces :", error);
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
let
|
|
170
|
-
let
|
|
171
|
-
let
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
let
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
|
|
176
|
+
function calculateTiming(startSecInput, startNanoSecInput, endSecInput, endNanoSecInput, precision = 3) {
|
|
177
|
+
let startSec = parseFloat(startSecInput);
|
|
178
|
+
let startNanoSec = parseFloat(startNanoSecInput);
|
|
179
|
+
let endSec = parseFloat(endSecInput);
|
|
180
|
+
let endNanoSec = parseFloat(endNanoSecInput);
|
|
181
|
+
|
|
182
|
+
let startNanoSecParsed = parseFloat("0." + startNanoSec);
|
|
183
|
+
let endNanoSecParsed = parseFloat("0." + endNanoSec);
|
|
184
|
+
|
|
177
185
|
let preciseStart = parseFloat(startSec + startNanoSecParsed);
|
|
178
186
|
let preciseEnd = parseFloat(endSec + endNanoSecParsed);
|
|
179
|
-
let preciseDuration = parseFloat(preciseEnd-preciseStart);
|
|
180
|
-
|
|
181
|
-
let startDate = new Date(preciseStart.toFixed(precision)*1000);
|
|
187
|
+
let preciseDuration = parseFloat(preciseEnd - preciseStart);
|
|
188
|
+
|
|
189
|
+
let startDate = new Date(preciseStart.toFixed(precision) * 1000);
|
|
182
190
|
let startTS = startDate.toISOString();
|
|
183
|
-
|
|
184
|
-
let endDate = new Date(preciseEnd.toFixed(precision)*1000);
|
|
191
|
+
|
|
192
|
+
let endDate = new Date(preciseEnd.toFixed(precision) * 1000);
|
|
185
193
|
let endTS = endDate.toISOString();
|
|
186
|
-
|
|
194
|
+
|
|
187
195
|
return {
|
|
188
196
|
preciseStart: preciseStart,
|
|
189
|
-
preciseEnd
|
|
190
|
-
preciseDuration
|
|
191
|
-
start
|
|
197
|
+
preciseEnd: preciseEnd,
|
|
198
|
+
preciseDuration: preciseDuration,
|
|
199
|
+
start: parseFloat(preciseStart.toFixed(precision)),
|
|
192
200
|
end: parseFloat(preciseEnd.toFixed(precision)),
|
|
193
|
-
duration
|
|
201
|
+
duration: parseFloat(preciseDuration.toFixed(precision)),
|
|
194
202
|
startDate: startDate,
|
|
195
203
|
endDate: endDate,
|
|
196
|
-
startTS
|
|
204
|
+
startTS: startTS,
|
|
197
205
|
endTS: endTS
|
|
198
206
|
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function parseTraceInfo(t) {
|
|
210
|
+
const ep = t.attributes.http.target;
|
|
211
|
+
const method = t.attributes.http.method.toLowerCase();
|
|
212
|
+
const status = t.attributes.http.status_code;
|
|
213
|
+
|
|
214
|
+
const timing = calculateTiming(t.startTime[0], t.startTime[1], t.endTime[0], t.endTime[1]);
|
|
215
|
+
|
|
209
216
|
log(\`\${timing.startTS} - \${timing.endTS} - \${t._spanContext.traceId} - \${t.name} - \${ep} - \${status} - \${timing.duration}\`);
|
|
210
217
|
return {
|
|
211
|
-
ts
|
|
218
|
+
ts: timing.startTS,
|
|
212
219
|
ep: ep,
|
|
213
220
|
method: method,
|
|
214
221
|
status: status,
|
|
215
222
|
duration: timing.duration
|
|
216
223
|
};
|
|
217
224
|
}
|
|
218
|
-
|
|
219
|
-
async function loadStats(path,method,status,cellRequestCount,cellAverageResponseTime){
|
|
220
|
-
|
|
225
|
+
|
|
226
|
+
async function loadStats(path, method, status, cellRequestCount, cellAverageResponseTime) {
|
|
227
|
+
log(\`loadStats(\${path}, \${method}, \${status}, \${cellRequestCount}, \${cellAverageResponseTime})\`);
|
|
228
|
+
let traces = await fetchTracesByFind(path, method, status);
|
|
221
229
|
let requestCount = traces.length;
|
|
222
230
|
let averageResponseTime = 0;
|
|
223
|
-
|
|
224
|
-
traces.forEach(trace=>{
|
|
231
|
+
|
|
232
|
+
traces.forEach(trace => {
|
|
225
233
|
t = parseTraceInfo(trace);
|
|
226
|
-
log(JSON.stringify(t,null,2));
|
|
234
|
+
log(JSON.stringify(t, null, 2));
|
|
227
235
|
averageResponseTime += parseFloat(t.duration);
|
|
228
|
-
log(\`averageResponseTime += t.duration --> \${averageResponseTime} += \${
|
|
236
|
+
log(\`averageResponseTime += t.duration --> \${averageResponseTime} += \${t.duration}\`);
|
|
229
237
|
});
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
|
|
232
239
|
averageResponseTime = averageResponseTime / requestCount;
|
|
233
|
-
|
|
240
|
+
|
|
234
241
|
log(\`averageResponseTime = averageResponseTime / requestCount --> \${averageResponseTime} = \${averageResponseTime} / \${requestCount}\`);
|
|
235
|
-
|
|
242
|
+
|
|
236
243
|
cellRequestCount.textContent = requestCount;
|
|
237
|
-
cellAverageResponseTime.textContent = requestCount? averageResponseTime.toFixed(3):"--";
|
|
238
|
-
|
|
239
|
-
setTimeout(loadStats
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
function loadAPISpec(apiSpec) {
|
|
244
|
-
|
|
244
|
+
cellAverageResponseTime.textContent = requestCount ? averageResponseTime.toFixed(3) : "--";
|
|
245
|
+
|
|
246
|
+
// setTimeout(() => loadStats(path, method, status, cellRequestCount, cellAverageResponseTime), 2000);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function populateOASTable(apiSpec) {
|
|
245
250
|
const tableBody = document.getElementById('apiTable').getElementsByTagName('tbody')[0];
|
|
246
251
|
Object.keys(apiSpec.paths).forEach(path => {
|
|
247
252
|
Object.keys(apiSpec.paths[path]).forEach(method => {
|
|
248
253
|
Object.keys(apiSpec.paths[path][method].responses).forEach(responseType => {
|
|
249
|
-
if(!Number.isNaN(parseInt(responseType))){
|
|
254
|
+
if (!Number.isNaN(parseInt(responseType))) {
|
|
250
255
|
const row = tableBody.insertRow();
|
|
251
256
|
const cellPath = row.insertCell(0);
|
|
252
257
|
const cellMethod = row.insertCell(1);
|
|
253
258
|
const cellStatus = row.insertCell(2);
|
|
254
259
|
const cellDescription = row.insertCell(3);
|
|
255
260
|
const cellRequestCount = row.insertCell(4);
|
|
256
|
-
cellRequestCount.style="text-align: center;"
|
|
261
|
+
cellRequestCount.style = "text-align: center;";
|
|
262
|
+
cellRequestCount.textContent = "--";
|
|
257
263
|
const cellAverageResponseTime = row.insertCell(5);
|
|
258
264
|
cellAverageResponseTime.style.textAlign = "center";
|
|
259
|
-
|
|
265
|
+
cellAverageResponseTime.textContent = "--";
|
|
266
|
+
|
|
267
|
+
const basePath = apiSpec.basePath ? apiSpec.basePath : "";
|
|
268
|
+
const fullPath = basePath + path;
|
|
260
269
|
cellPath.textContent = path;
|
|
270
|
+
cellPath.style.cursor = 'pointer';
|
|
271
|
+
cellPath.onclick = function () {
|
|
272
|
+
window.location.href = row.detailPath;
|
|
273
|
+
};
|
|
261
274
|
cellMethod.textContent = method.toUpperCase();
|
|
262
275
|
cellStatus.textContent = responseType;
|
|
263
|
-
cellDescription.textContent =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
cellDescription.textContent = apiSpec.paths[path][method].summary
|
|
277
|
+
+ " - "
|
|
278
|
+
+ apiSpec.paths[path][method].responses[responseType].description;
|
|
279
|
+
|
|
280
|
+
row.detailPath = \`/telemetry/detail/\${responseType}/\${method.toLowerCase()}\${fullPath}\`;
|
|
281
|
+
const cellCheckbox = row.insertCell(6);
|
|
282
|
+
|
|
283
|
+
// Create and insert checkbox
|
|
284
|
+
const checkbox = document.createElement('input');
|
|
285
|
+
checkbox.type = 'checkbox';
|
|
286
|
+
cellCheckbox.appendChild(checkbox);
|
|
287
|
+
cellCheckbox.style.textAlign = 'center';
|
|
288
|
+
// class to search for the checkboxes
|
|
289
|
+
checkbox.className = 'autoUpdateCheckbox';
|
|
290
|
+
checkbox.disabled = intervalTimer.disabled;
|
|
291
|
+
|
|
292
|
+
// Add event listener to handle checkbox auto-update
|
|
293
|
+
checkbox.addEventListener('change', function () {
|
|
294
|
+
if (this.checked)
|
|
295
|
+
intervalTimer.subscribe(() =>
|
|
296
|
+
loadStats(fullPath, method.toLowerCase(), responseType, cellRequestCount, cellAverageResponseTime)
|
|
297
|
+
);
|
|
298
|
+
else
|
|
299
|
+
intervalTimer.unsubscribe(() =>
|
|
300
|
+
loadStats(fullPath, method.toLowerCase(), responseType, cellRequestCount, cellAverageResponseTime)
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
|
|
276
304
|
}
|
|
277
305
|
});
|
|
278
306
|
});
|
|
279
307
|
});
|
|
280
308
|
}
|
|
281
|
-
|
|
309
|
+
|
|
282
310
|
function sortTable(column) {
|
|
283
311
|
const table = document.getElementById('apiTable');
|
|
284
312
|
let rows, switching, i, x, y, shouldSwitch;
|
|
285
313
|
switching = true;
|
|
286
|
-
// Loop until no switching has been done:
|
|
287
314
|
while (switching) {
|
|
288
315
|
switching = false;
|
|
289
316
|
rows = table.rows;
|
|
290
|
-
// Loop through all table rows (except the first, which contains table headers):
|
|
291
317
|
for (i = 1; i < (rows.length - 1); i++) {
|
|
292
318
|
shouldSwitch = false;
|
|
293
|
-
// Get the two elements you want to compare, one from current row and one from the next:
|
|
294
319
|
x = rows[i].getElementsByTagName("TD")[column];
|
|
295
320
|
y = rows[i + 1].getElementsByTagName("TD")[column];
|
|
296
|
-
// Check if the two rows should switch place:
|
|
297
321
|
if (x.textContent.toLowerCase() > y.textContent.toLowerCase()) {
|
|
298
322
|
shouldSwitch = true;
|
|
299
323
|
break;
|
|
300
324
|
}
|
|
301
325
|
}
|
|
302
326
|
if (shouldSwitch) {
|
|
303
|
-
// If a switch has been marked, make the switch and mark that a switch has been done:
|
|
304
327
|
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
|
305
328
|
switching = true;
|
|
306
329
|
}
|
|
307
330
|
}
|
|
308
331
|
}
|
|
309
|
-
|
|
310
|
-
window.onload = fetchSpec();
|
|
311
|
-
</script>
|
|
312
|
-
</body>
|
|
313
|
-
</html>
|
|
314
|
-
`,
|
|
315
|
-
detail: `
|
|
316
332
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
body {
|
|
325
|
-
font-family: Arial, sans-serif;
|
|
326
|
-
margin: 20px;
|
|
327
|
-
}
|
|
328
|
-
table {
|
|
329
|
-
width: 100%;
|
|
330
|
-
border-collapse: collapse;
|
|
331
|
-
}
|
|
332
|
-
th, td {
|
|
333
|
-
border: 1px solid #dddddd;
|
|
334
|
-
padding: 8px;
|
|
335
|
-
text-align: left;
|
|
336
|
-
cursor: pointer;
|
|
337
|
-
}
|
|
338
|
-
th {
|
|
339
|
-
background-color: #f2f2f2;
|
|
333
|
+
document.getElementById('autoUpdate').addEventListener('change', function () {
|
|
334
|
+
const autoUpdateDisabled = !this.checked;
|
|
335
|
+
intervalTimer.disabled = autoUpdateDisabled;
|
|
336
|
+
// Disable/Enable all the checkboxes
|
|
337
|
+
const checkboxes = document.getElementsByClassName('autoUpdateCheckbox');
|
|
338
|
+
for (let i = 0; i < checkboxes.length; i++) {
|
|
339
|
+
checkboxes[i].disabled = autoUpdateDisabled;
|
|
340
340
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
text-align: center;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
.overlay {
|
|
353
|
-
position: fixed;
|
|
354
|
-
top: 0;
|
|
355
|
-
bottom: 0;
|
|
356
|
-
left: 0;
|
|
357
|
-
right: 0;
|
|
358
|
-
background: rgba(0, 0, 0, 0.7);
|
|
359
|
-
transition: opacity 500ms;
|
|
360
|
-
visibility: hidden;
|
|
361
|
-
opacity: 0;
|
|
362
|
-
overflow: scroll;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.overlay:target {
|
|
366
|
-
visibility: visible;
|
|
367
|
-
opacity: 1;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
async function showTelemetryStatus() {
|
|
344
|
+
const tss = document.getElementById("telemetryStatusSpan");
|
|
345
|
+
const response = await fetch("/telemetry/status");
|
|
346
|
+
if (!response.ok) {
|
|
347
|
+
throw new Error("ERROR getting the Status");
|
|
348
|
+
return false;
|
|
368
349
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
font-size: small;
|
|
379
|
-
overflow: scroll;
|
|
350
|
+
tStatus = await response.json();
|
|
351
|
+
|
|
352
|
+
log(tStatus);
|
|
353
|
+
if (tStatus.active) {
|
|
354
|
+
tss.textContent = "active";
|
|
355
|
+
tss.style.color = "#009900";
|
|
356
|
+
} else {
|
|
357
|
+
tss.textContent = "stopped";
|
|
358
|
+
tss.style.color = "#666666";
|
|
380
359
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
text-decoration: none;
|
|
390
|
-
color: #333;
|
|
360
|
+
return tStatus.active;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
document.getElementById('toggleTelemetry').addEventListener('change', function () {
|
|
364
|
+
if (this.checked) {
|
|
365
|
+
fetch('/telemetry/start').then(showTelemetryStatus());
|
|
366
|
+
} else {
|
|
367
|
+
fetch('/telemetry/stop').then(showTelemetryStatus());
|
|
391
368
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
window.onload = async function () {
|
|
372
|
+
document.getElementById('autoUpdate').checked = !intervalTimer.disabled; // Default to false
|
|
373
|
+
const activeTelemetry = await showTelemetryStatus();
|
|
374
|
+
document.getElementById('toggleTelemetry').checked = activeTelemetry; // Default to not toggled
|
|
375
|
+
const apiSpec = await fetchSpec()
|
|
376
|
+
populateOASTable(apiSpec);
|
|
377
|
+
intervalTimer.start();
|
|
378
|
+
};
|
|
379
|
+
</script>
|
|
380
|
+
</body>
|
|
381
|
+
|
|
382
|
+
</html>`,
|
|
383
|
+
detail: `
|
|
384
|
+
<!DOCTYPE html>
|
|
385
|
+
<html lang="en">
|
|
386
|
+
|
|
387
|
+
<head>
|
|
388
|
+
<meta charset="UTF-8">
|
|
389
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
390
|
+
<title>OAS - Telemetry</title>
|
|
391
|
+
<style>
|
|
392
|
+
body {
|
|
393
|
+
font-family: Arial, sans-serif;
|
|
394
|
+
margin: 20px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
table {
|
|
398
|
+
width: 100%;
|
|
399
|
+
border-collapse: collapse;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
th,
|
|
403
|
+
td {
|
|
404
|
+
border: 1px solid #dddddd;
|
|
405
|
+
padding: 8px;
|
|
406
|
+
text-align: left;
|
|
407
|
+
cursor: pointer;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
th {
|
|
411
|
+
background-color: #f2f2f2;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.box {
|
|
415
|
+
width: 100%;
|
|
416
|
+
margin: 0 auto;
|
|
417
|
+
background: rgba(255, 255, 255, 0.2);
|
|
418
|
+
padding: 35px;
|
|
419
|
+
border: 2px solid #fff;
|
|
420
|
+
border-radius: 20px/50px;
|
|
421
|
+
background-clip: padding-box;
|
|
422
|
+
text-align: center;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.overlay {
|
|
426
|
+
position: fixed;
|
|
427
|
+
top: 0;
|
|
428
|
+
bottom: 0;
|
|
429
|
+
left: 0;
|
|
430
|
+
right: 0;
|
|
431
|
+
background: rgba(0, 0, 0, 0.7);
|
|
432
|
+
transition: opacity 500ms;
|
|
433
|
+
visibility: hidden;
|
|
434
|
+
opacity: 0;
|
|
435
|
+
overflow: scroll;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.overlay:target {
|
|
439
|
+
visibility: visible;
|
|
440
|
+
opacity: 1;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.popup {
|
|
444
|
+
margin: 70px auto;
|
|
445
|
+
padding: 20px;
|
|
446
|
+
background: #fff;
|
|
447
|
+
border-radius: 5px;
|
|
448
|
+
width: 70%;
|
|
449
|
+
position: relative;
|
|
450
|
+
transition: all 5s ease-in-out;
|
|
451
|
+
font-size: small;
|
|
452
|
+
overflow: scroll;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.popup .close {
|
|
456
|
+
position: absolute;
|
|
457
|
+
top: 20px;
|
|
458
|
+
right: 30px;
|
|
459
|
+
transition: all 200ms;
|
|
460
|
+
font-size: 30px;
|
|
461
|
+
font-weight: bold;
|
|
462
|
+
text-decoration: none;
|
|
463
|
+
color: #333;
|
|
464
|
+
}
|
|
465
|
+
</style>
|
|
466
|
+
</head>
|
|
467
|
+
|
|
468
|
+
<body>
|
|
469
|
+
<h1><span id="heading">Telemetry for...</span></h1>
|
|
470
|
+
<a href="/telemetry/">Back</a><br><br>
|
|
471
|
+
<table id="apiTable">
|
|
472
|
+
<thead>
|
|
473
|
+
<tr>
|
|
474
|
+
<th onclick="sortTable(0)">TimeStamp</th>
|
|
475
|
+
<th onclick="sortTable(1)">End point</th>
|
|
476
|
+
<th onclick="sortTable(2)">Method</th>
|
|
477
|
+
<th onclick="sortTable(3)">Status</th>
|
|
478
|
+
<th onclick="sortTable(4)" style="text-align: center;">Response time<br> (sec)</th>
|
|
479
|
+
</tr>
|
|
480
|
+
</thead>
|
|
481
|
+
<tbody>
|
|
482
|
+
</tbody>
|
|
483
|
+
</table>
|
|
484
|
+
<script>
|
|
485
|
+
|
|
486
|
+
let traceCount = -1;
|
|
487
|
+
let LOG = true;
|
|
488
|
+
|
|
489
|
+
function log(s) {
|
|
490
|
+
if (LOG)
|
|
418
491
|
console.log(s);
|
|
419
492
|
}
|
|
420
|
-
|
|
493
|
+
|
|
421
494
|
function parsePath() {
|
|
422
|
-
|
|
495
|
+
|
|
423
496
|
let detailPath = window.location.pathname.split("/");
|
|
424
|
-
|
|
425
|
-
if(detailPath.length < 6 || detailPath[5] == ""){
|
|
497
|
+
|
|
498
|
+
if (detailPath.length < 6 || detailPath[5] == "") {
|
|
426
499
|
alert("Wrong invocation params");
|
|
427
500
|
return;
|
|
428
501
|
}
|
|
429
|
-
|
|
502
|
+
|
|
430
503
|
let status = parseInt(detailPath[3]);
|
|
431
|
-
|
|
432
|
-
if(Number.isNaN(status))
|
|
504
|
+
|
|
505
|
+
if (Number.isNaN(status))
|
|
433
506
|
status = -1;
|
|
434
|
-
|
|
507
|
+
|
|
435
508
|
const method = detailPath[4];
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const path = decodeURI("/"+ detailPath
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
const path = decodeURI("/" + detailPath
|
|
512
|
+
.splice(5, detailPath.length - 5)
|
|
513
|
+
.filter(c => (c != ""))
|
|
514
|
+
.join("/"));
|
|
515
|
+
|
|
443
516
|
headingObj = document.getElementById('heading');
|
|
444
517
|
headingObj.textContent = \`Telemetry for \${path} - \${method} - \${status} \`;
|
|
445
|
-
fetchTracesByParsing(path,method,status);
|
|
518
|
+
fetchTracesByParsing(path, method, status);
|
|
446
519
|
}
|
|
447
|
-
|
|
448
|
-
function getSearchQuery(path,method,status){
|
|
520
|
+
|
|
521
|
+
function getSearchQuery(path, method, status) {
|
|
449
522
|
let pathComponents = path.split("/");
|
|
450
523
|
let pathRegex = "^"
|
|
451
|
-
|
|
452
|
-
pathComponents.forEach(c =>{
|
|
453
|
-
if(c != "") {
|
|
524
|
+
|
|
525
|
+
pathComponents.forEach(c => {
|
|
526
|
+
if (c != "") {
|
|
454
527
|
pathRegex += "/";
|
|
455
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
528
|
+
if (c.charAt(0) == "{" && c.charAt(c.length - 1) == "}") {
|
|
456
529
|
pathRegex += "(.*)";
|
|
457
|
-
}else{
|
|
530
|
+
} else {
|
|
458
531
|
pathRegex += c;
|
|
459
532
|
}
|
|
460
533
|
}
|
|
461
534
|
});
|
|
462
|
-
|
|
535
|
+
|
|
463
536
|
pathRegex += "\$";
|
|
464
|
-
|
|
537
|
+
|
|
465
538
|
return {
|
|
466
|
-
"attributes.
|
|
467
|
-
"name"
|
|
468
|
-
"attributes.
|
|
539
|
+
"attributes.http.target": { \$regex: new RegExp(pathRegex) },
|
|
540
|
+
"name": method.toUpperCase(),
|
|
541
|
+
"attributes.http.status_code": status
|
|
469
542
|
};
|
|
470
543
|
}
|
|
471
|
-
|
|
472
|
-
async function fetchTracesByFinding(path,method,status) {
|
|
544
|
+
|
|
545
|
+
async function fetchTracesByFinding(path, method, status) {
|
|
473
546
|
try {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
547
|
+
log(\`Fetching traces for <\${path}> - \${method} - \${status} \`);
|
|
548
|
+
const body = {
|
|
549
|
+
"flags": { "containsRegex": true },
|
|
550
|
+
"config": { "regexIds": ["attributes.http.target"] },
|
|
551
|
+
"search": {
|
|
552
|
+
"attributes.http.target": getPathRegEx(path),
|
|
553
|
+
"attributes.http.method": method.toUpperCase(),
|
|
554
|
+
"attributes.http.status_code": parseInt(status)
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
log("body: " + JSON.stringify(body, null, 2));
|
|
558
|
+
//response is to the post at /telemetry/find
|
|
559
|
+
const response = await fetch("/telemetry/find", {
|
|
560
|
+
method: "POST",
|
|
561
|
+
headers: {
|
|
562
|
+
"Content-Type": "application/json"
|
|
563
|
+
},
|
|
564
|
+
body: JSON.stringify(body)
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (!response.ok) {
|
|
568
|
+
throw new Error("ERROR getting the Traces.");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const traces = await response.json();
|
|
572
|
+
loadTraces(traces);
|
|
573
|
+
|
|
574
|
+
} catch (error) {
|
|
575
|
+
console.error("ERROR getting the Traces :", error);
|
|
485
576
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
} catch (error) {
|
|
491
|
-
console.error("ERROR getting the Traces :", error);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function getPathRegEx(path){
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getPathRegEx(path) {
|
|
496
580
|
let pathComponents = path.split("/");
|
|
497
581
|
let pathRegExpStr = "^"
|
|
498
|
-
|
|
499
|
-
pathComponents.forEach(c =>{
|
|
500
|
-
if(c != "") {
|
|
582
|
+
|
|
583
|
+
pathComponents.forEach(c => {
|
|
584
|
+
if (c != "") {
|
|
501
585
|
pathRegExpStr += "/";
|
|
502
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
586
|
+
if (c.charAt(0) == "{" && c.charAt(c.length - 1) == "}") {
|
|
503
587
|
pathRegExpStr += "(.*)";
|
|
504
|
-
}else{
|
|
588
|
+
} else {
|
|
505
589
|
pathRegExpStr += c;
|
|
506
590
|
}
|
|
507
591
|
}
|
|
508
592
|
});
|
|
509
|
-
|
|
593
|
+
|
|
510
594
|
pathRegExpStr += "\$";
|
|
511
|
-
|
|
512
|
-
return
|
|
595
|
+
|
|
596
|
+
return pathRegExpStr;
|
|
513
597
|
}
|
|
514
|
-
|
|
515
|
-
async function fetchTracesByParsing(path,method,status) {
|
|
598
|
+
|
|
599
|
+
async function fetchTracesByParsing(path, method, status) {
|
|
516
600
|
try {
|
|
517
601
|
log(\`Fetchig traces for <\${path}> - \${method} - \${status},.. \`);
|
|
518
|
-
|
|
602
|
+
|
|
519
603
|
const response = await fetch("/telemetry/list");
|
|
520
|
-
|
|
604
|
+
|
|
521
605
|
if (!response.ok) {
|
|
522
|
-
|
|
606
|
+
throw new Error("ERROR getting the Traces.");
|
|
523
607
|
}
|
|
524
|
-
|
|
608
|
+
|
|
525
609
|
const responseJSON = await response.json();
|
|
526
610
|
const traces = responseJSON.spans;
|
|
527
|
-
|
|
611
|
+
const filteredTraces = traces; //filter made in server side
|
|
528
612
|
log(\`Feched \${traces.length} traces.\`);
|
|
529
613
|
//log(\`First trace: \${JSON.stringify(traces[0],null,2)}\`);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
(getPathRegEx(path).test(t.attributes.http_dot_target)) &&
|
|
533
|
-
(t.attributes.http_dot_method.toUpperCase().includes(method.toUpperCase())) &&
|
|
534
|
-
(t.attributes.http_dot_status_code == status)
|
|
535
|
-
);
|
|
536
|
-
})
|
|
537
|
-
|
|
538
|
-
if(filteredTraces.length != traceCount){
|
|
614
|
+
|
|
615
|
+
if (filteredTraces.length != traceCount) {
|
|
539
616
|
loadTraces(filteredTraces);
|
|
540
617
|
traceCount = filteredTraces.length;
|
|
541
618
|
}
|
|
542
|
-
|
|
543
|
-
setTimeout(fetchTracesByParsing,1000,path,method,status);
|
|
544
|
-
|
|
619
|
+
|
|
620
|
+
setTimeout(fetchTracesByParsing, 1000, path, method, status);
|
|
621
|
+
|
|
545
622
|
} catch (error) {
|
|
546
623
|
console.error("ERROR getting the Traces :", error);
|
|
547
624
|
}
|
|
548
625
|
}
|
|
549
|
-
|
|
550
|
-
function calculateTiming(startSecInput,startNanoSecInput,endSecInput,endNanoSecInput,precision = 3){
|
|
626
|
+
|
|
627
|
+
function calculateTiming(startSecInput, startNanoSecInput, endSecInput, endNanoSecInput, precision = 3) {
|
|
551
628
|
// Default precision 3 = miliseconds
|
|
552
|
-
|
|
553
|
-
let startSec= parseFloat(startSecInput)
|
|
554
|
-
let startNanoSec= parseFloat(startNanoSecInput)
|
|
555
|
-
let endSec= parseFloat(endSecInput)
|
|
556
|
-
let endNanoSec= parseFloat(endNanoSecInput)
|
|
557
|
-
|
|
558
|
-
let startNanoSecParsed = parseFloat("0."+startNanoSec);
|
|
559
|
-
let endNanoSecParsed = parseFloat("0."+endNanoSec);
|
|
560
|
-
|
|
629
|
+
|
|
630
|
+
let startSec = parseFloat(startSecInput)
|
|
631
|
+
let startNanoSec = parseFloat(startNanoSecInput)
|
|
632
|
+
let endSec = parseFloat(endSecInput)
|
|
633
|
+
let endNanoSec = parseFloat(endNanoSecInput)
|
|
634
|
+
|
|
635
|
+
let startNanoSecParsed = parseFloat("0." + startNanoSec);
|
|
636
|
+
let endNanoSecParsed = parseFloat("0." + endNanoSec);
|
|
637
|
+
|
|
561
638
|
let preciseStart = parseFloat(startSec + startNanoSecParsed);
|
|
562
639
|
let preciseEnd = parseFloat(endSec + endNanoSecParsed);
|
|
563
|
-
let preciseDuration = parseFloat(preciseEnd-preciseStart);
|
|
564
|
-
|
|
565
|
-
let startDate = new Date(preciseStart.toFixed(precision)*1000);
|
|
640
|
+
let preciseDuration = parseFloat(preciseEnd - preciseStart);
|
|
641
|
+
|
|
642
|
+
let startDate = new Date(preciseStart.toFixed(precision) * 1000);
|
|
566
643
|
let startTS = startDate.toISOString();
|
|
567
|
-
|
|
568
|
-
let endDate = new Date(preciseEnd.toFixed(precision)*1000);
|
|
644
|
+
|
|
645
|
+
let endDate = new Date(preciseEnd.toFixed(precision) * 1000);
|
|
569
646
|
let endTS = endDate.toISOString();
|
|
570
|
-
|
|
647
|
+
|
|
571
648
|
return {
|
|
572
649
|
preciseStart: preciseStart,
|
|
573
|
-
preciseEnd
|
|
574
|
-
preciseDuration
|
|
575
|
-
start
|
|
650
|
+
preciseEnd: preciseEnd,
|
|
651
|
+
preciseDuration: preciseDuration,
|
|
652
|
+
start: parseFloat(preciseStart.toFixed(precision)),
|
|
576
653
|
end: parseFloat(preciseEnd.toFixed(precision)),
|
|
577
|
-
duration
|
|
654
|
+
duration: parseFloat(preciseDuration.toFixed(precision)),
|
|
578
655
|
startDate: startDate,
|
|
579
656
|
endDate: endDate,
|
|
580
|
-
startTS
|
|
657
|
+
startTS: startTS,
|
|
581
658
|
endTS: endTS
|
|
582
659
|
};
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function parseTraceInfo(t){
|
|
587
|
-
const ep = t.attributes.
|
|
588
|
-
const method = t.attributes.
|
|
589
|
-
const status = t.attributes.
|
|
590
|
-
|
|
591
|
-
const timing = calculateTiming(t.startTime[0],t.startTime[1],t.endTime[0],t.endTime[1]);
|
|
592
|
-
|
|
660
|
+
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function parseTraceInfo(t) {
|
|
664
|
+
const ep = t.attributes.http.target;
|
|
665
|
+
const method = t.attributes.http.method.toLowerCase();
|
|
666
|
+
const status = t.attributes.http.status_code;
|
|
667
|
+
|
|
668
|
+
const timing = calculateTiming(t.startTime[0], t.startTime[1], t.endTime[0], t.endTime[1]);
|
|
669
|
+
|
|
593
670
|
log(\`\${timing.startTS} - \${timing.endTS} - \${t._spanContext.traceId} - \${t.name} - \${ep} - \${status} - \${timing.duration}\`);
|
|
594
671
|
return {
|
|
595
|
-
ts
|
|
672
|
+
ts: timing.startTS,
|
|
596
673
|
ep: ep,
|
|
597
674
|
method: method,
|
|
598
675
|
status: status,
|
|
599
676
|
duration: timing.duration
|
|
600
677
|
};
|
|
601
678
|
}
|
|
602
|
-
|
|
679
|
+
|
|
603
680
|
function loadTraces(traces) {
|
|
604
|
-
|
|
681
|
+
|
|
605
682
|
const tableBody = document.getElementById('apiTable').getElementsByTagName('tbody')[0];
|
|
606
683
|
while (tableBody.hasChildNodes()) {
|
|
607
684
|
tableBody.removeChild(tableBody.lastChild);
|
|
608
685
|
}
|
|
609
|
-
|
|
686
|
+
|
|
610
687
|
traces.forEach(trace => {
|
|
611
688
|
const row = tableBody.insertRow();
|
|
612
689
|
const cellTS = row.insertCell(0);
|
|
@@ -617,26 +694,26 @@ function ui() {
|
|
|
617
694
|
cellStatus.style.textAlign = "center";
|
|
618
695
|
const cellDuration = row.insertCell(4);
|
|
619
696
|
cellDuration.style.textAlign = "center";
|
|
620
|
-
|
|
697
|
+
|
|
621
698
|
let t = parseTraceInfo(trace);
|
|
622
|
-
|
|
699
|
+
|
|
623
700
|
cellTS.textContent = t.ts;
|
|
624
701
|
cellEP.textContent = t.ep;
|
|
625
702
|
cellMethod.textContent = t.method;
|
|
626
703
|
cellStatus.textContent = t.status;
|
|
627
|
-
cellDuration.textContent = t.duration.toFixed(3);
|
|
628
|
-
|
|
704
|
+
cellDuration.textContent = t.duration.toFixed(3);
|
|
705
|
+
|
|
629
706
|
row.trace = trace;
|
|
630
|
-
row.onclick = function() {
|
|
707
|
+
row.onclick = function () {
|
|
631
708
|
const popup = document.getElementById("tracePopup");
|
|
632
|
-
popup.firstChild.nodeValue = JSON.stringify(this.trace,null,2);
|
|
709
|
+
popup.firstChild.nodeValue = JSON.stringify(this.trace, null, 2);
|
|
633
710
|
const popupOverlay = document.getElementById("popupOverlay");
|
|
634
711
|
popupOverlay.style.visibility = "visible";
|
|
635
712
|
popupOverlay.style.opacity = 1;
|
|
636
713
|
};
|
|
637
714
|
});
|
|
638
715
|
}
|
|
639
|
-
|
|
716
|
+
|
|
640
717
|
function sortTable(column) {
|
|
641
718
|
const table = document.getElementById('apiTable');
|
|
642
719
|
let rows, switching, i, x, y, shouldSwitch;
|
|
@@ -664,120 +741,152 @@ function ui() {
|
|
|
664
741
|
}
|
|
665
742
|
}
|
|
666
743
|
}
|
|
667
|
-
|
|
668
|
-
function hidePopup(){
|
|
744
|
+
|
|
745
|
+
function hidePopup() {
|
|
669
746
|
const popupOverlay = document.getElementById("popupOverlay");
|
|
670
747
|
popupOverlay.style.visibility = "hidden";
|
|
671
748
|
popupOverlay.style.opacity = 0;
|
|
672
749
|
}
|
|
673
|
-
|
|
750
|
+
|
|
674
751
|
window.onload = parsePath();
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
"
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
"
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
"
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
752
|
+
</script>
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
<div id="popupOverlay" class="overlay">
|
|
757
|
+
<div class="popup">
|
|
758
|
+
<pre id="tracePopup">
|
|
759
|
+
"attributes": {
|
|
760
|
+
"http": {
|
|
761
|
+
"url": "http://localhost:3000/api/v1/test",
|
|
762
|
+
"host": "localhost:3000",
|
|
763
|
+
"method": "GET",
|
|
764
|
+
"scheme": "http",
|
|
765
|
+
"target": "/api/v1/test",
|
|
766
|
+
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
|
|
767
|
+
"flavor": "1.1",
|
|
768
|
+
"status_code": 304,
|
|
769
|
+
"status_text": "NOT MODIFIED"
|
|
770
|
+
},
|
|
771
|
+
"net": {
|
|
772
|
+
"host": {
|
|
773
|
+
"name": "localhost",
|
|
774
|
+
"ip": "::1",
|
|
775
|
+
"port": 3000
|
|
776
|
+
},
|
|
777
|
+
"transport": "ip_tcp",
|
|
778
|
+
"peer": {
|
|
779
|
+
"ip": "::1",
|
|
780
|
+
"port": 58101
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
"links": {
|
|
785
|
+
|
|
786
|
+
},
|
|
787
|
+
"events": {
|
|
788
|
+
|
|
789
|
+
},
|
|
790
|
+
"_droppedAttributesCount": 0,
|
|
791
|
+
"_droppedEventsCount": 0,
|
|
792
|
+
"_droppedLinksCount": 0,
|
|
793
|
+
"status": {
|
|
794
|
+
"code": 0
|
|
795
|
+
},
|
|
796
|
+
"endTime": {
|
|
797
|
+
"0": 1724019619,
|
|
798
|
+
"1": 414126300
|
|
799
|
+
},
|
|
800
|
+
"_ended": true,
|
|
801
|
+
"_duration": {
|
|
802
|
+
"0": 0,
|
|
803
|
+
"1": 2126300
|
|
804
|
+
},
|
|
805
|
+
"name": "GET",
|
|
806
|
+
"_spanContext": {
|
|
807
|
+
"traceId": "ee70c9a937bbf95940a8971dc96077b3",
|
|
808
|
+
"spanId": "4fe34ee075253ecb",
|
|
809
|
+
"traceFlags": 1
|
|
810
|
+
},
|
|
811
|
+
"kind": 1,
|
|
812
|
+
"_performanceStartTime": 40561.097599983215,
|
|
813
|
+
"_performanceOffset": -8.425537109375,
|
|
814
|
+
"_startTimeProvided": false,
|
|
815
|
+
"startTime": {
|
|
816
|
+
"0": 1724019619,
|
|
817
|
+
"1": 412000000
|
|
818
|
+
},
|
|
819
|
+
"resource": {
|
|
820
|
+
"_attributes": {
|
|
821
|
+
"service": {
|
|
822
|
+
"name": "unknown_service:node"
|
|
823
|
+
},
|
|
824
|
+
"telemetry": {
|
|
825
|
+
"sdk": {
|
|
826
|
+
"language": "nodejs",
|
|
827
|
+
"name": "opentelemetry",
|
|
828
|
+
"version": "1.22.0"
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
"process": {
|
|
832
|
+
"pid": 23128,
|
|
833
|
+
"executable": {
|
|
834
|
+
"name": "npm",
|
|
835
|
+
"path": "C:\\Program Files\\nodejs\\node.exe"
|
|
836
|
+
},
|
|
837
|
+
"command_args": {
|
|
838
|
+
"0": "C:\\Program Files\\nodejs\\node.exe",
|
|
839
|
+
"1": "C:\\Personal\\ISA\\telemetry\\ot-ui-poc\\index"
|
|
840
|
+
},
|
|
841
|
+
"runtime": {
|
|
842
|
+
"version": "14.21.3",
|
|
843
|
+
"name": "nodejs",
|
|
844
|
+
"description": "Node.js"
|
|
845
|
+
},
|
|
846
|
+
"command": "C:\\Personal\\ISA\\telemetry\\ot-ui-poc\\index",
|
|
847
|
+
"owner": "manol"
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
"asyncAttributesPending": false,
|
|
851
|
+
"_syncAttributes": {
|
|
852
|
+
"service": {
|
|
853
|
+
"name": "unknown_service:node"
|
|
854
|
+
},
|
|
855
|
+
"telemetry": {
|
|
856
|
+
"sdk": {
|
|
857
|
+
"language": "nodejs",
|
|
858
|
+
"name": "opentelemetry",
|
|
859
|
+
"version": "1.22.0"
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
"_asyncAttributesPromise": {
|
|
864
|
+
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
"instrumentationLibrary": {
|
|
868
|
+
"name": "@opentelemetry/instrumentation-http",
|
|
869
|
+
"version": "0.51.0"
|
|
870
|
+
},
|
|
871
|
+
"_spanLimits": {
|
|
872
|
+
"attributeValueLengthLimit": null,
|
|
873
|
+
"attributeCountLimit": 128,
|
|
874
|
+
"linkCountLimit": 128,
|
|
875
|
+
"eventCountLimit": 128,
|
|
876
|
+
"attributePerEventCountLimit": 128,
|
|
877
|
+
"attributePerLinkCountLimit": 128
|
|
878
|
+
},
|
|
879
|
+
"_attributeValueLengthLimit": null,
|
|
880
|
+
"_spanProcessor": "oas-telemetry skips this field to avoid circular reference",
|
|
881
|
+
"_id": "f2989F2IDm3uSfml"
|
|
882
|
+
</pre>
|
|
883
|
+
<a class="close" href="#" onclick="hidePopup()">×</a>
|
|
776
884
|
</div>
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
885
|
+
</div>
|
|
886
|
+
|
|
887
|
+
</body>
|
|
888
|
+
|
|
889
|
+
</html>`
|
|
781
890
|
};
|
|
782
891
|
}
|
|
783
892
|
module.exports = exports.default;
|