• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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