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