@oas-tools/oas-telemetry 0.1.10 → 0.2.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/README.md +39 -1
- package/dist/index.cjs +8 -7
- package/dist/telemetry.cjs +1 -24
- package/dist/ui.cjs +756 -692
- package/package.json +14 -2
- package/src/index.js +8 -9
- package/src/telemetry.js +1 -23
- package/src/ui.js +754 -694
- package/src/ui/detail.html +0 -441
- package/src/ui/main.html +0 -266
package/dist/ui.cjs
CHANGED
|
@@ -3,717 +3,781 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default =
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
#telemetryStatusSpan {
|
|
35
|
-
color:rgb(0, 123, 6);
|
|
36
|
-
font-size: x-small;
|
|
37
|
-
}
|
|
38
|
-
</style>
|
|
39
|
-
</head>
|
|
40
|
-
<body>
|
|
41
|
-
<h1>Telemetry <span id="telemetryStatusSpan"></span></h1>
|
|
42
|
-
<table id="apiTable">
|
|
43
|
-
<thead>
|
|
44
|
-
<tr>
|
|
45
|
-
<th onclick="sortTable(0)">Path</th>
|
|
46
|
-
<th onclick="sortTable(1)">Method</th>
|
|
47
|
-
<th onclick="sortTable(2)">Status</th>
|
|
48
|
-
<th onclick="sortTable(3)">Description</th>
|
|
49
|
-
<th onclick="sortTable(4)" style="text-align: center;">Request <br>Count</th>
|
|
50
|
-
<th onclick="sortTable(5)" style="text-align: center;">Average response time<br> (sec)</th>
|
|
51
|
-
|
|
52
|
-
</tr>
|
|
53
|
-
</thead>
|
|
54
|
-
<tbody>
|
|
55
|
-
</tbody>
|
|
56
|
-
</table>
|
|
57
|
-
<br/>
|
|
58
|
-
<button onclick="fetch('/telemetry/start');loadTelemetryStatus();">Start</button>
|
|
59
|
-
<button onclick="fetch('/telemetry/stop');loadTelemetryStatus();">Stop</button>
|
|
60
|
-
<button onclick="fetch('/telemetry/reset');loadTelemetryStatus();">Reset</button>
|
|
61
|
-
<script>
|
|
62
|
-
|
|
63
|
-
async function fetchSpec() {
|
|
64
|
-
try {
|
|
65
|
-
const response = await fetch("/telemetry/spec");
|
|
66
|
-
if (!response.ok) {
|
|
67
|
-
throw new Error("ERROR getting the Spec");
|
|
68
|
-
}
|
|
69
|
-
apiSpec = await response.json();
|
|
70
|
-
loadAPISpec(apiSpec);
|
|
71
|
-
loadTelemetryStatus();
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error("ERROR getting the Spec :", error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function loadTelemetryStatus(){
|
|
78
|
-
console.log("TEST");
|
|
79
|
-
const tss = document.getElementById("telemetryStatusSpan");
|
|
80
|
-
const response = await fetch("/telemetry/status");
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
throw new Error("ERROR getting the Status");
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
tStatus = await response.json();
|
|
86
|
-
|
|
87
|
-
console.log(tStatus);
|
|
88
|
-
if(tStatus.active){
|
|
89
|
-
tss.textContent = "active";
|
|
90
|
-
tss.style.color = "#009900";
|
|
91
|
-
}
|
|
92
|
-
else{
|
|
93
|
-
tss.textContent = "stoped";
|
|
94
|
-
tss.style.color = "#666666";
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
function getPathRegEx(path){
|
|
102
|
-
let pathComponents = path.split("/");
|
|
103
|
-
let pathRegExpStr = "^"
|
|
104
|
-
|
|
105
|
-
pathComponents.forEach(c =>{
|
|
106
|
-
if(c != "") {
|
|
107
|
-
pathRegExpStr += "/";
|
|
108
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
109
|
-
pathRegExpStr += "(.*)";
|
|
110
|
-
}else{
|
|
111
|
-
pathRegExpStr += c;
|
|
6
|
+
exports.default = ui;
|
|
7
|
+
function ui() {
|
|
8
|
+
/*
|
|
9
|
+
Sources:
|
|
10
|
+
ui/detail.html
|
|
11
|
+
ui/main.html
|
|
12
|
+
Parsing:
|
|
13
|
+
` --> \`
|
|
14
|
+
$ --> \$
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
main: `
|
|
19
|
+
<!DOCTYPE html>
|
|
20
|
+
<html lang="en">
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="UTF-8">
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
24
|
+
<title>OAS - Telemetry</title>
|
|
25
|
+
<style>
|
|
26
|
+
body {
|
|
27
|
+
font-family: Arial, sans-serif;
|
|
28
|
+
margin: 20px;
|
|
29
|
+
}
|
|
30
|
+
table {
|
|
31
|
+
width: 100%;
|
|
32
|
+
border-collapse: collapse;
|
|
112
33
|
}
|
|
34
|
+
th, td {
|
|
35
|
+
border: 1px solid #dddddd;
|
|
36
|
+
padding: 8px;
|
|
37
|
+
text-align: left;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
}
|
|
40
|
+
th {
|
|
41
|
+
background-color: #f2f2f2;
|
|
42
|
+
}
|
|
43
|
+
#telemetryStatusSpan {
|
|
44
|
+
color:rgb(0, 123, 6);
|
|
45
|
+
font-size: x-small;
|
|
46
|
+
}
|
|
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);
|
|
113
77
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (!response.ok) {
|
|
129
|
-
throw new Error("ERROR getting the Traces.");
|
|
78
|
+
|
|
79
|
+
async function fetchSpec() {
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch("/telemetry/spec");
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error("ERROR getting the Spec");
|
|
84
|
+
}
|
|
85
|
+
apiSpec = await response.json();
|
|
86
|
+
loadAPISpec(apiSpec);
|
|
87
|
+
loadTelemetryStatus();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error("ERROR getting the Spec :", error);
|
|
90
|
+
}
|
|
130
91
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const duration = durationSec + (durationMiliSec / 1000)
|
|
169
|
-
|
|
170
|
-
let startDateObj = new Date((startSec * 1000)+(startNanoSec / 10000));
|
|
171
|
-
let startTS = startDateObj.toISOString();
|
|
172
|
-
|
|
173
|
-
let endDateObj = new Date((endSec * 1000)+(endNanoSec / 10000));
|
|
174
|
-
let endTS = endDateObj.toISOString();
|
|
175
|
-
|
|
176
|
-
console.log(\`\${startTS} - \${endTS} - \${t._spanContext.traceId} - \${t.name} - \${ep} - \${status} - \${duration}\`);
|
|
177
|
-
return {
|
|
178
|
-
ts : startTS,
|
|
179
|
-
ep: ep,
|
|
180
|
-
method: method,
|
|
181
|
-
status: status,
|
|
182
|
-
duration: duration
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async function loadStats(path,method,status,cellRequestCount,cellAverageResponseTime){
|
|
187
|
-
let traces = await fetchTracesByParsing(path,method,status);
|
|
188
|
-
let requestCount = traces.length;
|
|
189
|
-
let averageResponseTime = 0;
|
|
190
|
-
|
|
191
|
-
traces.forEach(trace=>{
|
|
192
|
-
t = parseTraceInfo(trace);
|
|
193
|
-
averageResponseTime += t.duration;
|
|
194
|
-
});
|
|
195
|
-
averageResponseTime = averageResponseTime / requestCount;
|
|
196
|
-
|
|
197
|
-
cellRequestCount.textContent = requestCount;
|
|
198
|
-
cellAverageResponseTime.textContent = requestCount? averageResponseTime.toFixed(3):"--";
|
|
199
|
-
|
|
200
|
-
setTimeout(loadStats,2000,path,method,status,cellRequestCount,cellAverageResponseTime);
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function loadAPISpec(apiSpec) {
|
|
205
|
-
|
|
206
|
-
const tableBody = document.getElementById('apiTable').getElementsByTagName('tbody')[0];
|
|
207
|
-
Object.keys(apiSpec.paths).forEach(path => {
|
|
208
|
-
Object.keys(apiSpec.paths[path]).forEach(method => {
|
|
209
|
-
Object.keys(apiSpec.paths[path][method].responses).forEach(responseType => {
|
|
210
|
-
if(!Number.isNaN(parseInt(responseType))){
|
|
211
|
-
const row = tableBody.insertRow();
|
|
212
|
-
const cellPath = row.insertCell(0);
|
|
213
|
-
const cellMethod = row.insertCell(1);
|
|
214
|
-
const cellStatus = row.insertCell(2);
|
|
215
|
-
const cellDescription = row.insertCell(3);
|
|
216
|
-
const cellRequestCount = row.insertCell(4);
|
|
217
|
-
cellRequestCount.style="text-align: center;"
|
|
218
|
-
const cellAverageResponseTime = row.insertCell(5);
|
|
219
|
-
cellAverageResponseTime.style.textAlign = "center";
|
|
220
|
-
|
|
221
|
-
cellPath.textContent = path;
|
|
222
|
-
cellMethod.textContent = method.toUpperCase();
|
|
223
|
-
cellStatus.textContent = responseType;
|
|
224
|
-
cellDescription.textContent = apiSpec.paths[path][method].summary
|
|
225
|
-
+ " - "
|
|
226
|
-
+ apiSpec.paths[path][method].responses[responseType].description;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
loadStats(path,method.toLowerCase(),responseType,cellRequestCount,cellAverageResponseTime);
|
|
230
|
-
setTimeout(loadStats,1000,path,method.toLowerCase(),responseType,cellRequestCount,cellAverageResponseTime);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
row.detailPath = \`/telemetry/detail/\${responseType}/\${method.toLowerCase()}\${path}\`;
|
|
234
|
-
row.onclick = function(){
|
|
235
|
-
window.location.href = this.detailPath;
|
|
236
|
-
};
|
|
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";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
function getPathRegEx(path){
|
|
118
|
+
let pathComponents = path.split("/");
|
|
119
|
+
let pathRegExpStr = "^"
|
|
120
|
+
|
|
121
|
+
pathComponents.forEach(c =>{
|
|
122
|
+
if(c != "") {
|
|
123
|
+
pathRegExpStr += "/";
|
|
124
|
+
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
125
|
+
pathRegExpStr += "(.*)";
|
|
126
|
+
}else{
|
|
127
|
+
pathRegExpStr += c;
|
|
128
|
+
}
|
|
237
129
|
}
|
|
238
130
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
131
|
+
|
|
132
|
+
pathRegExpStr += "\$";
|
|
133
|
+
|
|
134
|
+
return new RegExp(pathRegExpStr);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async function fetchTracesByParsing(path,method,status) {
|
|
139
|
+
try {
|
|
140
|
+
log(\`Fetchig traces for <\${path}> - \${method} - \${status},.. \`);
|
|
141
|
+
|
|
142
|
+
const response = await fetch("/telemetry/list");
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new Error("ERROR getting the Traces.");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const responseJSON = await response.json();
|
|
149
|
+
const traces = responseJSON.spans;
|
|
150
|
+
|
|
151
|
+
log(\`Feched \${traces.length} traces.\`);
|
|
152
|
+
//log(\`First trace: \${JSON.stringify(traces[0],null,2)}\`);
|
|
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
|
+
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error("ERROR getting the Traces :", error);
|
|
261
164
|
}
|
|
262
165
|
}
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
|
|
166
|
+
function calculateTiming(startSecInput,startNanoSecInput,endSecInput,endNanoSecInput,precision = 3){
|
|
167
|
+
// Default precision 3 = miliseconds
|
|
168
|
+
|
|
169
|
+
let startSec= parseFloat(startSecInput)
|
|
170
|
+
let startNanoSec= parseFloat(startNanoSecInput)
|
|
171
|
+
let endSec= parseFloat(endSecInput)
|
|
172
|
+
let endNanoSec= parseFloat(endNanoSecInput)
|
|
173
|
+
|
|
174
|
+
let startNanoSecParsed = parseFloat("0."+startNanoSec);
|
|
175
|
+
let endNanoSecParsed = parseFloat("0."+endNanoSec);
|
|
176
|
+
|
|
177
|
+
let preciseStart = parseFloat(startSec + startNanoSecParsed);
|
|
178
|
+
let preciseEnd = parseFloat(endSec + endNanoSecParsed);
|
|
179
|
+
let preciseDuration = parseFloat(preciseEnd-preciseStart);
|
|
180
|
+
|
|
181
|
+
let startDate = new Date(preciseStart.toFixed(precision)*1000);
|
|
182
|
+
let startTS = startDate.toISOString();
|
|
183
|
+
|
|
184
|
+
let endDate = new Date(preciseEnd.toFixed(precision)*1000);
|
|
185
|
+
let endTS = endDate.toISOString();
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
preciseStart: preciseStart,
|
|
189
|
+
preciseEnd : preciseEnd,
|
|
190
|
+
preciseDuration : preciseDuration,
|
|
191
|
+
start : parseFloat(preciseStart.toFixed(precision)),
|
|
192
|
+
end: parseFloat(preciseEnd.toFixed(precision)),
|
|
193
|
+
duration : parseFloat(preciseDuration.toFixed(precision)),
|
|
194
|
+
startDate: startDate,
|
|
195
|
+
endDate: endDate,
|
|
196
|
+
startTS :startTS,
|
|
197
|
+
endTS: endTS
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseTraceInfo(t){
|
|
203
|
+
const ep = t.attributes.http_dot_target;
|
|
204
|
+
const method = t.attributes.http_dot_method.toLowerCase();
|
|
205
|
+
const status = t.attributes.http_dot_status_code;
|
|
206
|
+
|
|
207
|
+
const timing = calculateTiming(t.startTime[0],t.startTime[1],t.endTime[0],t.endTime[1]);
|
|
208
|
+
|
|
209
|
+
log(\`\${timing.startTS} - \${timing.endTS} - \${t._spanContext.traceId} - \${t.name} - \${ep} - \${status} - \${timing.duration}\`);
|
|
210
|
+
return {
|
|
211
|
+
ts : timing.startTS,
|
|
212
|
+
ep: ep,
|
|
213
|
+
method: method,
|
|
214
|
+
status: status,
|
|
215
|
+
duration: timing.duration
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function loadStats(path,method,status,cellRequestCount,cellAverageResponseTime){
|
|
220
|
+
let traces = await fetchTracesByParsing(path,method,status);
|
|
221
|
+
let requestCount = traces.length;
|
|
222
|
+
let averageResponseTime = 0;
|
|
223
|
+
|
|
224
|
+
traces.forEach(trace=>{
|
|
225
|
+
t = parseTraceInfo(trace);
|
|
226
|
+
log(JSON.stringify(t,null,2));
|
|
227
|
+
averageResponseTime += parseFloat(t.duration);
|
|
228
|
+
log(\`averageResponseTime += t.duration --> \${averageResponseTime} += \${ t.duration }\`);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
averageResponseTime = averageResponseTime / requestCount;
|
|
233
|
+
|
|
234
|
+
log(\`averageResponseTime = averageResponseTime / requestCount --> \${averageResponseTime} = \${averageResponseTime} / \${requestCount}\`);
|
|
235
|
+
|
|
236
|
+
cellRequestCount.textContent = requestCount;
|
|
237
|
+
cellAverageResponseTime.textContent = requestCount? averageResponseTime.toFixed(3):"--";
|
|
238
|
+
|
|
239
|
+
setTimeout(loadStats,2000,path,method,status,cellRequestCount,cellAverageResponseTime);
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function loadAPISpec(apiSpec) {
|
|
244
|
+
|
|
245
|
+
const tableBody = document.getElementById('apiTable').getElementsByTagName('tbody')[0];
|
|
246
|
+
Object.keys(apiSpec.paths).forEach(path => {
|
|
247
|
+
Object.keys(apiSpec.paths[path]).forEach(method => {
|
|
248
|
+
Object.keys(apiSpec.paths[path][method].responses).forEach(responseType => {
|
|
249
|
+
if(!Number.isNaN(parseInt(responseType))){
|
|
250
|
+
const row = tableBody.insertRow();
|
|
251
|
+
const cellPath = row.insertCell(0);
|
|
252
|
+
const cellMethod = row.insertCell(1);
|
|
253
|
+
const cellStatus = row.insertCell(2);
|
|
254
|
+
const cellDescription = row.insertCell(3);
|
|
255
|
+
const cellRequestCount = row.insertCell(4);
|
|
256
|
+
cellRequestCount.style="text-align: center;"
|
|
257
|
+
const cellAverageResponseTime = row.insertCell(5);
|
|
258
|
+
cellAverageResponseTime.style.textAlign = "center";
|
|
259
|
+
|
|
260
|
+
cellPath.textContent = path;
|
|
261
|
+
cellMethod.textContent = method.toUpperCase();
|
|
262
|
+
cellStatus.textContent = responseType;
|
|
263
|
+
cellDescription.textContent = apiSpec.paths[path][method].summary
|
|
264
|
+
+ " - "
|
|
265
|
+
+ apiSpec.paths[path][method].responses[responseType].description;
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
loadStats(path,method.toLowerCase(),responseType,cellRequestCount,cellAverageResponseTime);
|
|
269
|
+
setTimeout(loadStats,1000,path,method.toLowerCase(),responseType,cellRequestCount,cellAverageResponseTime);
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
row.detailPath = \`/telemetry/detail/\${responseType}/\${method.toLowerCase()}\${path}\`;
|
|
273
|
+
row.onclick = function(){
|
|
274
|
+
window.location.href = this.detailPath;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function sortTable(column) {
|
|
283
|
+
const table = document.getElementById('apiTable');
|
|
284
|
+
let rows, switching, i, x, y, shouldSwitch;
|
|
266
285
|
switching = true;
|
|
286
|
+
// Loop until no switching has been done:
|
|
287
|
+
while (switching) {
|
|
288
|
+
switching = false;
|
|
289
|
+
rows = table.rows;
|
|
290
|
+
// Loop through all table rows (except the first, which contains table headers):
|
|
291
|
+
for (i = 1; i < (rows.length - 1); i++) {
|
|
292
|
+
shouldSwitch = false;
|
|
293
|
+
// Get the two elements you want to compare, one from current row and one from the next:
|
|
294
|
+
x = rows[i].getElementsByTagName("TD")[column];
|
|
295
|
+
y = rows[i + 1].getElementsByTagName("TD")[column];
|
|
296
|
+
// Check if the two rows should switch place:
|
|
297
|
+
if (x.textContent.toLowerCase() > y.textContent.toLowerCase()) {
|
|
298
|
+
shouldSwitch = true;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (shouldSwitch) {
|
|
303
|
+
// If a switch has been marked, make the switch and mark that a switch has been done:
|
|
304
|
+
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
|
305
|
+
switching = true;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
267
308
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
</
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<
|
|
279
|
-
<
|
|
280
|
-
<meta
|
|
281
|
-
<
|
|
282
|
-
<
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
color: #333;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
</style>
|
|
354
|
-
</head>
|
|
355
|
-
<body>
|
|
356
|
-
<h1><span id="heading">Telemetry for...</span></h1>
|
|
357
|
-
<a href="/telemetry/">Back</a><br><br>
|
|
358
|
-
<table id="apiTable">
|
|
359
|
-
<thead>
|
|
360
|
-
<tr>
|
|
361
|
-
<th onclick="sortTable(0)">TimeStamp</th>
|
|
362
|
-
<th onclick="sortTable(1)">End point</th>
|
|
363
|
-
<th onclick="sortTable(2)">Method</th>
|
|
364
|
-
<th onclick="sortTable(3)">Status</th>
|
|
365
|
-
<th onclick="sortTable(4)" style="text-align: center;">Response time<br> (sec)</th>
|
|
366
|
-
</tr>
|
|
367
|
-
</thead>
|
|
368
|
-
<tbody>
|
|
369
|
-
</tbody>
|
|
370
|
-
</table>
|
|
371
|
-
<script>
|
|
372
|
-
|
|
373
|
-
let traceCount=-1;
|
|
374
|
-
|
|
375
|
-
function parsePath() {
|
|
376
|
-
|
|
377
|
-
let detailPath = window.location.pathname.split("/");
|
|
378
|
-
|
|
379
|
-
if(detailPath.length < 6 || detailPath[5] == ""){
|
|
380
|
-
alert("Wrong invocation params");
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
let status = parseInt(detailPath[3]);
|
|
385
|
-
|
|
386
|
-
if(Number.isNaN(status))
|
|
387
|
-
status = -1;
|
|
388
|
-
|
|
389
|
-
const method = detailPath[4];
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const path = decodeURI("/"+ detailPath
|
|
393
|
-
.splice(5,detailPath.length-5)
|
|
394
|
-
.filter(c=>(c != ""))
|
|
395
|
-
.join("/"));
|
|
396
|
-
|
|
397
|
-
headingObj = document.getElementById('heading');
|
|
398
|
-
headingObj.textContent = \`Telemetry for \${path} - \${method} - \${status} \`;
|
|
399
|
-
fetchTracesByParsing(path,method,status);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function getSearchQuery(path,method,status){
|
|
403
|
-
let pathComponents = path.split("/");
|
|
404
|
-
let pathRegex = "^"
|
|
405
|
-
|
|
406
|
-
pathComponents.forEach(c =>{
|
|
407
|
-
if(c != "") {
|
|
408
|
-
pathRegex += "/";
|
|
409
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
410
|
-
pathRegex += "(.*)";
|
|
411
|
-
}else{
|
|
412
|
-
pathRegex += c;
|
|
309
|
+
|
|
310
|
+
window.onload = fetchSpec();
|
|
311
|
+
</script>
|
|
312
|
+
</body>
|
|
313
|
+
</html>
|
|
314
|
+
`,
|
|
315
|
+
detail: `
|
|
316
|
+
|
|
317
|
+
<!DOCTYPE html>
|
|
318
|
+
<html lang="en">
|
|
319
|
+
<head>
|
|
320
|
+
<meta charset="UTF-8">
|
|
321
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
322
|
+
<title>OAS - Telemetry</title>
|
|
323
|
+
<style>
|
|
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;
|
|
340
|
+
}
|
|
341
|
+
.box {
|
|
342
|
+
width: 100%;
|
|
343
|
+
margin: 0 auto;
|
|
344
|
+
background: rgba(255,255,255,0.2);
|
|
345
|
+
padding: 35px;
|
|
346
|
+
border: 2px solid #fff;
|
|
347
|
+
border-radius: 20px/50px;
|
|
348
|
+
background-clip: padding-box;
|
|
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;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.popup {
|
|
371
|
+
margin: 70px auto;
|
|
372
|
+
padding: 20px;
|
|
373
|
+
background: #fff;
|
|
374
|
+
border-radius: 5px;
|
|
375
|
+
width: 70%;
|
|
376
|
+
position: relative;
|
|
377
|
+
transition: all 5s ease-in-out;
|
|
378
|
+
font-size: small;
|
|
379
|
+
overflow: scroll;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.popup .close {
|
|
383
|
+
position: absolute;
|
|
384
|
+
top: 20px;
|
|
385
|
+
right: 30px;
|
|
386
|
+
transition: all 200ms;
|
|
387
|
+
font-size: 30px;
|
|
388
|
+
font-weight: bold;
|
|
389
|
+
text-decoration: none;
|
|
390
|
+
color: #333;
|
|
413
391
|
}
|
|
392
|
+
|
|
393
|
+
</style>
|
|
394
|
+
</head>
|
|
395
|
+
<body>
|
|
396
|
+
<h1><span id="heading">Telemetry for...</span></h1>
|
|
397
|
+
<a href="/telemetry/">Back</a><br><br>
|
|
398
|
+
<table id="apiTable">
|
|
399
|
+
<thead>
|
|
400
|
+
<tr>
|
|
401
|
+
<th onclick="sortTable(0)">TimeStamp</th>
|
|
402
|
+
<th onclick="sortTable(1)">End point</th>
|
|
403
|
+
<th onclick="sortTable(2)">Method</th>
|
|
404
|
+
<th onclick="sortTable(3)">Status</th>
|
|
405
|
+
<th onclick="sortTable(4)" style="text-align: center;">Response time<br> (sec)</th>
|
|
406
|
+
</tr>
|
|
407
|
+
</thead>
|
|
408
|
+
<tbody>
|
|
409
|
+
</tbody>
|
|
410
|
+
</table>
|
|
411
|
+
<script>
|
|
412
|
+
|
|
413
|
+
let traceCount=-1;
|
|
414
|
+
let LOG=false;
|
|
415
|
+
|
|
416
|
+
function log(s){
|
|
417
|
+
if(LOG)
|
|
418
|
+
console.log(s);
|
|
414
419
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
async function fetchTracesByFinding(path,method,status) {
|
|
427
|
-
try {
|
|
428
|
-
const response = await fetch("/telemetry/find",{
|
|
429
|
-
method: 'POST',
|
|
430
|
-
headers: {
|
|
431
|
-
'Accept': 'application/json',
|
|
432
|
-
'Content-Type': 'application/json'
|
|
433
|
-
},
|
|
434
|
-
body: JSON.stringify({a: 1, b: 'Textual content'})
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
if (!response.ok) {
|
|
438
|
-
throw new Error("ERROR getting the Traces.");
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
const traces = await response.json();
|
|
442
|
-
loadTraces(traces);
|
|
443
|
-
|
|
444
|
-
} catch (error) {
|
|
445
|
-
console.error("ERROR getting the Traces :", error);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
function getPathRegEx(path){
|
|
450
|
-
let pathComponents = path.split("/");
|
|
451
|
-
let pathRegExpStr = "^"
|
|
452
|
-
|
|
453
|
-
pathComponents.forEach(c =>{
|
|
454
|
-
if(c != "") {
|
|
455
|
-
pathRegExpStr += "/";
|
|
456
|
-
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
457
|
-
pathRegExpStr += "(.*)";
|
|
458
|
-
}else{
|
|
459
|
-
pathRegExpStr += c;
|
|
420
|
+
|
|
421
|
+
function parsePath() {
|
|
422
|
+
|
|
423
|
+
let detailPath = window.location.pathname.split("/");
|
|
424
|
+
|
|
425
|
+
if(detailPath.length < 6 || detailPath[5] == ""){
|
|
426
|
+
alert("Wrong invocation params");
|
|
427
|
+
return;
|
|
460
428
|
}
|
|
429
|
+
|
|
430
|
+
let status = parseInt(detailPath[3]);
|
|
431
|
+
|
|
432
|
+
if(Number.isNaN(status))
|
|
433
|
+
status = -1;
|
|
434
|
+
|
|
435
|
+
const method = detailPath[4];
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
const path = decodeURI("/"+ detailPath
|
|
439
|
+
.splice(5,detailPath.length-5)
|
|
440
|
+
.filter(c=>(c != ""))
|
|
441
|
+
.join("/"));
|
|
442
|
+
|
|
443
|
+
headingObj = document.getElementById('heading');
|
|
444
|
+
headingObj.textContent = \`Telemetry for \${path} - \${method} - \${status} \`;
|
|
445
|
+
fetchTracesByParsing(path,method,status);
|
|
461
446
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
447
|
+
|
|
448
|
+
function getSearchQuery(path,method,status){
|
|
449
|
+
let pathComponents = path.split("/");
|
|
450
|
+
let pathRegex = "^"
|
|
451
|
+
|
|
452
|
+
pathComponents.forEach(c =>{
|
|
453
|
+
if(c != "") {
|
|
454
|
+
pathRegex += "/";
|
|
455
|
+
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
456
|
+
pathRegex += "(.*)";
|
|
457
|
+
}else{
|
|
458
|
+
pathRegex += c;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
pathRegex += "\$";
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
"attributes.http_dot_target" : { \$regex: new RegExp(pathRegex)},
|
|
467
|
+
"name" : method.toUpperCase(),
|
|
468
|
+
"attributes.http_dot_status_code" : status
|
|
469
|
+
};
|
|
477
470
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
471
|
+
|
|
472
|
+
async function fetchTracesByFinding(path,method,status) {
|
|
473
|
+
try {
|
|
474
|
+
const response = await fetch("/telemetry/find",{
|
|
475
|
+
method: 'POST',
|
|
476
|
+
headers: {
|
|
477
|
+
'Accept': 'application/json',
|
|
478
|
+
'Content-Type': 'application/json'
|
|
479
|
+
},
|
|
480
|
+
body: JSON.stringify({a: 1, b: 'Textual content'})
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
if (!response.ok) {
|
|
484
|
+
throw new Error("ERROR getting the Traces.");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const traces = await response.json();
|
|
488
|
+
loadTraces(traces);
|
|
489
|
+
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error("ERROR getting the Traces :", error);
|
|
492
|
+
}
|
|
495
493
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const cellEP = row.insertCell(1);
|
|
550
|
-
const cellMethod = row.insertCell(2);
|
|
551
|
-
cellMethod.style.textAlign = "center";
|
|
552
|
-
const cellStatus = row.insertCell(3);
|
|
553
|
-
cellStatus.style.textAlign = "center";
|
|
554
|
-
const cellDuration = row.insertCell(4);
|
|
555
|
-
cellDuration.style.textAlign = "center";
|
|
556
|
-
|
|
557
|
-
let t = parseTraceInfo(trace);
|
|
558
|
-
|
|
559
|
-
cellTS.textContent = t.ts;
|
|
560
|
-
cellEP.textContent = t.ep;
|
|
561
|
-
cellMethod.textContent = t.method;
|
|
562
|
-
cellStatus.textContent = t.status;
|
|
563
|
-
cellDuration.textContent = t.duration;
|
|
564
|
-
|
|
565
|
-
row.trace = trace;
|
|
566
|
-
row.onclick = function() {
|
|
567
|
-
const popup = document.getElementById("tracePopup");
|
|
568
|
-
popup.firstChild.nodeValue = JSON.stringify(this.trace,null,2);
|
|
569
|
-
const popupOverlay = document.getElementById("popupOverlay");
|
|
570
|
-
popupOverlay.style.visibility = "visible";
|
|
571
|
-
popupOverlay.style.opacity = 1;
|
|
572
|
-
};
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function sortTable(column) {
|
|
577
|
-
const table = document.getElementById('apiTable');
|
|
578
|
-
let rows, switching, i, x, y, shouldSwitch;
|
|
579
|
-
switching = true;
|
|
580
|
-
// Loop until no switching has been done:
|
|
581
|
-
while (switching) {
|
|
582
|
-
switching = false;
|
|
583
|
-
rows = table.rows;
|
|
584
|
-
// Loop through all table rows (except the first, which contains table headers):
|
|
585
|
-
for (i = 1; i < (rows.length - 1); i++) {
|
|
586
|
-
shouldSwitch = false;
|
|
587
|
-
// Get the two elements you want to compare, one from current row and one from the next:
|
|
588
|
-
x = rows[i].getElementsByTagName("TD")[column];
|
|
589
|
-
y = rows[i + 1].getElementsByTagName("TD")[column];
|
|
590
|
-
// Check if the two rows should switch place:
|
|
591
|
-
if (x.textContent.toLowerCase() > y.textContent.toLowerCase()) {
|
|
592
|
-
shouldSwitch = true;
|
|
593
|
-
break;
|
|
494
|
+
|
|
495
|
+
function getPathRegEx(path){
|
|
496
|
+
let pathComponents = path.split("/");
|
|
497
|
+
let pathRegExpStr = "^"
|
|
498
|
+
|
|
499
|
+
pathComponents.forEach(c =>{
|
|
500
|
+
if(c != "") {
|
|
501
|
+
pathRegExpStr += "/";
|
|
502
|
+
if(c.charAt(0) == "{" && c.charAt(c.length-1) == "}"){
|
|
503
|
+
pathRegExpStr += "(.*)";
|
|
504
|
+
}else{
|
|
505
|
+
pathRegExpStr += c;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
pathRegExpStr += "\$";
|
|
511
|
+
|
|
512
|
+
return new RegExp(pathRegExpStr);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function fetchTracesByParsing(path,method,status) {
|
|
516
|
+
try {
|
|
517
|
+
log(\`Fetchig traces for <\${path}> - \${method} - \${status},.. \`);
|
|
518
|
+
|
|
519
|
+
const response = await fetch("/telemetry/list");
|
|
520
|
+
|
|
521
|
+
if (!response.ok) {
|
|
522
|
+
throw new Error("ERROR getting the Traces.");
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const responseJSON = await response.json();
|
|
526
|
+
const traces = responseJSON.spans;
|
|
527
|
+
|
|
528
|
+
log(\`Feched \${traces.length} traces.\`);
|
|
529
|
+
//log(\`First trace: \${JSON.stringify(traces[0],null,2)}\`);
|
|
530
|
+
let filteredTraces = traces.filter((t)=>{
|
|
531
|
+
return (
|
|
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){
|
|
539
|
+
loadTraces(filteredTraces);
|
|
540
|
+
traceCount = filteredTraces.length;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
setTimeout(fetchTracesByParsing,1000,path,method,status);
|
|
544
|
+
|
|
545
|
+
} catch (error) {
|
|
546
|
+
console.error("ERROR getting the Traces :", error);
|
|
594
547
|
}
|
|
595
548
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
549
|
+
|
|
550
|
+
function calculateTiming(startSecInput,startNanoSecInput,endSecInput,endNanoSecInput,precision = 3){
|
|
551
|
+
// 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
|
+
|
|
561
|
+
let preciseStart = parseFloat(startSec + startNanoSecParsed);
|
|
562
|
+
let preciseEnd = parseFloat(endSec + endNanoSecParsed);
|
|
563
|
+
let preciseDuration = parseFloat(preciseEnd-preciseStart);
|
|
564
|
+
|
|
565
|
+
let startDate = new Date(preciseStart.toFixed(precision)*1000);
|
|
566
|
+
let startTS = startDate.toISOString();
|
|
567
|
+
|
|
568
|
+
let endDate = new Date(preciseEnd.toFixed(precision)*1000);
|
|
569
|
+
let endTS = endDate.toISOString();
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
preciseStart: preciseStart,
|
|
573
|
+
preciseEnd : preciseEnd,
|
|
574
|
+
preciseDuration : preciseDuration,
|
|
575
|
+
start : parseFloat(preciseStart.toFixed(precision)),
|
|
576
|
+
end: parseFloat(preciseEnd.toFixed(precision)),
|
|
577
|
+
duration : parseFloat(preciseDuration.toFixed(precision)),
|
|
578
|
+
startDate: startDate,
|
|
579
|
+
endDate: endDate,
|
|
580
|
+
startTS :startTS,
|
|
581
|
+
endTS: endTS
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function parseTraceInfo(t){
|
|
587
|
+
const ep = t.attributes.http_dot_target;
|
|
588
|
+
const method = t.attributes.http_dot_method.toLowerCase();
|
|
589
|
+
const status = t.attributes.http_dot_status_code;
|
|
590
|
+
|
|
591
|
+
const timing = calculateTiming(t.startTime[0],t.startTime[1],t.endTime[0],t.endTime[1]);
|
|
592
|
+
|
|
593
|
+
log(\`\${timing.startTS} - \${timing.endTS} - \${t._spanContext.traceId} - \${t.name} - \${ep} - \${status} - \${timing.duration}\`);
|
|
594
|
+
return {
|
|
595
|
+
ts : timing.startTS,
|
|
596
|
+
ep: ep,
|
|
597
|
+
method: method,
|
|
598
|
+
status: status,
|
|
599
|
+
duration: timing.duration
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function loadTraces(traces) {
|
|
604
|
+
|
|
605
|
+
const tableBody = document.getElementById('apiTable').getElementsByTagName('tbody')[0];
|
|
606
|
+
while (tableBody.hasChildNodes()) {
|
|
607
|
+
tableBody.removeChild(tableBody.lastChild);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
traces.forEach(trace => {
|
|
611
|
+
const row = tableBody.insertRow();
|
|
612
|
+
const cellTS = row.insertCell(0);
|
|
613
|
+
const cellEP = row.insertCell(1);
|
|
614
|
+
const cellMethod = row.insertCell(2);
|
|
615
|
+
cellMethod.style.textAlign = "center";
|
|
616
|
+
const cellStatus = row.insertCell(3);
|
|
617
|
+
cellStatus.style.textAlign = "center";
|
|
618
|
+
const cellDuration = row.insertCell(4);
|
|
619
|
+
cellDuration.style.textAlign = "center";
|
|
620
|
+
|
|
621
|
+
let t = parseTraceInfo(trace);
|
|
622
|
+
|
|
623
|
+
cellTS.textContent = t.ts;
|
|
624
|
+
cellEP.textContent = t.ep;
|
|
625
|
+
cellMethod.textContent = t.method;
|
|
626
|
+
cellStatus.textContent = t.status;
|
|
627
|
+
cellDuration.textContent = t.duration.toFixed(3);
|
|
628
|
+
|
|
629
|
+
row.trace = trace;
|
|
630
|
+
row.onclick = function() {
|
|
631
|
+
const popup = document.getElementById("tracePopup");
|
|
632
|
+
popup.firstChild.nodeValue = JSON.stringify(this.trace,null,2);
|
|
633
|
+
const popupOverlay = document.getElementById("popupOverlay");
|
|
634
|
+
popupOverlay.style.visibility = "visible";
|
|
635
|
+
popupOverlay.style.opacity = 1;
|
|
636
|
+
};
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function sortTable(column) {
|
|
641
|
+
const table = document.getElementById('apiTable');
|
|
642
|
+
let rows, switching, i, x, y, shouldSwitch;
|
|
599
643
|
switching = true;
|
|
644
|
+
// Loop until no switching has been done:
|
|
645
|
+
while (switching) {
|
|
646
|
+
switching = false;
|
|
647
|
+
rows = table.rows;
|
|
648
|
+
// Loop through all table rows (except the first, which contains table headers):
|
|
649
|
+
for (i = 1; i < (rows.length - 1); i++) {
|
|
650
|
+
shouldSwitch = false;
|
|
651
|
+
// Get the two elements you want to compare, one from current row and one from the next:
|
|
652
|
+
x = rows[i].getElementsByTagName("TD")[column];
|
|
653
|
+
y = rows[i + 1].getElementsByTagName("TD")[column];
|
|
654
|
+
// Check if the two rows should switch place:
|
|
655
|
+
if (x.textContent.toLowerCase() > y.textContent.toLowerCase()) {
|
|
656
|
+
shouldSwitch = true;
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (shouldSwitch) {
|
|
661
|
+
// If a switch has been marked, make the switch and mark that a switch has been done:
|
|
662
|
+
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
|
663
|
+
switching = true;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
600
666
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
<
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
"
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
"
|
|
636
|
-
"
|
|
637
|
-
"
|
|
638
|
-
"
|
|
639
|
-
"
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
1714196017,
|
|
645
|
-
860657322
|
|
646
|
-
],
|
|
647
|
-
"_ended": true,
|
|
648
|
-
"_duration": [
|
|
649
|
-
0,
|
|
650
|
-
1657322
|
|
651
|
-
],
|
|
652
|
-
"name": "GET",
|
|
653
|
-
"_spanContext": {
|
|
654
|
-
"traceId": "7963907d7be515617050ece544ab5c9e",
|
|
655
|
-
"spanId": "f5cec976b23725c5",
|
|
656
|
-
"traceFlags": 1
|
|
657
|
-
},
|
|
658
|
-
"kind": 1,
|
|
659
|
-
"_performanceStartTime": 211662.4864029996,
|
|
660
|
-
"_performanceOffset": -0.596435546875,
|
|
661
|
-
"_startTimeProvided": false,
|
|
662
|
-
"startTime": [
|
|
663
|
-
1714196017,
|
|
664
|
-
859000000
|
|
665
|
-
],
|
|
666
|
-
"resource": {
|
|
667
|
-
"_attributes": {
|
|
668
|
-
"service_dot_name": "unknown_service:/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
669
|
-
"telemetry_dot_sdk_dot_language": "nodejs",
|
|
670
|
-
"telemetry_dot_sdk_dot_name": "opentelemetry",
|
|
671
|
-
"telemetry_dot_sdk_dot_version": "1.22.0",
|
|
672
|
-
"process_dot_pid": 12568,
|
|
673
|
-
"process_dot_executable_dot_name": "/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
674
|
-
"process_dot_executable_dot_path": "/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
675
|
-
"process_dot_command_args": [
|
|
676
|
-
"/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
677
|
-
"/home/pafmon/devel/github/ot-ui-poc/index.js"
|
|
667
|
+
|
|
668
|
+
function hidePopup(){
|
|
669
|
+
const popupOverlay = document.getElementById("popupOverlay");
|
|
670
|
+
popupOverlay.style.visibility = "hidden";
|
|
671
|
+
popupOverlay.style.opacity = 0;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
window.onload = parsePath();
|
|
675
|
+
</script>
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
<div id="popupOverlay" class="overlay">
|
|
680
|
+
<div class="popup">
|
|
681
|
+
<pre id="tracePopup">
|
|
682
|
+
"attributes": {
|
|
683
|
+
"http_dot_url": "http://localhost:3000/api/v1/test/unknown",
|
|
684
|
+
"http_dot_host": "localhost:3000",
|
|
685
|
+
"net_dot_host_dot_name": "localhost",
|
|
686
|
+
"http_dot_method": "GET",
|
|
687
|
+
"http_dot_scheme": "http",
|
|
688
|
+
"http_dot_target": "/api/v1/test/unknown",
|
|
689
|
+
"http_dot_user_agent": "curl/7.68.0",
|
|
690
|
+
"http_dot_flavor": "1.1",
|
|
691
|
+
"net_dot_transport": "ip_tcp",
|
|
692
|
+
"net_dot_host_dot_ip": "::ffff:127.0.0.1",
|
|
693
|
+
"net_dot_host_dot_port": 3000,
|
|
694
|
+
"net_dot_peer_dot_ip": "::ffff:127.0.0.1",
|
|
695
|
+
"net_dot_peer_dot_port": 37718,
|
|
696
|
+
"http_dot_status_code": 404,
|
|
697
|
+
"http_dot_status_text": "NOT FOUND"
|
|
698
|
+
},
|
|
699
|
+
"links": [],
|
|
700
|
+
"events": [],
|
|
701
|
+
"_droppedAttributesCount": 0,
|
|
702
|
+
"_droppedEventsCount": 0,
|
|
703
|
+
"_droppedLinksCount": 0,
|
|
704
|
+
"status": {
|
|
705
|
+
"code": 0
|
|
706
|
+
},
|
|
707
|
+
"endTime": [
|
|
708
|
+
1714196017,
|
|
709
|
+
860657322
|
|
678
710
|
],
|
|
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
|
-
|
|
711
|
+
"_ended": true,
|
|
712
|
+
"_duration": [
|
|
713
|
+
0,
|
|
714
|
+
1657322
|
|
715
|
+
],
|
|
716
|
+
"name": "GET",
|
|
717
|
+
"_spanContext": {
|
|
718
|
+
"traceId": "7963907d7be515617050ece544ab5c9e",
|
|
719
|
+
"spanId": "f5cec976b23725c5",
|
|
720
|
+
"traceFlags": 1
|
|
721
|
+
},
|
|
722
|
+
"kind": 1,
|
|
723
|
+
"_performanceStartTime": 211662.4864029996,
|
|
724
|
+
"_performanceOffset": -0.596435546875,
|
|
725
|
+
"_startTimeProvided": false,
|
|
726
|
+
"startTime": [
|
|
727
|
+
1714196017,
|
|
728
|
+
859000000
|
|
729
|
+
],
|
|
730
|
+
"resource": {
|
|
731
|
+
"_attributes": {
|
|
732
|
+
"service_dot_name": "unknown_service:/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
733
|
+
"telemetry_dot_sdk_dot_language": "nodejs",
|
|
734
|
+
"telemetry_dot_sdk_dot_name": "opentelemetry",
|
|
735
|
+
"telemetry_dot_sdk_dot_version": "1.22.0",
|
|
736
|
+
"process_dot_pid": 12568,
|
|
737
|
+
"process_dot_executable_dot_name": "/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
738
|
+
"process_dot_executable_dot_path": "/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
739
|
+
"process_dot_command_args": [
|
|
740
|
+
"/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
741
|
+
"/home/pafmon/devel/github/ot-ui-poc/index.js"
|
|
742
|
+
],
|
|
743
|
+
"process_dot_runtime_dot_version": "18.0.0",
|
|
744
|
+
"process_dot_runtime_dot_name": "nodejs",
|
|
745
|
+
"process_dot_runtime_dot_description": "Node.js",
|
|
746
|
+
"process_dot_command": "/home/pafmon/devel/github/ot-ui-poc/index.js",
|
|
747
|
+
"process_dot_owner": "pafmon"
|
|
748
|
+
},
|
|
749
|
+
"asyncAttributesPending": false,
|
|
750
|
+
"_syncAttributes": {
|
|
751
|
+
"service_dot_name": "unknown_service:/home/pafmon/.nvm/versions/node/v18.0.0/bin/node",
|
|
752
|
+
"telemetry_dot_sdk_dot_language": "nodejs",
|
|
753
|
+
"telemetry_dot_sdk_dot_name": "opentelemetry",
|
|
754
|
+
"telemetry_dot_sdk_dot_version": "1.22.0"
|
|
755
|
+
},
|
|
756
|
+
"_asyncAttributesPromise": {}
|
|
757
|
+
},
|
|
758
|
+
"instrumentationLibrary": {
|
|
759
|
+
"name": "@opentelemetry/instrumentation-http",
|
|
760
|
+
"version": "0.51.0"
|
|
761
|
+
},
|
|
762
|
+
"_spanLimits": {
|
|
763
|
+
"attributeValueLengthLimit": null,
|
|
764
|
+
"attributeCountLimit": 128,
|
|
765
|
+
"linkCountLimit": 128,
|
|
766
|
+
"eventCountLimit": 128,
|
|
767
|
+
"attributePerEventCountLimit": 128,
|
|
768
|
+
"attributePerLinkCountLimit": 128
|
|
769
|
+
},
|
|
770
|
+
"_attributeValueLengthLimit": null,
|
|
771
|
+
"_spanProcessor": "oas-telemetry skips this field to avoid circular reference",
|
|
772
|
+
"_id": "3qYjJV2KdMa6zJw7"
|
|
773
|
+
</pre>
|
|
774
|
+
<a class="close" href="#" onclick="hidePopup()">×</a>
|
|
775
|
+
</div>
|
|
776
|
+
</div>
|
|
777
|
+
|
|
778
|
+
</body>
|
|
779
|
+
</html>
|
|
780
|
+
`
|
|
781
|
+
};
|
|
718
782
|
}
|
|
719
783
|
module.exports = exports.default;
|