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 private timeOutId: number | undefined; 50 private currentSelection: SelectionParam | undefined; 51 52 set data(systemLogParam: SelectionParam) { 53 if (systemLogParam === this.currentSelection) { 54 return; 55 } 56 this.currentSelection = systemLogParam; 57 if (this.hiLogsTbl) { 58 this.hiLogsTbl.recycleDataSource = []; 59 this.filterData = []; 60 } 61 window.clearTimeout(this.timeOutId); 62 // @ts-ignore 63 let oneDayTime = (window as unknown).recordEndNS - this.ONE_DAY_NS; 64 if (systemLogParam && systemLogParam.hiLogs.length > 0) { 65 this.progressEL!.loading = true; 66 queryLogAllData(oneDayTime, systemLogParam.leftNs, systemLogParam.rightNs).then((res) => { 67 if (res.length === 0) { 68 this.progressEL!.loading = false; 69 } 70 systemLogParam.sysAlllogsData = res; 71 this.systemLogSource = res; 72 this.refreshTable(); 73 }); 74 } 75 } 76 77 init(): void { 78 this.levelFilterInput = this.shadowRoot?.querySelector<HTMLSelectElement>('#level-filter'); 79 this.logTableTitle = this.shadowRoot?.querySelector<HTMLDivElement>('#log-title'); 80 this.tagFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#tag-filter'); 81 this.searchFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#search-filter'); 82 this.processFilter = this.shadowRoot?.querySelector<HTMLInputElement>('#process-filter'); 83 this.spSystemTrace = document 84 .querySelector('body > sp-application') 85 ?.shadowRoot?.querySelector<SpSystemTrace>('#sp-system-trace'); 86 this.tableTimeHandle = this.delayedRefresh(this.refreshTable); 87 this.tableTitleTimeHandle = this.delayedRefresh(this.refreshLogsTitle); 88 this.tagFilterDiv = this.shadowRoot!.querySelector<HTMLDivElement>('#tagFilter'); 89 this.hiLogsTbl = this.shadowRoot!.querySelector<LitPageTable>('#tb-hilogs'); 90 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 91 this.hiLogsTbl!.getItemTextColor = (data): string => { 92 // @ts-ignore 93 return ColorUtils.getHilogColor(data.level); 94 }; 95 this.hiLogsTbl!.itemTextHandleMap.set('startTs', (startTs) => { 96 // @ts-ignore 97 return ns2Timestamp(startTs); 98 }); 99 this.hiLogsTbl!.addEventListener('row-hover', (e): void => { 100 // @ts-ignore 101 let data = e.detail.data; 102 if (data) { 103 let pointX: number = ns2x( 104 data.startTs || 0, 105 TraceRow.range!.startNS, 106 TraceRow.range!.endNS, 107 TraceRow.range!.totalNS, 108 new Rect(0, 0, TraceRow.FRAME_WIDTH, 0) 109 ); 110 this.traceSheetEl!.systemLogFlag = new Flag( 111 Math.floor(pointX), 112 0, 113 0, 114 0, 115 data.startTs!, 116 '#999999', 117 '', 118 true, 119 '' 120 ); 121 this.spSystemTrace?.refreshCanvas(false); 122 } 123 }); 124 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 125 tbl!.addEventListener('scroll', () => { 126 this.tableTitleTimeHandle?.(); 127 }); 128 } 129 130 initElements(): void { 131 this.init(); 132 this.tagFilterDiv!.onclick = (ev): void => { 133 // @ts-ignore 134 let parentNode = ev.target.parentNode; 135 if (parentNode && this.tagFilterDiv!.contains(parentNode)) { 136 this.tagFilterDiv!.removeChild(parentNode); 137 this.allowTag.delete(parentNode.textContent.trim().toLowerCase()); 138 } 139 this.tableTimeHandle?.(); 140 }; 141 this.tagFilterInput!.addEventListener('keyup', (ev) => { 142 if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) { 143 ev.stopPropagation(); 144 } 145 }); 146 this.searchFilterInput!.oninput = (): void => { 147 this.tableTimeHandle?.(); 148 }; 149 this.searchFilterInput!.addEventListener('keyup', (ev) => { 150 if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) { 151 ev.stopPropagation(); 152 } 153 }); 154 this.processFilter!.oninput = (): void => { 155 this.tableTimeHandle?.(); 156 }; 157 this.processFilter!.addEventListener('keyup', (ev) => { 158 if (ev.key.toLocaleLowerCase() === String.fromCharCode(47)) { 159 ev.stopPropagation(); 160 } 161 }); 162 this.levelFilterInput!.onchange = (): void => { 163 this.tableTimeHandle?.(); 164 }; 165 } 166 167 connectedCallback(): void { 168 super.connectedCallback(); 169 this.tagFilterInput?.addEventListener('keyup', this.tagFilterKeyEvent); 170 new ResizeObserver((): void => { 171 this.parentElement!.style.overflow = 'hidden'; 172 if (this.hiLogsTbl) { 173 // @ts-ignore 174 this.hiLogsTbl.shadowRoot.querySelector('.table').style.height = 175 this.parentElement!.clientHeight - 20 - 45 + 'px'; 176 } 177 if (this.filterData.length > 0) { 178 this.refreshTable(); 179 this.tableTitleTimeHandle?.(); 180 } 181 }).observe(this.parentElement!); 182 } 183 184 disconnectedCallback(): void { 185 super.disconnectedCallback(); 186 this.tagFilterInput?.removeEventListener('keyup', this.tagFilterKeyEvent); 187 } 188 189 initHtml(): string { 190 return TabPaneHiLogsHtml; 191 } 192 193 rerefreshLogsTab(): void { 194 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 195 let height = 0; 196 if (tbl) { 197 tbl.querySelectorAll<HTMLElement>('.tr').forEach((trEl: HTMLElement, index: number): void => { 198 if (index === 0) { 199 let frontTotalRowSize = Math.round((tbl!.scrollTop / trEl.clientHeight) * 100) / 100; 200 if (frontTotalRowSize.toString().indexOf('.') >= 0) { 201 let rowCount = frontTotalRowSize.toString().split('.'); 202 height += trEl.clientHeight - (Number(rowCount[1]) / 100) * trEl.clientHeight; 203 } 204 } 205 let allTdEl = trEl.querySelectorAll<HTMLElement>('.td'); 206 allTdEl[0].style.color = '#3D88C7'; 207 allTdEl[0].style.textDecoration = 'underline'; 208 allTdEl[0].style.textDecorationColor = '#3D88C7'; 209 trEl.addEventListener('mouseout', (): void => { 210 this.traceSheetEl!.systemLogFlag = undefined; 211 this.spSystemTrace?.refreshCanvas(false); 212 }); 213 }); 214 } 215 } 216 217 refreshLogsTitle(): void { 218 let tbl = this.hiLogsTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 219 let height = 0; 220 let firstRowHeight = 27; 221 let tableHeadHeight = 26; 222 this.rerefreshLogsTab(); 223 if (this.hiLogsTbl && this.hiLogsTbl.currentRecycleList.length > 0) { 224 let startDataIndex = this.hiLogsTbl.startSkip + 1; 225 let endDataIndex = startDataIndex; 226 let crossTopHeight = tbl!.scrollTop % firstRowHeight; 227 let topShowHeight = crossTopHeight === 0 ? 0 : firstRowHeight - crossTopHeight; 228 if (topShowHeight < firstRowHeight * 0.3) { 229 startDataIndex++; 230 } 231 let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight; 232 while (height < tableHeight) { 233 if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) { 234 break; 235 } 236 height += firstRowHeight; 237 endDataIndex++; 238 } 239 if (tableHeight - height - topShowHeight > firstRowHeight * 0.3) { 240 endDataIndex++; 241 } 242 if (endDataIndex >= this.filterData.length) { 243 endDataIndex = this.filterData.length; 244 } else { 245 endDataIndex = this.hiLogsTbl.startSkip === 0 ? endDataIndex - 1 : endDataIndex; 246 } 247 this.logTableTitle!.textContent = `Hilogs [${this.hiLogsTbl.startSkip === 0 ? 1 : startDataIndex}, 248 ${endDataIndex}] / ${this.filterData.length || 0}`; 249 } else { 250 this.logTableTitle!.textContent = 'Hilogs [0, 0] / 0'; 251 } 252 if (this.hiLogsTbl!.recycleDataSource.length > 0) { 253 this.progressEL!.loading = false; 254 } 255 } 256 257 initTabSheetEl(traceSheet: TraceSheet): void { 258 this.traceSheetEl = traceSheet; 259 this.levelFilterInput!.selectedIndex = 0; 260 this.tagFilterInput!.value = ''; 261 this.tagFilterDiv!.innerHTML = ''; 262 this.allowTag.clear(); 263 this.processFilter!.value = ''; 264 this.searchFilterInput!.value = ''; 265 } 266 267 tagFilterKeyEvent = (e: KeyboardEvent): void => { 268 let inputValue = this.tagFilterInput!.value.trim(); 269 if (e.key === 'Enter') { 270 if (inputValue !== '' && !this.allowTag.has(inputValue.toLowerCase()) && this.allowTag.size < 10) { 271 let tagElement = document.createElement('div'); 272 tagElement.className = 'tagElement'; 273 tagElement.id = inputValue; 274 let tag = document.createElement('div'); 275 tag.className = 'tag'; 276 tag.innerHTML = inputValue; 277 this.allowTag.add(inputValue.toLowerCase()); 278 let closeButton = document.createElement('lit-icon'); 279 closeButton.setAttribute('name', 'close-light'); 280 closeButton.style.color = '#FFFFFF'; 281 tagElement.append(tag); 282 tagElement.append(closeButton); 283 this.tagFilterDiv!.append(tagElement); 284 } 285 } 286 this.tableTimeHandle?.(); 287 }; 288 289 private updateFilterData(): void { 290 if (this.systemLogSource?.length > 0) { 291 this.filterData = this.systemLogSource.filter((data) => this.isFilterLog(data)); 292 } 293 if (this.hiLogsTbl) { 294 // @ts-ignore 295 this.hiLogsTbl.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 45 + 'px'; 296 } 297 if (this.filterData.length > 0) { 298 this.hiLogsTbl!.recycleDataSource = this.filterData; 299 } else { 300 this.hiLogsTbl!.recycleDataSource = []; 301 } 302 this.refreshLogsTitle(); 303 } 304 305 private isFilterLog(data: LogStruct): boolean { 306 let level = this.levelFilterInput?.selectedIndex || 0; 307 let search = this.searchFilterInput?.value.toLowerCase() || ''; 308 let tagData = this.tagFilterInput?.value.toLowerCase() || ''; 309 tagData = tagData.replace(/\s/g, ''); 310 search = search.replace(/\s/g, ''); 311 let processSearch = this.processFilter?.value.toLowerCase() || ''; 312 processSearch = processSearch.replace(/\s/g, ''); 313 // @ts-ignore 314 return ( 315 (data.startTs || 0) >= TraceRow.range!.startNS && 316 (data.startTs || 0) <= TraceRow.range!.endNS && 317 (level === 0 || this.optionLevel.indexOf(data.level!) >= level) && 318 (this.allowTag.size === 0 || this.filterTag(data.tag!.toLowerCase().replace(/\s/g, ''))) && 319 (search === '' || data.context!.toLowerCase().replace(/\s/g, '').indexOf(search) >= 0) && 320 (processSearch === '' || 321 (data.processName !== null && data.processName!.toLowerCase().replace(/\s/g, '').indexOf(processSearch) >= 0)) 322 ); 323 } 324 325 // 模糊过滤tag 326 private filterTag(tagAllName: unknown): boolean | undefined { 327 let flag = false; 328 if (this.allowTag.size === 0) { 329 return; 330 } 331 for (const value of this.allowTag) { 332 // @ts-ignore 333 if (tagAllName.indexOf(value) >= 0) { 334 flag = true; 335 return flag; 336 } 337 } 338 return flag; 339 } 340 341 private refreshTable(): void { 342 if (this.traceSheetEl) { 343 this.traceSheetEl.systemLogFlag = undefined; 344 this.spSystemTrace?.refreshCanvas(false); 345 this.updateFilterData(); 346 } 347 } 348 349 private delayedRefresh(optionFn: Function, dur: number = tableTimeOut): () => void { 350 return (...args: []): void => { 351 window.clearTimeout(this.timeOutId); 352 this.timeOutId = window.setTimeout((): void => { 353 optionFn.apply(this, ...args); 354 }, dur); 355 }; 356 } 357} 358 359let defaultIndex: number = 1; 360let tableTimeOut: number = 50; 361