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 { LogStruct } from '../../../../database/ui-worker/ProcedureWorkerLog'; 19import { ColorUtils } from '../../base/ColorUtils'; 20import { LitTable } from '../../../../../base-ui/table/lit-table'; 21import { LitIcon } from '../../../../../base-ui/icon/LitIcon'; 22import { TabPaneHiLogSummaryHtml } from './TabPaneHiLogSummary.html'; 23import { NUM_30, NUM_40 } from '../../../../bean/NumBean'; 24import { queryLogAllData } from '../../../../database/sql/SqlLite.sql'; 25 26@element('tab-hi-log-summary') 27export class TabPaneHiLogSummary extends BaseElement { 28 private logSummaryTable: HTMLDivElement | undefined | null; 29 private summaryDownLoadTbl: LitTable | undefined | null; 30 private systemLogSource: LogStruct[] = []; 31 private logTreeNodes: LogTreeNode[] = []; 32 private expansionDiv: HTMLDivElement | undefined | null; 33 private expansionUpIcon: LitIcon | undefined | null; 34 private expansionDownIcon: LitIcon | undefined | null; 35 private expandedNodeList: Set<number> = new Set(); 36 private logLevel: string[] = ['Debug', 'Info', 'Warn', 'Error', 'Fatal']; 37 private selectTreeDepth: number = 0; 38 private currentSelection: SelectionParam | undefined; 39 40 set data(systemLogDetailParam: SelectionParam) { 41 if (systemLogDetailParam === this.currentSelection) { 42 return; 43 } 44 this.currentSelection = systemLogDetailParam; 45 this.systemLogSource = []; 46 this.expandedNodeList.clear(); 47 this.expansionUpIcon!.name = 'up'; 48 this.expansionDownIcon!.name = 'down'; 49 this.logSummaryTable!.innerHTML = ''; 50 this.summaryDownLoadTbl!.recycleDataSource = []; 51 // @ts-ignore 52 let oneDayTime = (window as unknown).recordEndNS - 86400000000000; 53 if (systemLogDetailParam.hiLogs.length > 0) { 54 queryLogAllData(oneDayTime, systemLogDetailParam.leftNs, systemLogDetailParam.rightNs).then((res) => { 55 this.systemLogSource = res; 56 if (this.systemLogSource?.length !== 0 && systemLogDetailParam) { 57 this.refreshRowNodeTable(); 58 } 59 }); 60 } 61 } 62 63 initElements(): void { 64 this.logSummaryTable = this.shadowRoot?.querySelector<HTMLDivElement>('#tab-summary'); 65 this.summaryDownLoadTbl = this.shadowRoot?.querySelector<LitTable>('#tb-hilog-summary'); 66 this.expansionDiv = this.shadowRoot?.querySelector<HTMLDivElement>('.expansion-div'); 67 this.expansionUpIcon = this.shadowRoot?.querySelector<LitIcon>('.expansion-up-icon'); 68 this.expansionDownIcon = this.shadowRoot?.querySelector<LitIcon>('.expansion-down-icon'); 69 let summaryTreeLevel: string[] = ['Level', '/Process', '/Tag', '/Message']; 70 this.shadowRoot?.querySelectorAll<HTMLLabelElement>('.head-label').forEach((summaryTreeHead): void => { 71 summaryTreeHead.addEventListener('click', (): void => { 72 this.selectTreeDepth = summaryTreeLevel.indexOf(summaryTreeHead.textContent!); 73 this.expandedNodeList.clear(); 74 this.refreshSelectDepth(this.logTreeNodes); 75 this.refreshRowNodeTable(true); 76 }); 77 }); 78 this.logSummaryTable!.onscroll = (): void => { 79 let logTreeTableEl = this.shadowRoot?.querySelector<HTMLDivElement>('.log-tree-table'); 80 if (logTreeTableEl) { 81 logTreeTableEl.scrollTop = this.logSummaryTable?.scrollTop || 0; 82 } 83 }; 84 } 85 86 initHtml(): string { 87 return TabPaneHiLogSummaryHtml; 88 } 89 90 connectedCallback(): void { 91 super.connectedCallback(); 92 new ResizeObserver((): void => { 93 this.parentElement!.style.overflow = 'hidden'; 94 this.refreshRowNodeTable(); 95 }).observe(this.parentElement!); 96 this.expansionDiv?.addEventListener('click', this.expansionClickEvent); 97 } 98 99 disconnectedCallback(): void { 100 super.disconnectedCallback(); 101 this.expansionDiv?.removeEventListener('click', this.expansionClickEvent); 102 } 103 104 expansionClickEvent = (): void => { 105 this.expandedNodeList.clear(); 106 if (this.expansionUpIcon?.name === 'down') { 107 this.selectTreeDepth = 0; 108 this.expansionUpIcon!.name = 'up'; 109 this.expansionDownIcon!.name = 'down'; 110 } else { 111 this.selectTreeDepth = 4; 112 this.expansionUpIcon!.name = 'down'; 113 this.expansionDownIcon!.name = 'up'; 114 } 115 this.refreshSelectDepth(this.logTreeNodes); 116 this.refreshRowNodeTable(true); 117 }; 118 119 private refreshSelectDepth(logTreeNodes: LogTreeNode[]): void { 120 logTreeNodes.forEach((item): void => { 121 if (item.depth < this.selectTreeDepth) { 122 this.expandedNodeList.add(item.id); 123 if (item.children.length > 0) { 124 this.refreshSelectDepth(item.children); 125 } 126 } 127 }); 128 } 129 130 private createRowNodeTableEL( 131 rowNodeList: LogTreeNode[], 132 tableTreeEl: HTMLDivElement, 133 tableCountEl: HTMLDivElement, 134 rowColor: string = '' 135 ): void { 136 let unitPadding: number = 20; 137 let leftPadding: number = 5; 138 rowNodeList.forEach((rowNode): void => { 139 let tableTreeRowEl: HTMLElement = document.createElement('tr'); 140 tableTreeRowEl.className = 'tree-row-tr'; 141 tableTreeRowEl.title = rowNode.logName + ''; 142 let leftSpacingEl: HTMLElement = document.createElement('td'); 143 leftSpacingEl.style.paddingLeft = `${rowNode.depth * unitPadding + leftPadding}px`; 144 tableTreeRowEl.appendChild(leftSpacingEl); 145 this.addToggleIconEl(rowNode, tableTreeRowEl); 146 let rowNodeTextEL: HTMLElement = document.createElement('td'); 147 rowNodeTextEL.textContent = rowNode.logName + ''; 148 rowNodeTextEL.className = 'row-name-td'; 149 tableTreeRowEl.appendChild(rowNodeTextEL); 150 tableTreeEl.appendChild(tableTreeRowEl); 151 let tableCountRowEl: HTMLElement = document.createElement('tr'); 152 tableCountRowEl.title = rowNode.count.toString(); 153 let countEL: HTMLElement = document.createElement('td'); 154 countEL.textContent = rowNode.count.toString(); 155 countEL.className = 'count-column-td'; 156 if (rowNode.depth === 0) { 157 rowNodeTextEL.style.color = ColorUtils.getHilogColor(rowNode.logName!); 158 countEL.style.color = ColorUtils.getHilogColor(rowNode.logName!); 159 } else { 160 rowNodeTextEL.style.color = rowColor; 161 countEL.style.color = rowColor; 162 } 163 tableCountRowEl.appendChild(countEL); 164 tableCountEl.appendChild(tableCountRowEl); 165 if (rowNode.children && this.expandedNodeList.has(rowNode.id)) { 166 this.createRowNodeTableEL(rowNode.children, tableTreeEl, tableCountEl, countEL.style.color); 167 } 168 }); 169 } 170 171 private addToggleIconEl(rowNode: LogTreeNode, tableRowEl: HTMLElement): void { 172 let toggleIconEl: HTMLElement = document.createElement('td'); 173 let expandIcon = document.createElement('lit-icon'); 174 expandIcon.classList.add('tree-icon'); 175 if (rowNode.children && rowNode.children.length > 0) { 176 toggleIconEl.appendChild(expandIcon); 177 // @ts-ignore 178 expandIcon.name = this.expandedNodeList.has(rowNode.id) ? 'minus-square' : 'plus-square'; 179 toggleIconEl.classList.add('expand-icon'); 180 toggleIconEl.addEventListener('click', (): void => { 181 let scrollTop = this.logSummaryTable?.scrollTop ?? 0; 182 this.changeNode(rowNode.id); 183 this.logSummaryTable!.scrollTop = scrollTop; 184 }); 185 } 186 tableRowEl.appendChild(toggleIconEl); 187 } 188 189 private changeNode(currentNode: number): void { 190 if (this.expandedNodeList.has(currentNode)) { 191 this.expandedNodeList.delete(currentNode); 192 } else { 193 this.expandedNodeList.add(currentNode); 194 } 195 this.refreshRowNodeTable(); 196 } 197 198 private refreshRowNodeTable(useCacheRefresh: boolean = false): void { 199 this.logSummaryTable!.innerHTML = ''; 200 if (this.logSummaryTable && this.parentElement) { 201 this.logSummaryTable.style.height = `${this.parentElement!.clientHeight - NUM_30}px`; 202 } 203 if (!useCacheRefresh) { 204 this.logTreeNodes = this.buildTreeTblNodes(this.systemLogSource); 205 if (this.logTreeNodes.length > 0) { 206 this.summaryDownLoadTbl!.recycleDataSource = this.logTreeNodes; 207 } else { 208 this.summaryDownLoadTbl!.recycleDataSource = []; 209 } 210 } 211 let tableFragmentEl: DocumentFragment = document.createDocumentFragment(); 212 let tableTreeEl: HTMLDivElement = document.createElement('div'); 213 tableTreeEl.className = 'log-tree-table'; 214 let tableCountEl: HTMLDivElement = document.createElement('div'); 215 if (this.parentElement) { 216 tableTreeEl.style.height = `${this.parentElement!.clientHeight - NUM_40}px`; 217 } 218 this.createRowNodeTableEL(this.logTreeNodes, tableTreeEl, tableCountEl, ''); 219 let emptyTr = document.createElement('tr'); 220 emptyTr.className = 'tree-row-tr'; 221 tableTreeEl?.appendChild(emptyTr); 222 let emptyCountTr = document.createElement('tr'); 223 emptyCountTr.className = 'tree-row-tr'; 224 tableCountEl?.appendChild(emptyCountTr); 225 tableFragmentEl.appendChild(tableTreeEl); 226 tableFragmentEl.appendChild(tableCountEl); 227 this.logSummaryTable!.appendChild(tableFragmentEl); 228 } 229 230 private buildTreeTblNodes(logTreeNodes: LogStruct[]): LogTreeNode[] { 231 let id = 0; 232 let root: LogTreeNode = { id: id, depth: 0, children: [], logName: 'All', count: 0 }; 233 logTreeNodes.forEach((item) => { 234 id++; 235 let levelNode = root.children.find((node) => node.logName === item.level); 236 if (levelNode) { 237 levelNode.count++; 238 } else { 239 id++; 240 levelNode = { id: id, depth: 0, children: [], logName: item.level, count: 1 }; 241 root.children.push(levelNode); 242 } 243 let processNode = levelNode.children.find((node) => node.logName === item.processName); 244 if (processNode) { 245 processNode.count++; 246 } else { 247 id++; 248 processNode = { id: id, depth: 1, children: [], logName: item.processName, count: 1 }; 249 levelNode.children.push(processNode); 250 } 251 let tagNode = processNode.children.find((node) => node.logName === item.tag); 252 if (tagNode) { 253 tagNode.count++; 254 } else { 255 id++; 256 tagNode = { id: id, depth: 2, children: [], logName: item.tag, count: 1 }; 257 processNode.children.push(tagNode); 258 } 259 let messageNode = tagNode.children.find((node) => node.logName === item.context); 260 if (messageNode) { 261 messageNode.count++; 262 } else { 263 id++; 264 tagNode.children.push({ id: id, depth: 3, children: [], logName: item.context, count: 1 }); 265 } 266 root.count++; 267 }); 268 return root.children.sort((leftData, rightData) => { 269 return this.logLevel.indexOf(leftData.logName!) - this.logLevel.indexOf(rightData.logName!); 270 }); 271 } 272} 273 274export interface LogTreeNode { 275 id: number; 276 depth: number; 277 children: LogTreeNode[]; 278 logName: string | undefined; 279 count: number; 280} 281