1 <!DOCTYPE html>2 <html>3 <head>4 <title>Trace viewer</title>5 <style type="text/css">6 7 html {8 font-family: sans-serif;9 }10 11 .logs {12 font-family: monospace;13 }14 15 .logs .log-entry {16 font-family: monospace;17 white-space: pre-wrap;18 }19 20 .logs .log-entry[level="DEBUG"] { color: darkblue; }21 .logs .log-entry[level="INFO"] { color: green; }22 .logs .log-entry[level="WARNING"] { color: darkorange; }23 .logs .log-entry[level="ERROR"] { color: purple; }24 .logs .log-entry[level="FATAL"] { color: red; }25 26 .logs .log-entry[select="1"] { background-color: rgba(0,0,0,0.05); }27 .logs .log-entry[select="2"] { background-color: rgba(0,0,255,0.05); }28 .logs .log-entry[select="3"] { background-color: rgba(0,255,0,0.05); }29 .logs .log-entry[select="4"] { background-color: rgba(0,255,255,0.05); }30 .logs .log-entry[select="5"] { background-color: rgba(255,0,0,0.05); }31 .logs .log-entry[select="6"] { background-color: rgba(255,0,255,0.05); }32 .logs .log-entry[select="7"] { background-color: rgba(255,255,0,0.05); }33 .logs .log-entry[select="8"] { background-color: rgba(0,0,127,0.05); }34 .logs .log-entry[select="9"] { background-color: rgba(0,127,0,0.05); }35 .logs .log-entry[select="0"] { background-color: rgba(0,127,127,0.05); }36 37 .logs[select="1"] .log-entry[select="1"],38 .logs[select="2"] .log-entry[select="2"],39 .logs[select="3"] .log-entry[select="3"],40 .logs[select="4"] .log-entry[select="4"],41 .logs[select="5"] .log-entry[select="5"],42 .logs[select="6"] .log-entry[select="6"],43 .logs[select="7"] .log-entry[select="7"],44 .logs[select="8"] .log-entry[select="8"],45 .logs[select="9"] .log-entry[select="9"],46 .logs[select="0"] .log-entry[select="0"] {47 background-color: rgba(0,255,0,0.45);48 }49 50 .viewer {51 position: relative;52 _border: solid silver 1px;53 height: 200px;54 }55 56 .viewer .span {57 background-color: rgba(200,220,255,0.6);58 position: absolute;59 height: 16px;60 cursor: pointer;61 text-align: center;62 font-size: 10px;63 line-height: 16px;64 text-overflow: ellipsis;65 overflow: hidden;66 white-space: nowrap;67 font-family: monospace;68 transition: all 0.5s ease;69 }70 71 .viewer[span_parallel="true"] .span[paralell_index="0"] { top: 0px !important; }72 .viewer[span_parallel="true"] .span[paralell_index="1"] { top: 18px !important; }73 .viewer[span_parallel="true"] .span[paralell_index="2"] { top: 36px !important; }74 .viewer[span_parallel="true"] .span[paralell_index="3"] { top: 0px !important; }75 .viewer[span_parallel="true"] .span[paralell_index="4"] { top: 0px !important; }76 .viewer[span_parallel="true"] .span[paralell_index="5"] { top: 0px !important; }77 .viewer[span_parallel="true"] .span[paralell_index="6"] { top: 0px !important; }78 .viewer[span_parallel="true"] .span[paralell_index="7"] { top: 0px !important; }79 .viewer[span_parallel="true"] .span[paralell_index="8"] { top: 0px !important; }80 .viewer[span_parallel="true"] .span[paralell_index="9"] { top: 0px !important; }81 .viewer[span_parallel="true"] .span[paralell_index="10"] { top: 0px !important; }82 83 .viewer .span:hover {84 box-shadow: 0 0 1px blue;85 } 86 87 .traces [trace_id] {88 font-family: monospace;89 white-space: pre-wrap;90 cursor: pointer;91 }92 93 .traces [trace_id]:hover {94 color: blue;95 }96 97 </style>98 </head>99 <body>100 101 <h1>Trace viewer </h1>102 103 <div>Spans parallel <input id="checkbox_spans_parallel" type="checkbox"></div>104 105 <div class="viewer" id="viewer"></div>106 107 <div style="float:left; width: 340px;">108 <h2>Traces</h2>109 <div>Paste your traces here <textarea id="input_traces" rows="1"></textarea></div>110 <div class="traces" id="div_traces"></div> 111 </div>112 113 <div style="margin-left: 340px;"> 114 <h2>Logs</h2>115 <div class="logs" id="div_logs"></div>116 117 <!--118 <h2>Spare logs</h2>119 <div class="logs" id="spare_logs"></div>120 -->121 </div>122 123 <script type="text/javascript">124 125 var checkbox_spans_parallel = document.getElementById('checkbox_spans_parallel');126 checkbox_spans_parallel.addEventListener('click', function() {127 var viewer = document.getElementById('viewer');128 viewer.setAttribute('span_parallel', this.checked);129 }, true);130 131 132 var lines = [];133 134 var add_line = function(line) {135 lines.push(line);136 };137 138 var span_parallell = true;139 140 var traces = {};141 142 var add_trace = function(line) {143 144 var parts = line.split('|');145 146 if ('TRACE.CREATE' == parts[1]) {147 var details = JSON.parse(parts[4]);148 var traceId = details.traceId;149 150 if (traceId in traces) {151 return true; // Already created :D152 }153 154 var trace = {155 id: traceId,156 namespace: parts[2],157 timestamp: parts[3],158 spans: {},159 }160 traces[traceId] = trace;161 162 print_trace_index(trace);163 164 return true; // Trace created :D165 166 } else if ('TRACE.SPAN.START' == parts[1]) {167 // "V1|TRACE.SPAN.START|ns|ac115589-d06d-49c7-81d5-525eedb48989|1517055732001191061|{"spanId":"71e51a26-c5d8-4de1-a258-463ca8f167af","monitoredResource":{"_id":"59bb80e520988c09f76c8e1f"},"startTime":1517055732001191061,"name":"","kind":"HTTP_SERVER","details":{"protocol":"http"}}"168 169 var traceId = parts[3];170 171 var trace = traces[traceId];172 if (!trace) {173 return false;174 }175 176 var details = JSON.parse(parts[5]);177 178 var spanId = details.spanId;179 180 var span = trace.spans[spanId];181 if (!span) {182 details.logs = [];183 trace.spans[spanId] = details;184 }185 186 return true;187 188 } else if ('TRACE.SPAN.FINISH' == parts[1]) {189 // "V1|TRACE.SPAN.FINISH|ns|ac115589-d06d-49c7-81d5-525eedb48989|1517055732005564980|{"spanId":"71e51a26-c5d8-4de1-a258-463ca8f167af","monitoredResource":{"_id":"59bb80e520988c09f76c8e1f"},"endTime":1517055732005500355,"details":{"http_response_status":201},"properties":{"handler":"/v0/organizations/{organization}:createNamespace","method":"POST","path":"/v0/organizations/jj:createNamespace","url":"/v0/organizations/jj:createNamespace"}}"190 191 var traceId = parts[3];192 193 var trace = traces[traceId];194 if (!trace) {195 return false;196 }197 198 var details = JSON.parse(parts[5]);199 200 var spanId = details.spanId;201 202 var span = trace.spans[spanId];203 if (!span) {204 console.log('span not started :_( ');205 return false;206 }207 208 span.endTime = details.endTime;209 span.properties = details.properties;210 span.details = details.details;211 212 return true;213 }214 215 };216 217 var print_trace_index = function(trace) {218 var parent = document.getElementById('div_traces');219 220 var div = document.createElement('div');221 div.setAttribute('trace_id', trace.id);222 div.textContent = trace.id; 223 div.addEventListener('click', function(e) {224 var trace_id = this.getAttribute('trace_id');225 print_trace(trace_id);226 }, true);227 228 parent.appendChild(div);229 };230 231 232 var pretty_nanos = function(nanos) {233 234 var power = 1000;235 var units = ['ns', 'µs', 'ms'];236 var i = 0;237 for (; nanos > power;) {238 i++;239 nanos /= power;240 }241 242 return nanos.toFixed(1) + units[i];243 244 };245 246 var print_trace = function(traceId) {247 248 var viewer = document.getElementById('viewer');249 viewer.innerHTML = '';250 251 var div_logs = document.getElementById('div_logs');252 div_logs.innerHTML = '';253 254 var trace = traces[traceId];255 256 var timestamp_min = null;257 var timestamp_max = null;258 259 var span2color={};260 var color2span={};261 var spanCounter=0;262 263 for (var spanId in trace.spans) {264 var span = trace.spans[spanId];265 266 if (null === timestamp_min || timestamp_min>span.startTime) {267 timestamp_min = span.startTime;268 }269 270 if (null === timestamp_max || timestamp_max<span.endTime) {271 timestamp_max = span.endTime;272 }273 274 var color = span2color[spanId]275 if (!color) {276 span2color[spanId] = spanCounter;277 color2span[spanCounter] = spanId;278 spanCounter++;279 }280 281 }282 283 var timestamp_range = timestamp_max - timestamp_min;284 285 var span_deep = function(span) {286 287 var MAX_DEEP = 100;288 289 for (var deep = 0; deep<MAX_DEEP; deep++) {290 291 var parentSpanId = span.parentSpanId;292 if (!parentSpanId) {293 return deep;294 }295 296 span = trace.spans[parentSpanId];297 if (!span) {298 return deep;299 }300 }301 302 return undefined;303 };304 305 var logs = [];306 var i=0;307 for (var spanId in trace.spans) {308 var span = trace.spans[spanId];309 310 var span_delay = span.endTime-span.startTime;311 312 span.logs.map(function(log) {logs.push(log); });313 314 var left = (100*(span.startTime-timestamp_min)/timestamp_range).toFixed(2)315 316 var div = document.createElement('div');317 div.setAttribute('span_id', span.spanId);318 div.className = 'span';319 div.style.top = i*18 + 'px';320 div.setAttribute('paralell_index', span_deep(span));321 /*322 if (span_parallell) {323 div.style.top = span_deep(span)*18 + 'px';324 } else {325 div.style.top = i*18 + 'px';326 }*/327 div.style.left = left + '%';328 div.style.width = 100*(span.endTime-span.startTime)/timestamp_range + '%';329 div.addEventListener('mouseover', function(e){330 div_logs.setAttribute('select', span2color[this.getAttribute('span_id')] );331 }, true);332 333 console.log(span);334 335 var content = '(' + pretty_nanos(span_delay) + ') ';336 if (span.logs.length) {337 content += span.logs[0].message;338 }339 340 div.textContent = content;341 342 viewer.appendChild(div);343 344 i++;345 }346 347 // TODO: sort logs by timestamp...348 logs.sort( (a,b) => a.timestamp - b.timestamp );349 350 logs.map(function(log) {351 var div = document.createElement('div');352 div.className = 'log-entry';353 div.setAttribute('level', log.level);354 div.setAttribute('span_id', log.spanId);355 div.setAttribute('select', span2color[log.spanId]);356 div.textContent = (new Date(log.timestamp/1000000))+ 't' +log.level + 't' + log.message;357 div_logs.appendChild(div);358 });359 360 };361 362 var spare_logs = document.getElementById('spare_logs');363 364 var add_log = function(log) {365 366 //V1|LOG.INFO|ns|59bb80e520988c09f76c8e1f|7b35c96c-7405-4705-bba2-417e5f6415dc|78c84606-f966-41f1-8509-fc71c473e0d8|1517069566344381262|Response status: '200 OK'367 368 var parts = log.split('|');369 370 var traceId = parts[4];371 372 var trace = traces[traceId];373 if (!trace) {374 return false;375 }376 377 var spanId = parts[5];378 var span = trace.spans[spanId];379 if (!span) {380 return false;381 }382 383 var level = parts[1].split('.')[1];384 var message = parts[7];385 386 span.logs.push({387 spanId: spanId,388 level: level,389 message: message,390 timestamp: parts[6],391 });392 393 return true;394 };395 396 var input_traces = document.getElementById('input_traces');397 input_traces.addEventListener('paste', function(e) {398 var text = e.clipboardData.getData('text');399 process_text(text);400 }, true);401 402 input_traces.addEventListener('keyup', function(e) {403 if (13 == e.keyCode) {404 process_text(this.value);405 }406 }, true);407 408 var process_text = function(text) {409 var lines = text.split('n');410 lines.map(add_line);411 process_lines();412 };413 414 var process_lines = function() {415 var n = 1;416 while (n) {417 n = process_lines_iterative();418 }419 };420 421 var process_lines_iterative = function() {422 423 var remove = [];424 425 for (var i in lines) {426 var line = lines[i];427 428 var processed = false;429 if (line.startsWith('V1|LOG')) {430 processed = add_log(line);431 } else if (line.startsWith('V1|TRACE')) {432 processed = add_trace(line);433 }434 435 if (processed) {436 remove.push(i);437 }438 }439 440 for (var i=remove.length-1; i>=0; i--) {441 var j = remove[i];442 lines.splice(j, 1);443 }444 445 return remove.length;446 };447 448 </script>449 450 </body>451 </html>
Enlace
El enlace para compartir es: