1/* 2 * Copyright (C) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { BaseElement, element } from '../../../../../base-ui/BaseElement'; 17import { SelectionParam } from '../../../../bean/BoxSelection'; 18import { TraceRow } from '../../base/TraceRow'; 19import { TraceSheet } from '../../base/TraceSheet'; 20import { Flag } from '../../timer-shaft/Flag'; 21import { SpSystemTrace } from '../../../SpSystemTrace'; 22import { ns2Timestamp, ns2x, Rect } from '../../../../database/ui-worker/ProcedureWorkerCommon'; 23import { LogStruct } from '../../../../database/ui-worker/ProcedureWorkerLog'; 24import { ColorUtils } from '../../base/ColorUtils'; 25import { LitPageTable } from '../../../../../base-ui/table/LitPageTable'; 26import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 27import { queryLogAllData } from '../../../../database/sql/SqlLite.sql'; 28import { TabPaneHiLogsHtml } from './TabPaneHiLogs.html'; 29 30@element('tab-hi-log') 31export class TabPaneHiLogs extends BaseElement { 32 tableTimeHandle: (() => void) | undefined; 33 tableTitleTimeHandle: (() => void) | undefined; 34 private systemLogSource: LogStruct[] = []; 35 private spSystemTrace: SpSystemTrace | undefined | null; 36 private traceSheetEl: TraceSheet | undefined | null; 37 private levelFilterInput: HTMLSelectElement | undefined | null; 38 private tagFilterInput: HTMLInputElement | undefined | null; 39 private searchFilterInput: HTMLInputElement | undefined | null; 40 private processFilter: HTMLInputElement | undefined | null; 41 private logTableTitle: HTMLDivElement | undefined | null; 42 private tagFilterDiv: HTMLDivElement | undefined | null; 43 private hiLogsTbl: LitPageTable | undefined | null; 44 private filterData: LogStruct[] = []; 45 private optionLevel: string[] = ['Debug', 'Info', 'Warn', 'Error', 'Fatal']; 46 private allowTag: Set<string> = new Set(); 47 private ONE_DAY_NS = 86400000000000; 48 private progressEL: LitProgressBar | null | undefined; 49 50 set data(systemLogParam: SelectionParam) { 51 if (this.hiLogsTbl) { 52 this.hiLogsTbl.recycleDataSource = []; 53 this.filterData = []; 54 } 55 let oneDayTime = (window as any).recordEndNS - this.ONE_DAY_NS; 56 if (systemLogParam && systemLogParam.hiLogs.length > 0) { 57 this.progressEL!.loading = true; 58 queryLogAllData(oneDayTime, systemLogParam.leftNs, systemLogParam.rightNs).then((res) => { 59 systemLogParam.sysAlllogsData = res; 60 this.systemLogSource = res; 61 this.tableTimeHandle?.(); 62 }); 63 } 64 } 65 66 init(): void { 67 this.levelFilterInput = this.shadowRoot?.querySelector<HTMLSelectElement>('#level-filter'); 68 this.logTableTitle = this.shadowRoot?.querySelector<HTMLDivElement>('#log-title'); 69 this.tagFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#tag-filter'); 70 this.searchFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#search-filter'); 71 this.processFilter = this.shadowRoot?.querySelector<HTMLInputElement>('#process-filter'); 72 this.spSystemTrace = document 73 .querySelector('body > sp-application') 74 ?.shadowRoot?.querySelector<SpSystemTrace>('#sp-system-trace'); 75 this.tableTimeHandle = this.delayedRefresh(this.refreshTable); 76 this.tableTitleTimeHandle = this.delayedRefresh(this.refreshLogsTitle); 77 this.tagFilterDiv = this.shadowRoot!.querySelector<HTMLDivElement>('#tagFilter'); 78 this.hiLogsTbl = this.shadowRoot!.querySelector<LitPageTable>('#tb-hilogs'); 79 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 80 this.hiLogsTbl!.getItemTextColor = (data) => { 81 return ColorUtils.getHilogColor(data.level); 82 }; 83 this.hiLogsTbl!.itemTextHandleMap.set('startTs', (startTs) => { 84 return ns2Timestamp(startTs); 85 }); 86 this.hiLogsTbl!.addEventListener('row-hover', (e): void => { 87 // @ts-ignore 88 let data = e.detail.data; 89 if (data) { 90 let pointX: number = ns2x( 91 data.startTs || 0, 92 TraceRow.range!.startNS, 93 TraceRow.range!.endNS, 94 TraceRow.range!.totalNS, 95 new Rect(0, 0, TraceRow.FRAME_WIDTH, 0) 96 ); 97 this.traceSheetEl!.systemLogFlag = new Flag( 98 Math.floor(pointX), 99 0, 100 0, 101 0, 102 data.startTs!, 103 '#999999', 104 '', 105 true, 106 '' 107 ); 108 this.spSystemTrace?.refreshCanvas(false); 109 } 110 }); 111 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 112 tbl!.addEventListener('scroll', () => { 113 this.tableTitleTimeHandle?.(); 114 }); 115 } 116 117 initElements(): void { 118 this.init(); 119 this.tagFilterDiv!.onclick = (ev): void => { 120 // @ts-ignore 121 let parentNode = ev.target.parentNode; 122 if (parentNode && this.tagFilterDiv!.contains(parentNode)) { 123 this.tagFilterDiv!.removeChild(parentNode); 124 this.allowTag['delete'](parentNode.textContent.trim().toLowerCase()); 125 } 126 this.tableTimeHandle?.(); 127 }; 128 this.searchFilterInput!.oninput = (): void => { 129 this.tableTimeHandle?.(); 130 }; 131 this.processFilter!.oninput = (): void => { 132 this.tableTimeHandle?.(); 133 }; 134 this.levelFilterInput!.onchange = (): void => { 135 this.tableTimeHandle?.(); 136 }; 137 } 138 139 connectedCallback(): void { 140 super.connectedCallback(); 141 this.tagFilterInput?.addEventListener('keyup', this.tagFilterKeyEvent); 142 new ResizeObserver((): void => { 143 this.parentElement!.style.overflow = 'hidden'; 144 if (this.hiLogsTbl) { 145 // @ts-ignore 146 this.hiLogsTbl.shadowRoot.querySelector('.table').style.height = 147 this.parentElement!.clientHeight - 20 - 45 + 'px'; 148 } 149 this.tableTimeHandle?.(); 150 this.tableTitleTimeHandle?.(); 151 }).observe(this.parentElement!); 152 } 153 154 disconnectedCallback(): void { 155 super.disconnectedCallback(); 156 this.tagFilterInput?.removeEventListener('keyup', this.tagFilterKeyEvent); 157 } 158 159 initHtml(): string { 160 return TabPaneHiLogsHtml; 161 } 162 163 rerefreshLogsTab(): void { 164 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 165 let height = 0; 166 if (tbl) { 167 tbl.querySelectorAll<HTMLElement>('.tr').forEach((trEl: HTMLElement, index: number): void => { 168 if (index === 0) { 169 let frontTotalRowSize = Math.round((tbl!.scrollTop / trEl.clientHeight) * 100) / 100; 170 if (frontTotalRowSize.toString().indexOf('.') >= 0) { 171 let rowCount = frontTotalRowSize.toString().split('.'); 172 height += trEl.clientHeight - (Number(rowCount[1]) / 100) * trEl.clientHeight; 173 } 174 } 175 let allTdEl = trEl.querySelectorAll<HTMLElement>('.td'); 176 allTdEl[0].style.color = '#3D88C7'; 177 allTdEl[0].style.textDecoration = 'underline'; 178 allTdEl[0].style.textDecorationColor = '#3D88C7'; 179 trEl.addEventListener('mouseout', (): void => { 180 this.traceSheetEl!.systemLogFlag = undefined; 181 this.spSystemTrace?.refreshCanvas(false); 182 }); 183 }); 184 } 185 } 186 187 refreshLogsTitle(): void { 188 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 189 let height = 0; 190 let firstRowHeight = 27; 191 let tableHeadHeight = 26; 192 this.rerefreshLogsTab(); 193 if (this.hiLogsTbl && this.hiLogsTbl.currentRecycleList.length > 0) { 194 let startDataIndex = this.hiLogsTbl.startSkip + 1; 195 let endDataIndex = startDataIndex; 196 if (height < firstRowHeight * 0.3) { 197 startDataIndex++; 198 } 199 let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight; 200 while (height < tableHeight) { 201 if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) { 202 break; 203 } 204 height += firstRowHeight; 205 endDataIndex++; 206 } 207 if (tableHeight - height > firstRowHeight * 0.3) { 208 endDataIndex++; 209 } 210 if (endDataIndex >= this.filterData.length) { 211 endDataIndex = this.filterData.length; 212 } else { 213 endDataIndex = this.hiLogsTbl.startSkip === 0 ? endDataIndex - 1 : endDataIndex; 214 } 215 this.logTableTitle!.textContent = `Hilogs [${this.hiLogsTbl.startSkip === 0 ? 1 : startDataIndex}, 216 ${endDataIndex}] / ${this.filterData.length || 0}`; 217 } else { 218 this.logTableTitle!.textContent = 'Hilogs [0, 0] / 0'; 219 } 220 this.progressEL!.loading = false; 221 } 222 223 initTabSheetEl(traceSheet: TraceSheet): void { 224 this.traceSheetEl = traceSheet; 225 this.levelFilterInput!.selectedIndex = 0; 226 this.tagFilterInput!.value = ''; 227 this.tagFilterDiv!.innerHTML = ''; 228 this.allowTag.clear(); 229 this.processFilter!.value = ''; 230 this.searchFilterInput!.value = ''; 231 } 232 233 tagFilterKeyEvent = (e: KeyboardEvent): void => { 234 let inputValue = this.tagFilterInput!.value.trim(); 235 if (e.code === 'Enter') { 236 if (inputValue !== '' && !this.allowTag.has(inputValue.toLowerCase())) { 237 let tagElement = document.createElement('div'); 238 tagElement.className = 'tagElement'; 239 tagElement.id = inputValue; 240 let tag = document.createElement('div'); 241 tag.className = 'tag'; 242 tag.innerHTML = inputValue; 243 this.allowTag.add(inputValue.toLowerCase()); 244 let closeButton = document.createElement('lit-icon'); 245 closeButton.setAttribute('name', 'close-light'); 246 closeButton.style.color = '#FFFFFF'; 247 tagElement.append(tag); 248 tagElement.append(closeButton); 249 this.tagFilterDiv!.append(tagElement); 250 this.tagFilterInput!.value = ''; 251 this.tagFilterInput!.placeholder = 'Filter by tag...'; 252 } 253 } else if (e.code === 'Backspace') { 254 let index = this.tagFilterDiv!.childNodes.length - defaultIndex; 255 if (index >= 0 && inputValue === '') { 256 let childNode = this.tagFilterDiv!.childNodes[index]; 257 this.tagFilterDiv!.removeChild(childNode); 258 this.allowTag['delete'](childNode.textContent!.trim().toLowerCase()); 259 } 260 } 261 this.tableTimeHandle?.(); 262 }; 263 264 private updateFilterData(): void { 265 if (this.systemLogSource?.length > 0) { 266 this.filterData = this.systemLogSource.filter((data) => this.isFilterLog(data)); 267 } 268 if (this.hiLogsTbl) { 269 // @ts-ignore 270 this.hiLogsTbl.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 45 + 'px'; 271 } 272 if (this.filterData.length > 0) { 273 this.hiLogsTbl!.recycleDataSource = this.filterData; 274 } else { 275 this.hiLogsTbl!.recycleDataSource = []; 276 } 277 this.refreshLogsTitle(); 278 } 279 280 private isFilterLog(data: LogStruct): boolean { 281 let level = this.levelFilterInput?.selectedIndex || 0; 282 let search = this.searchFilterInput?.value.toLowerCase() || ''; 283 search = search.replace(/\s/g, ''); 284 let processSearch = this.processFilter?.value.toLowerCase() || ''; 285 processSearch = processSearch.replace(/\s/g, ''); 286 return ( 287 (data.startTs || 0) >= TraceRow.range!.startNS && 288 (data.startTs || 0) <= TraceRow.range!.endNS && 289 (level === 0 || this.optionLevel.indexOf(data.level!) >= level) && 290 (this.allowTag.size === 0 || this.allowTag.has(data.tag!.toLowerCase())) && 291 (search === '' || data.context!.toLowerCase().replace(/\s/g, '').indexOf(search) >= 0) && 292 (processSearch === '' || 293 (data.processName !== null && data.processName!.toLowerCase().replace(/\s/g, '').indexOf(processSearch) >= 0)) 294 ); 295 } 296 297 private refreshTable(): void { 298 if (this.traceSheetEl) { 299 this.traceSheetEl.systemLogFlag = undefined; 300 this.spSystemTrace?.refreshCanvas(false); 301 this.updateFilterData(); 302 } 303 } 304 305 private delayedRefresh(optionFn: Function, dur: number = tableTimeOut): () => void { 306 let timeOutId: number; 307 return (...args: []): void => { 308 window.clearTimeout(timeOutId); 309 timeOutId = window.setTimeout((): void => { 310 optionFn.apply(this, ...args); 311 }, dur); 312 }; 313 } 314} 315 316let defaultIndex: number = 1; 317let tableTimeOut: number = 50; 318