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