github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/static/js/log-results-grid.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  let cellEditingClass = '';
    20  
    21  class ReadOnlyCellEditor {
    22      // gets called once before the renderer is used
    23      init(params) {
    24        // create the cell
    25          this.eInput = document.createElement('textarea');
    26          cellEditingClass = params.rowIndex%2===0 ? 'even-popup-textarea' : 'odd-popup-textarea'
    27          this.eInput.classList.add(cellEditingClass);
    28          this.eInput.readOnly = true;
    29          this.eInput.cols = params.cols;
    30          this.eInput.rows = params.rows;
    31          this.eInput.maxLength = params.maxLength;
    32          this.eInput.value = params.value;
    33      }
    34      // gets called once when grid ready to insert the element
    35      getGui() {
    36          return this.eInput;
    37      }
    38      // returns the new value after editing
    39      getValue() {
    40          return this.eInput.value;
    41      }   
    42      isPopup() {
    43          return true
    44      }
    45      refresh(params) {
    46          return true;
    47        }
    48      destroy() {
    49          this.eInput.classList.remove(cellEditingClass);
    50      }
    51    }
    52  
    53  const cellEditorParams = (params) => {
    54      const jsonLog = JSON.stringify(JSON.unflatten(params.data), null, 2) 
    55      return {
    56          value :jsonLog,
    57          cols: 100,
    58          rows: 10
    59      };
    60    };
    61  // initial columns
    62  let logsColumnDefs = [
    63      {
    64          field: "timestamp",
    65          headerName: "timestamp",
    66          editable: true, 
    67          cellEditor: ReadOnlyCellEditor,
    68          cellEditorPopup: true,
    69          cellEditorPopupPosition: 'under',
    70          cellRenderer: (params) => {
    71              return moment(params.value).format(timestampDateFmt);
    72          },
    73          cellEditorParams:cellEditorParams,
    74          maxWidth: 216,
    75          minWidth: 216,
    76      },
    77      {
    78          field: "logs",
    79          headerName: "logs",
    80          minWidth: 1128,
    81          cellRenderer: (params) => {
    82              let logString = '';
    83              let counter = 0;
    84              if (updatedSelFieldList){
    85                  selectedFieldsList = _.intersection(selectedFieldsList, availColNames);
    86              }else{
    87                  selectedFieldsList = _.union(selectedFieldsList, availColNames);
    88              }
    89  
    90              if (selectedFieldsList.length != 0) {
    91                  availColNames.forEach((colName, index) => {
    92                      if(selectedFieldsList.includes(colName)){
    93                          $(`.toggle-${string2Hex(colName)}`).addClass('active');
    94                      } else {
    95                          $(`.toggle-${string2Hex(colName)}`).removeClass('active');
    96                      }
    97                  });
    98              } else {
    99                  selectedFieldsList = [];
   100                  availColNames.forEach((colName, index) => {
   101                      $(`.toggle-${string2Hex(colName)}`).addClass('active');
   102                      selectedFieldsList.push(colName);
   103                  });
   104              }
   105              _.forEach(params.data, (value, key) => {
   106                  let colSep = counter > 0 ? '<span class="col-sep"> | </span>' : '';
   107                  if (key != 'logs' && selectedFieldsList.includes(key)) {
   108                      logString += `<span class="cname-hide-${string2Hex(key)}">${colSep}${key}=`+ JSON.stringify(JSON.unflatten(value), null, 2)+`</span>`;                    
   109  
   110                      counter++;
   111                  }
   112                  if (key === 'timestamp'){
   113                      logString += `<span class="cname-hide-${string2Hex(key)}">${colSep}${key}=${value}</span>`;
   114                  }
   115              });
   116              return logString;
   117          },
   118      }
   119  ];
   120  
   121  // initial dataset
   122  let logsRowData = [];
   123  let allLiveTailColumns = [];
   124  let total_liveTail_searched = 0;
   125   // let the grid know which columns and what data to use
   126  const gridOptions = {
   127      columnDefs: logsColumnDefs,
   128      rowData: logsRowData,
   129      animateRows: true,
   130      readOnlyEdit: true,
   131      singleClickEdit: true,
   132      headerHeight:32,
   133      defaultColDef: {
   134          initialWidth: 100,
   135          sortable: true,
   136          resizable: true,
   137          minWidth: 200,
   138          icons: {
   139              sortAscending: '<i class="fa fa-sort-alpha-down"/>',
   140              sortDescending: '<i class="fa fa-sort-alpha-up"/>',
   141            },
   142      },
   143      icons: {
   144          sortAscending: '<i class="fa fa-sort-alpha-down"/>',
   145          sortDescending: '<i class="fa fa-sort-alpha-up"/>',
   146        },
   147      enableCellTextSelection: true,
   148      suppressScrollOnNewData: true,
   149      suppressAnimationFrame: true,
   150      suppressFieldDotNotation: true,
   151      onBodyScroll(evt){
   152          if(evt.direction === 'vertical' && canScrollMore == true){
   153              let diff = logsRowData.length - evt.api.getLastDisplayedRow();
   154              // if we're less than 1 items from the end...fetch more data
   155              if(diff <= 5) {
   156                  // Show loading indicator
   157                  showLoadingIndicator();
   158                  
   159                  let scrollingTrigger = true;
   160                  data = getSearchFilter(false, scrollingTrigger);
   161                  if (data && data.searchText == "error") {
   162                    alert("Error");
   163                    hideLoadingIndicator(); // Hide loading indicator on error
   164                    return;
   165                  }
   166                  doSearch(data).then(() => {
   167                      hideLoadingIndicator(); // Hide loading indicator once data is fetched
   168                  });
   169              }
   170          }
   171      },
   172      overlayLoadingTemplate: '<div class="ag-overlay-loading-center"><div class="loading-icon"></div><div class="loading-text">Loading...</div></div>',
   173  };
   174  
   175  function showLoadingIndicator() {
   176      gridOptions.api.showLoadingOverlay();
   177  }
   178  
   179  function hideLoadingIndicator() {
   180      gridOptions.api.hideOverlay();
   181  }
   182  
   183  const myCellRenderer= (params) => {
   184      let logString = '';
   185      if (typeof params.data === 'object' && params.data !== null){
   186          let value = params.data[params.colName]
   187          if (value !== ""){
   188              if (Array.isArray(value)){
   189                  logString= JSON.stringify(JSON.unflatten(value), null, 2)
   190              }else{
   191                  logString= value
   192              }
   193          }
   194      }
   195      return logString;
   196  };
   197  
   198  let gridDiv = null;
   199  
   200  function renderLogsGrid(columnOrder, hits){
   201      if (sortByTimestampAtDefault) {
   202          logsColumnDefs[0].sort = "desc";
   203      }else {
   204          logsColumnDefs[0].sort = undefined;
   205      }
   206      if (gridDiv == null){
   207          gridDiv = document.querySelector('#LogResultsGrid');
   208          new agGrid.Grid(gridDiv, gridOptions);
   209      }
   210  
   211      let logview = getLogView();
   212  
   213      let cols = columnOrder.map((colName, index) => {
   214          let hideCol = false;
   215          if (index >= defaultColumnCount) {
   216              hideCol = true;
   217          }
   218         
   219          if (logview != 'single-line' && colName == 'logs'){
   220              hideCol = true;
   221          }
   222  
   223          if (index > 1) {
   224              if (selectedFieldsList.indexOf(colName) != -1){
   225                  hideCol = true;
   226              } else{
   227                  hideCol = false;
   228              }
   229          }
   230          return {
   231              field: colName,
   232              hide: hideCol,
   233              headerName: colName,
   234              cellRenderer: myCellRenderer,
   235              cellRendererParams : {
   236                  colName: colName
   237               }
   238          };
   239      });
   240      if(hits.length != 0){
   241          logsRowData = _.concat(hits, logsRowData);
   242          if (liveTailState && logsRowData.length > 500){
   243              logsRowData = logsRowData.slice(0, 500);
   244          }
   245              
   246      }
   247      logsColumnDefs = _.chain(logsColumnDefs).concat(cols).uniqBy('field').value();
   248      gridOptions.api.setColumnDefs(logsColumnDefs);
   249  
   250      const allColumnIds = [];
   251      gridOptions.columnApi.getColumns().forEach((column) => {
   252          allColumnIds.push(column.getId());
   253      });
   254      gridOptions.columnApi.autoSizeColumns(allColumnIds, false);
   255      gridOptions.api.setRowData(logsRowData);
   256      
   257      switch (logview){
   258          case 'single-line':
   259              logOptionSingleHandler();
   260              break;
   261          case 'multi-line':
   262              logOptionMultiHandler();
   263              break;
   264          case 'table':
   265              logOptionTableHandler();
   266              break;
   267      }
   268  }
   269  
   270  function updateColumns() {
   271      // Always show timestamp
   272      gridOptions.columnApi.setColumnVisible("timestamp", true);
   273      let isAnyColActive = false;
   274      availColNames.forEach((colName, index) => {
   275          if ($(`.toggle-${string2Hex(colName)}`).hasClass('active')) {
   276              isAnyColActive = true;
   277              gridOptions.columnApi.setColumnVisible(colName, true);
   278          } else {
   279              gridOptions.columnApi.setColumnVisible(colName, false);
   280          }
   281      });
   282  
   283      if (isAnyColActive) {
   284          // Always hide logs column if we have some fields selected
   285          gridOptions.columnApi.setColumnVisible("logs", false);
   286      }
   287      gridOptions.api.sizeColumnsToFit();
   288  }
   289  
   290  function getLogView(){
   291      let logview = Cookies.get('log-view') || 'table';
   292      return logview
   293  }
   294  
   295  JSON.unflatten = function (data) {
   296      "use strict";
   297      if (Object(data) !== data || Array.isArray(data)) return data;
   298      let regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
   299          resultholder = {};
   300      for (let p in data) {
   301          let cur = resultholder,
   302              prop = "",
   303              m;
   304          while (m = regex.exec(p)) {
   305              cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
   306              prop = m[2] || m[1];
   307          }
   308          cur[prop] = data[p];
   309      }
   310      return resultholder[""] || resultholder;
   311  };