• 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  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