github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/static/js/search.js (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 'use strict'; 18 19 20 function wsURL(path) { 21 var protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; 22 var url = protocol + location.host; 23 return url + path; 24 } 25 26 function doCancel(data) { 27 socket.send(JSON.stringify(data)); 28 $('body').css('cursor', 'default'); 29 $('#run-filter-btn').html(' '); 30 $("#run-filter-btn").removeClass("cancel-search"); 31 $('#run-filter-btn').removeClass('active'); 32 $("#query-builder-btn").html(" "); 33 $("#query-builder-btn").removeClass("cancel-search"); 34 $("#query-builder-btn").removeClass("active"); 35 $('#progress-div').html(``); 36 } 37 function doLiveTailCancel(data) { 38 $("body").css("cursor", "default"); 39 $("#live-tail-btn").html("Live Tail"); 40 $("#live-tail-btn").removeClass("active"); 41 $("#progress-div").html(``); 42 } 43 function resetDataTable(firstQUpdate) { 44 if (firstQUpdate) { 45 $('#empty-response').hide(); 46 $("#custom-chart-tab").show(); 47 let currentTab = $("#custom-chart-tab").tabs("option", "active"); 48 if (currentTab == 0) { 49 $("#logs-view-controls").show(); 50 } else { 51 $("#logs-view-controls").hide(); 52 } 53 $("#agg-result-container").hide(); 54 $("#data-row-container").hide(); 55 hideError(); 56 } 57 } 58 59 function resetLogsGrid(firstQUpdate){ 60 if (firstQUpdate){ 61 resetAvailableFields(); 62 } 63 } 64 65 function doSearch(data) { 66 startQueryTime = (new Date()).getTime(); 67 newUri = wsURL("/api/search/ws"); 68 socket = new WebSocket(newUri); 69 let timeToFirstByte = 0; 70 let firstQUpdate = true; 71 let lastKnownHits = 0; 72 socket.onopen = function (e) { 73 console.time("socket timing"); 74 $('body').css('cursor', 'progress'); 75 $("#run-filter-btn").addClass("cancel-search"); 76 $('#run-filter-btn').addClass('active'); 77 $("#query-builder-btn").html(" "); 78 $("#query-builder-btn").addClass("cancel-search"); 79 $("#query-builder-btn").addClass("active"); 80 socket.send(JSON.stringify(data)); 81 }; 82 83 socket.onmessage = function (event) { 84 let jsonEvent = JSON.parse(event.data); 85 let eventType = jsonEvent.state; 86 let totalEventsSearched = jsonEvent.total_events_searched 87 let totalTime = (new Date()).getTime() - startQueryTime; 88 switch (eventType) { 89 case "RUNNING": 90 console.time("RUNNING"); 91 console.timeEnd("RUNNING"); 92 break; 93 case "QUERY_UPDATE": 94 console.time("QUERY_UPDATE"); 95 if (timeToFirstByte === 0) { 96 timeToFirstByte = Number(totalTime).toLocaleString(); 97 } 98 let totalHits; 99 100 if (jsonEvent && jsonEvent.hits && jsonEvent.hits.totalMatched) { 101 totalHits = jsonEvent.hits.totalMatched 102 totalMatchLogs = totalHits; 103 lastKnownHits = totalHits; 104 } else { 105 // we enter here only because backend sent null hits/totalmatched 106 totalHits = lastKnownHits 107 } 108 resetDataTable(firstQUpdate); 109 processQueryUpdate(jsonEvent, eventType, totalEventsSearched, timeToFirstByte, totalHits); 110 console.timeEnd("QUERY_UPDATE"); 111 firstQUpdate = false 112 break; 113 case "COMPLETE": 114 let eqRel = "eq"; 115 if (jsonEvent.totalMatched != null && jsonEvent.totalMatched.relation != null) { 116 eqRel = jsonEvent.totalMatched.relation; 117 } 118 console.time("COMPLETE"); 119 canScrollMore = jsonEvent.can_scroll_more; 120 scrollFrom = jsonEvent.total_rrc_count; 121 processCompleteUpdate(jsonEvent, eventType, totalEventsSearched, timeToFirstByte, eqRel); 122 console.timeEnd("COMPLETE"); 123 socket.close(1000); 124 break; 125 case "TIMEOUT": 126 console.time("TIMEOUT"); 127 console.log(`[message] Timeout state received from server: ${jsonEvent}`); 128 processTimeoutUpdate(jsonEvent); 129 console.timeEnd("TIMEOUT"); 130 break; 131 case "ERROR": 132 console.time("ERROR"); 133 console.log(`[message] Error state received from server: ${jsonEvent}`); 134 processErrorUpdate(jsonEvent); 135 console.timeEnd("ERROR"); 136 break; 137 default: 138 console.log(`[message] Unknown state received from server: `+ JSON.stringify(jsonEvent)); 139 if (jsonEvent.message.includes("expected")){ 140 jsonEvent.message = "Your query contains syntax error" 141 } else if (jsonEvent.message.includes("not present")){ 142 jsonEvent['no_data_err'] = "No data found for the query" 143 } 144 processSearchErrorLog(jsonEvent); 145 } 146 }; 147 148 socket.onclose = function (event) { 149 if (event.wasClean) { 150 console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); 151 } else { 152 console.log(`Connection close not clean=${event} code=${event.code} reason=${event.reason} `); 153 } 154 console.timeEnd("socket timing"); 155 }; 156 157 socket.addEventListener('error', (event) => { 158 console.log('WebSocket error: ', event); 159 }); 160 } 161 162 function reconnect() { 163 if (lockReconnect) { 164 return; 165 } 166 lockReconnect = true; 167 //keep reconnect,set delay to avoid much request, set tt, cancel first, then reset 168 clearInterval(tt); 169 tt = setInterval(function () { 170 if (!liveTailState) { 171 lockReconnect = false; 172 return; 173 } 174 data = getLiveTailFilter(false, false, 30); 175 createLiveTailSocket(data); 176 lockReconnect = false; 177 }, refreshInterval); 178 } 179 180 function createLiveTailSocket(data) { 181 try { 182 if (!liveTailState) return; 183 startQueryTime = new Date().getTime(); 184 newUri = wsURL("/api/search/live_tail"); 185 socket = new WebSocket(newUri); 186 doLiveTailSearch(data); 187 } catch (e) { 188 console.log("live tail connect websocket catch: " + e); 189 reconnect(); 190 } 191 } 192 function doLiveTailSearch(data) { 193 let timeToFirstByte = 0; 194 let firstQUpdate = true; 195 let lastKnownHits = 0; 196 socket.onopen = function (e) { 197 // console.time("socket timing"); 198 $("body").css("cursor", "progress"); 199 $("#live-tail-btn").html("Cancel Live Tail"); 200 $("#live-tail-btn").addClass("active"); 201 socket.send(JSON.stringify(data)); 202 }; 203 204 socket.onmessage = function (event) { 205 let jsonEvent = JSON.parse(event.data); 206 let eventType = jsonEvent.state; 207 if ( 208 jsonEvent && 209 jsonEvent.total_events_searched && 210 jsonEvent.total_events_searched != 0 211 ) { 212 total_liveTail_searched = jsonEvent.total_events_searched; 213 } 214 let totalEventsSearched = total_liveTail_searched; 215 let totalTime = new Date().getTime() - startQueryTime; 216 switch (eventType) { 217 case "RUNNING": 218 console.time("RUNNING"); 219 console.timeEnd("RUNNING"); 220 break; 221 case "QUERY_UPDATE": 222 console.time("QUERY_UPDATE"); 223 if (timeToFirstByte === 0) { 224 timeToFirstByte = Number(totalTime).toLocaleString(); 225 } 226 let totalHits; 227 228 if (jsonEvent && jsonEvent.hits && jsonEvent.hits.totalMatched) { 229 totalHits = jsonEvent.hits.totalMatched; 230 lastKnownHits = totalHits; 231 } else { 232 // we enter here only because backend sent null hits/totalmatched 233 totalHits = lastKnownHits; 234 } 235 resetDataTable(firstQUpdate); 236 processLiveTailQueryUpdate( 237 jsonEvent, 238 eventType, 239 totalEventsSearched, 240 timeToFirstByte, 241 totalHits 242 ); 243 // console.timeEnd("QUERY_UPDATE"); 244 firstQUpdate = false; 245 break; 246 case "COMPLETE": 247 let eqRel = "eq"; 248 if ( 249 jsonEvent.totalMatched != null && 250 jsonEvent.totalMatched.relation != null 251 ) { 252 eqRel = jsonEvent.totalMatched.relation; 253 } 254 console.time("COMPLETE"); 255 console.log(new Date().getTime()); 256 canScrollMore = jsonEvent.can_scroll_more; 257 scrollFrom = jsonEvent.total_rrc_count; 258 processLiveTailCompleteUpdate( 259 jsonEvent, 260 eventType, 261 totalEventsSearched, 262 timeToFirstByte, 263 eqRel 264 ); 265 console.timeEnd("COMPLETE"); 266 socket.close(1000); 267 break; 268 case "TIMEOUT": 269 console.time("TIMEOUT"); 270 console.log( 271 `[message] Timeout state received from server: ${jsonEvent}` 272 ); 273 processTimeoutUpdate(jsonEvent); 274 console.timeEnd("TIMEOUT"); 275 break; 276 case "ERROR": 277 console.time("ERROR"); 278 console.log( 279 `[message] Error state received from server: ${jsonEvent}` 280 ); 281 processErrorUpdate(jsonEvent); 282 console.timeEnd("ERROR"); 283 break; 284 default: 285 console.log( 286 `[message] Unknown state received from server: ` + 287 JSON.stringify(jsonEvent) 288 ); 289 if (jsonEvent.message.includes("expected")) { 290 jsonEvent.message = "Your query contains syntax error"; 291 } else if (jsonEvent.message.includes("not present")) { 292 jsonEvent["no_data_err"] = "No data found for the query"; 293 } 294 processSearchErrorLog(jsonEvent); 295 } 296 }; 297 298 socket.onclose = function (event) { 299 if (liveTailState) { 300 reconnect(); 301 console.log("live tail reconnect websocket"); 302 } else { 303 console.log("stop reconnect live tail"); 304 if (event.wasClean) { 305 console.log( 306 `[close] Connection closed cleanly, code=${event.code} reason=${event.reason}` 307 ); 308 } else { 309 console.log( 310 `Connection close not clean=${event} code=${event.code} reason=${event.reason} ` 311 ); 312 } 313 console.timeEnd("socket timing"); 314 } 315 }; 316 317 socket.addEventListener("error", (event) => { 318 console.log("WebSocket error: ", event); 319 }); 320 } 321 function getInitialSearchFilter(skipPushState, scrollingTrigger) { 322 let queryParams = new URLSearchParams(window.location.search); 323 let stDate = queryParams.get("startEpoch") || Cookies.get('startEpoch') || "now-15m"; 324 let endDate = queryParams.get("endEpoch") || Cookies.get('endEpoch') || "now"; 325 let selIndexName = queryParams.get('indexName'); 326 let queryLanguage = queryParams.get("queryLanguage") ||$('#query-language-btn span').html(); 327 queryLanguage = queryLanguage.replace('"', ''); 328 $("#query-language-btn span").html(queryLanguage); 329 $(".query-language-option").removeClass("active"); 330 if (queryLanguage == "SQL") { 331 $("#option-1").addClass("active"); 332 } else if (queryLanguage == "Log QL") { 333 $("#option-2").addClass("active"); 334 } else if (queryLanguage == "Splunk QL") { 335 $("#option-3").addClass("active"); 336 } 337 let filterTab = queryParams.get("filterTab"); 338 let filterValue = queryParams.get('searchText'); 339 if(filterTab == "0" || filterTab == null){ 340 if(filterValue != "*"){ 341 if(filterValue.indexOf("|") != -1){ 342 firstBoxSet = new Set(filterValue.split(" | ")[0].split(" ")); 343 secondBoxSet = new Set( 344 filterValue 345 .split("stats ")[1] 346 .split(" BY")[0] 347 .split(/(?=[A-Z])/) 348 ); 349 if (filterValue.includes(" BY ")) { 350 thirdBoxSet = new Set(filterValue.split(" BY ")[1].split(",")); 351 } 352 }else{ 353 firstBoxSet = new Set(filterValue.split(" ")); 354 } 355 if (firstBoxSet && firstBoxSet.size > 0) { 356 let tags = document.getElementById("tags"); 357 while (tags.firstChild) { 358 tags.removeChild(tags.firstChild); 359 } 360 firstBoxSet.forEach((value, i) => { 361 let tag = document.createElement("li"); 362 tag.innerText = value; 363 // Add a delete button to the tag 364 tag.innerHTML += '<button class="delete-button">x</button>'; 365 // Append the tag to the tags list 366 tags.appendChild(tag); 367 }); 368 } 369 if (secondBoxSet && secondBoxSet.size > 0) { 370 let tags = document.getElementById("tags-second"); 371 while (tags.firstChild) { 372 tags.removeChild(tags.firstChild); 373 } 374 secondBoxSet.forEach((value, i) => { 375 let tag = document.createElement("li"); 376 tag.innerText = value; 377 // Add a delete button to the tag 378 tag.innerHTML += '<button class="delete-button">x</button>'; 379 // Append the tag to the tags list 380 tags.appendChild(tag); 381 }); 382 } 383 if (thirdBoxSet && thirdBoxSet.size > 0) { 384 let tags = document.getElementById("tags-third"); 385 while (tags.firstChild) { 386 tags.removeChild(tags.firstChild); 387 } 388 thirdBoxSet.forEach((value, i) => { 389 let tag = document.createElement("li"); 390 tag.innerText = value; 391 // Add a delete button to the tag 392 tag.innerHTML += '<button class="delete-button">x</button>'; 393 // Append the tag to the tags list 394 395 tags.appendChild(tag); 396 }); 397 } 398 } 399 $("#query-input").val(filterValue); 400 }else{ 401 $("#custom-code-tab").tabs("option", "active", 1); 402 if (filterValue === "*") { 403 $("#filter-input").val("").change(); 404 } else { 405 $("#filter-input").val(filterValue).change(); 406 } 407 } 408 let sFrom = 0; 409 if(selIndexName!==null){ 410 if (selIndexName.length === 0){ 411 selIndexName = "*" 412 } 413 }else{ 414 selIndexName = "*" 415 } 416 selIndexName.split(',').forEach(function(searchVal){ 417 $(`.index-dropdown-item[data-index="${searchVal}"]`).toggleClass('active'); 418 }); 419 420 selectedSearchIndex = selIndexName.split(",").join(","); 421 Cookies.set('IndexList', selIndexName.split(",").join(",")); 422 423 if (!isNaN(stDate)) { 424 stDate = Number(stDate); 425 endDate = Number(endDate); 426 datePickerHandler(stDate, endDate, "custom"); 427 loadCustomDateTimeFromEpoch(stDate,endDate); 428 } else if (stDate !== "now-15m") { 429 datePickerHandler(stDate, endDate, stDate); 430 } else { 431 datePickerHandler(stDate, endDate, ""); 432 } 433 434 selectedSearchIndex = selIndexName; 435 if (!skipPushState) { 436 addQSParm("searchText", filterValue); 437 addQSParm("startEpoch", stDate); 438 addQSParm("endEpoch", endDate); 439 addQSParm("indexName", selIndexName); 440 addQSParm("queryLanguage", queryLanguage); 441 window.history.pushState({ path: myUrl }, '', myUrl); 442 } 443 444 if (scrollingTrigger){ 445 sFrom = scrollFrom; 446 } 447 448 449 return { 450 'state': 'query', 451 'searchText': filterValue, 452 'startEpoch': stDate, 453 'endEpoch': endDate, 454 'indexName': selIndexName, 455 'from' : sFrom, 456 'queryLanguage' : queryLanguage, 457 }; 458 } 459 function getLiveTailFilter(skipPushState, scrollingTrigger, startTime) { 460 let filterValue = $("#filter-input").val().trim() || "*"; 461 let endDate = "now"; 462 let date = new Date(); 463 let stDate = new Date(date.getTime() - startTime * 1000).getTime(); 464 if (startTime == 1800) stDate = "now-1h"; 465 let selIndexName = selectedSearchIndex; 466 let sFrom = 0; 467 let queryLanguage = $("#query-language-btn span").html(); 468 469 selIndexName.split(",").forEach(function (searchVal) { 470 $(`.index-dropdown-item[data-index="${searchVal}"]`).toggleClass( 471 "active" 472 ); 473 }); 474 475 selectedSearchIndex = selIndexName.split(",").join(","); 476 Cookies.set("IndexList", selIndexName.split(",").join(",")); 477 478 addQSParm("searchText", filterValue); 479 addQSParm("startEpoch", stDate); 480 addQSParm("endEpoch", endDate); 481 addQSParm("indexName", selIndexName); 482 addQSParm("queryLanguage", queryLanguage); 483 484 window.history.pushState({ path: myUrl }, "", myUrl); 485 486 if (scrollingTrigger) { 487 sFrom = scrollFrom; 488 } 489 490 return { 491 state: wsState, 492 searchText: filterValue, 493 startEpoch: stDate, 494 endEpoch: endDate, 495 indexName: selIndexName, 496 from: sFrom, 497 queryLanguage: queryLanguage, 498 }; 499 } 500 let filterTextQB = ""; 501 /** 502 * get real time search text 503 * @returns real time search text 504 */ 505 function getQueryBuilderCode() { 506 let filterValue = ""; 507 //concat the first input box 508 let index = 0; 509 if (firstBoxSet && firstBoxSet.size > 0) { 510 firstBoxSet.forEach((value, i) => { 511 if (index != firstBoxSet.size - 1) filterValue += value + " "; 512 else filterValue += value; 513 index++; 514 }); 515 }else{ 516 filterValue = '*'; 517 } 518 index = 0; 519 let bothRight = 0; 520 let showError = false; 521 //concat the second input box 522 if (secondBoxSet && secondBoxSet.size > 0) { 523 bothRight++; 524 filterValue += " | stats"; 525 secondBoxSet.forEach((value, i) => { 526 if (index != secondBoxSet.size - 1) filterValue += " " + value + ","; 527 else filterValue += " " + value; 528 index++; 529 }); 530 } 531 index = 0; 532 if (thirdBoxSet && thirdBoxSet.size > 0) { 533 if(bothRight == 0) showError = true; 534 //concat the third input box 535 filterValue += " BY"; 536 thirdBoxSet.forEach((value, i) => { 537 if (index != thirdBoxSet.size - 1) filterValue += " " + value + ","; 538 else filterValue += " " + value; 539 index++; 540 }); 541 } 542 if (filterValue == "") filterValue = "*"; 543 $("#query-input").val(filterValue); 544 if(thirdBoxSet && thirdBoxSet.size > 0 && (secondBoxSet == null || secondBoxSet.size == 0)) $("#query-builder-btn").addClass("stop-search").prop('disabled', true); 545 else $("#query-builder-btn").removeClass("stop-search").prop('disabled', false); 546 return showError ? "Searches with a Search Criteria must have an Aggregate Attribute" : filterValue; 547 } 548 function getSearchFilter(skipPushState, scrollingTrigger) { 549 let currentTab = $("#custom-code-tab").tabs("option", "active"); 550 let endDate = filterEndDate || "now"; 551 let stDate = filterStartDate || "now-15m"; 552 let selIndexName = selectedSearchIndex; 553 let sFrom = 0; 554 let queryLanguage = $("#query-language-btn span").html(); 555 556 selIndexName.split(",").forEach(function (searchVal) { 557 $(`.index-dropdown-item[data-index="${searchVal}"]`).toggleClass("active"); 558 }); 559 560 selectedSearchIndex = selIndexName.split(",").join(","); 561 Cookies.set("IndexList", selIndexName.split(",").join(",")); 562 563 if (!isNaN(stDate)) { 564 datePickerHandler(Number(stDate), Number(endDate), "custom"); 565 } else if (stDate !== "now-15m") { 566 datePickerHandler(stDate, endDate, stDate); 567 } else { 568 datePickerHandler(stDate, endDate, ""); 569 } 570 let filterValue = ""; 571 if(currentTab == 0){ 572 queryLanguage = "Splunk QL"; 573 //concat the 3 input boxes 574 filterValue = getQueryBuilderCode(); 575 isQueryBuilderSearch = true; 576 }else{ 577 filterValue = $("#filter-input").val().trim() || "*"; 578 isQueryBuilderSearch = false; 579 } 580 addQSParm("searchText", filterValue); 581 addQSParm("startEpoch", stDate); 582 addQSParm("endEpoch", endDate); 583 addQSParm("indexName", selIndexName); 584 addQSParm("queryLanguage", queryLanguage); 585 586 window.history.pushState({ path: myUrl }, "", myUrl); 587 588 if (scrollingTrigger) { 589 sFrom = scrollFrom; 590 } 591 592 filterTextQB = filterValue; 593 return { 594 state: wsState, 595 searchText: filterValue, 596 startEpoch: stDate, 597 endEpoch: endDate, 598 indexName: selIndexName, 599 from: sFrom, 600 queryLanguage: queryLanguage, 601 }; 602 } 603 604 function getSearchFilterForSave(qname, qdesc) { 605 let filterValue = filterTextQB.trim() || "*"; 606 let currentTab = $("#custom-code-tab").tabs("option", "active"); 607 return { 608 'queryName': qname, 609 'queryDescription': qdesc || "", 610 'searchText': filterValue, 611 'indexName': selectedSearchIndex, 612 'filterTab': currentTab.toString(), 613 'queryLanguage': $("#query-language-btn span").html() 614 }; 615 } 616 function processLiveTailQueryUpdate( 617 res, 618 eventType, 619 totalEventsSearched, 620 timeToFirstByte, 621 totalHits 622 ) { 623 if ( 624 res.hits && 625 res.hits.records !== null && 626 res.hits.records.length >= 1 && 627 res.qtype === "logs-query" 628 ) { 629 let columnOrder = _.uniq( 630 _.concat( 631 // make timestamp the first column 632 "timestamp", 633 // make logs the second column 634 "logs", 635 res.allColumns 636 ) 637 ); 638 allLiveTailColumns = res.allColumns; 639 renderAvailableFields(columnOrder); 640 renderLogsGrid(columnOrder, res.hits.records); 641 642 if (res && res.hits && res.hits.totalMatched) { 643 totalHits = res.hits.totalMatched; 644 } 645 } else if (logsRowData.length > 0) { 646 let columnOrder = _.uniq( 647 _.concat( 648 // make timestamp the first column 649 "timestamp", 650 // make logs the second column 651 "logs", 652 allLiveTailColumns 653 ) 654 ); 655 renderAvailableFields(columnOrder); 656 renderLogsGrid(columnOrder, logsRowData); 657 totalHits = logsRowData.length; 658 } else if ( 659 res.measure && 660 (res.qtype === "aggs-query" || res.qtype === "segstats-query") 661 ) { 662 if (res.groupByCols) { 663 columnOrder = _.uniq(_.concat(res.groupByCols)); 664 } 665 let columnOrder = []; 666 if (res.measureFunctions) { 667 columnOrder = _.uniq(_.concat(columnOrder, res.measureFunctions)); 668 } 669 670 aggsColumnDefs = []; 671 segStatsRowData = []; 672 renderMeasuresGrid(columnOrder, res.measure); 673 } 674 let totalTime = new Date().getTime() - startQueryTime; 675 let percentComplete = res.percent_complete; 676 renderTotalHits( 677 totalHits, 678 totalTime, 679 percentComplete, 680 eventType, 681 totalEventsSearched, 682 timeToFirstByte, 683 "", 684 res.qtype 685 ); 686 $("body").css("cursor", "default"); 687 } 688 function processQueryUpdate(res, eventType, totalEventsSearched, timeToFirstByte, totalHits) { 689 if (res.hits && res.hits.records!== null && res.hits.records.length >= 1 && res.qtype === "logs-query") { 690 let columnOrder = _.uniq(_.concat( 691 // make timestamp the first column 692 'timestamp', 693 // make logs the second column 694 'logs', 695 res.allColumns)); 696 697 // for sort function display 698 sortByTimestampAtDefault = res.sortByTimestampAtDefault; 699 700 renderAvailableFields(columnOrder); 701 renderLogsGrid(columnOrder, res.hits.records); 702 703 $("#logs-result-container").show(); 704 $("#agg-result-container").hide(); 705 706 if (res && res.hits && res.hits.totalMatched) { 707 totalHits = res.hits.totalMatched 708 } 709 } else if (res.measure && (res.qtype === "aggs-query" || res.qtype === "segstats-query")) { 710 if (res.groupByCols ) { 711 columnOrder = _.uniq(_.concat( 712 res.groupByCols)); 713 } 714 let columnOrder =[] 715 if (res.measureFunctions ) { 716 columnOrder = _.uniq(_.concat( 717 columnOrder,res.measureFunctions)); 718 } 719 720 aggsColumnDefs=[]; 721 segStatsRowData=[]; 722 renderMeasuresGrid(columnOrder, res.measure); 723 724 } 725 let totalTime = (new Date()).getTime() - startQueryTime; 726 let percentComplete = res.percent_complete; 727 renderTotalHits(totalHits, totalTime, percentComplete, eventType, totalEventsSearched, timeToFirstByte, "", res.qtype); 728 $('body').css('cursor', 'default'); 729 } 730 731 function processEmptyQueryResults() { 732 $("#logs-result-container").hide(); 733 $("#custom-chart-tab").hide(); 734 $("#agg-result-container").hide(); 735 $("#data-row-container").hide(); 736 $('#corner-popup').hide(); 737 $('#empty-response').show(); 738 $('#logs-view-controls').hide(); 739 let el = $('#empty-response'); 740 $('#empty-response').empty(); 741 el.append('<span>Your query returned no data, adjust your query.</span>') 742 } 743 function processLiveTailCompleteUpdate( 744 res, 745 eventType, 746 totalEventsSearched, 747 timeToFirstByte, 748 eqRel 749 ) { 750 let columnOrder = []; 751 let totalHits = res.totalMatched.value + logsRowData.length; 752 if (res.totalMatched.value + logsRowData.length > 500) totalHits = 500; 753 if ( 754 logsRowData.length == 0 && 755 res.totalMatched.value === 0 && 756 res.measure === undefined 757 ) { 758 processEmptyQueryResults(); 759 } 760 if (res.measure) { 761 if (res.groupByCols) { 762 columnOrder = _.uniq(_.concat(res.groupByCols)); 763 } 764 if (res.measureFunctions) { 765 columnOrder = _.uniq(_.concat(columnOrder, res.measureFunctions)); 766 } 767 resetDashboard(); 768 $("#logs-result-container").hide(); 769 $("#custom-chart-tab").show(); 770 $("#agg-result-container").show(); 771 aggsColumnDefs = []; 772 segStatsRowData = []; 773 renderMeasuresGrid(columnOrder, res.measure); 774 if ( 775 (res.qtype === "aggs-query" || res.qtype === "segstats-query") && 776 res.bucketCount 777 ) { 778 totalHits = res.bucketCount; 779 } 780 } 781 782 let totalTime = new Date().getTime() - startQueryTime; 783 let percentComplete = res.percent_complete; 784 if (res.total_rrc_count > 0) { 785 totalRrcCount += res.total_rrc_count; 786 } 787 renderTotalHits( 788 totalHits, 789 totalTime, 790 percentComplete, 791 eventType, 792 totalEventsSearched, 793 timeToFirstByte, 794 eqRel, 795 res.qtype 796 ); 797 $("#run-filter-btn").html(" "); 798 $("#run-filter-btn").removeClass("cancel-search"); 799 $("#run-filter-btn").removeClass("active"); 800 $("#query-builder-btn").html(" "); 801 $("#query-builder-btn").removeClass("cancel-search"); 802 $("#query-builder-btn").removeClass("active"); 803 wsState = "query"; 804 if (canScrollMore === false) { 805 scrollFrom = 0; 806 } 807 } 808 function processCompleteUpdate(res, eventType, totalEventsSearched, timeToFirstByte, eqRel) { 809 let columnOrder =[] 810 let totalHits = res.totalMatched.value; 811 if ((res.totalMatched == 0 || res.totalMatched.value === 0) && res.measure ===undefined) { 812 processEmptyQueryResults(); 813 } 814 if (res.measureFunctions && res.measureFunctions.length > 0) { 815 measureFunctions = res.measureFunctions; 816 } 817 if (res.measure) { 818 measureInfo = res.measure; 819 if (res.groupByCols) { 820 columnOrder = _.uniq(_.concat( 821 res.groupByCols)); 822 } 823 if (res.measureFunctions) { 824 columnOrder = _.uniq(_.concat( 825 columnOrder,res.measureFunctions)); 826 } 827 resetDashboard(); 828 $("#logs-result-container").hide(); 829 $("#custom-chart-tab").show(); 830 $("#agg-result-container").show(); 831 aggsColumnDefs=[]; 832 segStatsRowData=[]; 833 renderMeasuresGrid(columnOrder, res.measure); 834 if ((res.qtype ==="aggs-query" || res.qtype === "segstats-query") && res.bucketCount){ 835 totalHits = res.bucketCount; 836 } 837 }else{ 838 measureInfo = []; 839 } 840 isTimechart = res.isTimechart; 841 const currentUrl = window.location.href; 842 if (currentUrl.includes("index.html")) 843 timeChart(); 844 let totalTime = (new Date()).getTime() - startQueryTime; 845 let percentComplete = res.percent_complete; 846 if (res.total_rrc_count > 0){ 847 totalRrcCount += res.total_rrc_count; 848 } 849 renderTotalHits(totalHits, totalTime, percentComplete, eventType, totalEventsSearched, 850 timeToFirstByte, eqRel, res.qtype); 851 $('#run-filter-btn').html(' '); 852 $("#run-filter-btn").removeClass("cancel-search"); 853 $('#run-filter-btn').removeClass('active'); 854 $("#query-builder-btn").html(" "); 855 $("#query-builder-btn").removeClass("cancel-search"); 856 $("#query-builder-btn").removeClass("active"); 857 wsState = 'query' 858 if (canScrollMore === false){ 859 scrollFrom = 0; 860 } 861 } 862 863 function processTimeoutUpdate(res) { 864 showError(`Query ${res.qid} reached the timeout limit of ${res.timeoutSeconds} seconds`); 865 } 866 867 function processErrorUpdate(res) { 868 showError(`Message: ${res.message}`); 869 } 870 871 function processSearchError(res) { 872 if (res.can_scroll_more === false){ 873 showInfo(`You've reached maximum scroll limit (10,000).`); 874 } else if (res.message != "") { 875 showError(`Message: ${res.message}`); 876 resetDashboard(); 877 } 878 } 879 880 function processSearchErrorLog(res){ 881 if (res.can_scroll_more === false){ 882 showInfo(`You've reached maximum scroll limit (10,000).`); 883 } else if (res.message != "") { 884 showErrorResponse(`Message: ${res.message}`,res); 885 resetDashboard(); 886 } 887 } 888 889 function showErrorResponse(errorMsg,res){ 890 $("#logs-result-container").hide(); 891 $("#agg-result-container").hide(); 892 $("#data-row-container").hide(); 893 $('#corner-popup').hide(); 894 $('#empty-response').show(); 895 $('#logs-view-controls').hide(); 896 $("#custom-chart-tab").hide(); 897 let el = $('#empty-response'); 898 $('#empty-response').empty(); 899 if (res && res.no_data_err && res.no_data_err.includes("No data found")){ 900 el.html(`${res.no_data_err} <br> `+ errorMsg); 901 }else{ 902 el.html(errorMsg); 903 } 904 $('body').css('cursor', 'default'); 905 $('#run-filter-btn').html(' '); 906 $("#run-filter-btn").removeClass("cancel-search"); 907 $('#run-filter-btn').removeClass('active'); 908 $("#query-builder-btn").html(" "); 909 $("#query-builder-btn").removeClass("cancel-search"); 910 $("#query-builder-btn").removeClass("active"); 911 $('#run-metrics-query-btn').removeClass('active'); 912 913 wsState = 'query'; 914 } 915 916 917 918 function renderTotalHits(totalHits, elapedTimeMS, percentComplete, eventType, totalEventsSearched, timeToFirstByte, eqRel, qtype) { 919 //update chart title 920 console.log(`rendering total hits: ${totalHits}. elapedTimeMS: ${elapedTimeMS}`); 921 let startDate = displayStart ; 922 let endDate = displayEnd; 923 // Check if totalHits is undefined and set it to 0 924 let totalHitsFormatted = Number(totalHits || 0).toLocaleString(); 925 926 if (eventType === "QUERY_UPDATE") { 927 if (totalHits > 0){ 928 $('#hits-summary').html(` 929 <div><span class="total-hits">${totalHitsFormatted} </span><span>of ${totalEventsSearched} Records Matched</span> </div> 930 931 <div class="text-center">${dateFns.format(startDate, timestampDateFmt)} — ${dateFns.format(endDate, timestampDateFmt)}</div> 932 <div class="text-end">Response: ${timeToFirstByte} ms</div> 933 `); 934 } else{ 935 $('#hits-summary').html(`<div><span> ${totalEventsSearched} Records Searched</span> </div> 936 937 <div class="text-center">${dateFns.format(startDate, timestampDateFmt)} — ${dateFns.format(endDate, timestampDateFmt)}</div> 938 <div class="text-end">Response: ${timeToFirstByte} ms</div> 939 `); 940 } 941 $('#progress-div').html(` 942 <progress id="percent-complete" value=${percentComplete} max="100">${percentComplete}</progress> 943 <div id="percent-value">${parseInt(percentComplete)}%</div> 944 `); 945 } 946 else if (eventType === "COMPLETE") { 947 let operatorSign = ''; 948 if (eqRel === "gte") { 949 operatorSign = '>='; 950 } 951 if (qtype == "aggs-query" || qtype === "segstats-query") { 952 let bucketGrammer = totalHits == 1 ? "bucket was" : "buckets were"; 953 $("#hits-summary").html(` 954 <div><b>Response: ${timeToFirstByte} ms</b></div> 955 <div><span class="total-hits"><b>${operatorSign} ${totalHitsFormatted}</b></span><span> ${bucketGrammer} created from <b>${totalEventsSearched}</b> records.</span></div> 956 <div>${dateFns.format( 957 startDate, 958 timestampDateFmt 959 )} — ${dateFns.format(endDate, timestampDateFmt)}</div> 960 `); 961 } else if (totalHits > 0) { 962 $("#hits-summary").html(` 963 <div><b>Response: ${timeToFirstByte} ms</b></div> 964 <div><span class="total-hits"><b>${operatorSign} ${totalHitsFormatted}</b></span><span> of <b>${totalEventsSearched}</b> Records Matched</span></div> 965 <div>${dateFns.format( 966 startDate, 967 timestampDateFmt 968 )} — ${dateFns.format(endDate, timestampDateFmt)}</div> 969 `); 970 } else { 971 $("#hits-summary").html(` 972 <div><b>Response: ${timeToFirstByte} ms</b></div> 973 <div><span><b> ${totalEventsSearched} </b>Records Searched</span></div> 974 <div>${dateFns.format( 975 startDate, 976 timestampDateFmt 977 )} — ${dateFns.format(endDate, timestampDateFmt)}</div> 978 `); 979 } 980 $('#progress-div').html(``) 981 } 982 } 983 984 // LiveTail Refresh Duration 985 let refreshInterval = 10000; 986 987 $('.refresh-range-item').on('click', refreshRangeItemHandler); 988 989 function refreshRangeItemHandler(evt){ 990 $.each($(".refresh-range-item.active"), function () { 991 $(this).removeClass('active'); 992 }); 993 $(evt.currentTarget).addClass('active'); 994 let interval = $(evt.currentTarget).attr('id'); 995 $('#refresh-picker-btn span').html(interval); 996 997 refreshInterval = parseInterval(interval); // Parsing interval 998 if(liveTailState) 999 reconnect(); 1000 } 1001 1002 function parseInterval(interval) { 1003 const regex = /(\d+)([smhd])/; 1004 const match = interval.match(regex); 1005 const value = parseInt(match[1]); 1006 const unit = match[2]; 1007 1008 switch (unit) { 1009 case 's': 1010 return value * 1000; 1011 case 'm': 1012 return value * 60 * 1000; 1013 case 'h': 1014 return value * 60 * 60 * 1000; 1015 case 'd': 1016 return value * 24 * 60 * 60 * 1000; 1017 default: 1018 throw new Error("Invalid interval unit"); 1019 } 1020 }