aboutsummaryrefslogtreecommitdiff
path: root/ui/src/tracks/android_log/logs_panel.ts
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/tracks/android_log/logs_panel.ts')
-rw-r--r--ui/src/tracks/android_log/logs_panel.ts226
1 files changed, 89 insertions, 137 deletions
diff --git a/ui/src/tracks/android_log/logs_panel.ts b/ui/src/tracks/android_log/logs_panel.ts
index 51e0889e2..3970190db 100644
--- a/ui/src/tracks/android_log/logs_panel.ts
+++ b/ui/src/tracks/android_log/logs_panel.ts
@@ -18,12 +18,10 @@ import {duration, Span, time, Time, TimeSpan} from '../../base/time';
import {Actions} from '../../common/actions';
import {raf} from '../../core/raf_scheduler';
import {DetailsShell} from '../../widgets/details_shell';
-import {VirtualScrollContainer} from '../../widgets/virtual_scroll_container';
-import {SELECTED_LOG_ROWS_COLOR} from '../../frontend/css_constants';
import {globals} from '../../frontend/globals';
import {Timestamp} from '../../frontend/widgets/timestamp';
-import {createStore, EngineProxy, LONG, NUM, Store, STR} from '../../public';
+import {EngineProxy, LONG, NUM, NUM_NULL, Store, STR} from '../../public';
import {Monitor} from '../../base/monitor';
import {AsyncLimiter} from '../../base/async_limiter';
import {escapeGlob, escapeQuery} from '../../trace_processor/query_utils';
@@ -31,6 +29,8 @@ import {Select} from '../../widgets/select';
import {Button} from '../../widgets/button';
import {TextInput} from '../../widgets/text_input';
import {Intent} from '../../widgets/common';
+import {VirtualTable, VirtualTableRow} from '../../widgets/virtual_table';
+import {classNames} from '../../base/classnames';
const ROW_H = 20;
@@ -63,15 +63,12 @@ interface LogEntries {
}
export class LogPanel implements m.ClassComponent<LogPanelAttrs> {
- private readonly SKIRT_SIZE = 50;
private entries?: LogEntries;
- private isStale = true;
- private viewportBounds = {top: 0, bottom: 0};
- private readonly paginationStore = createStore<Pagination>({
+ private pagination: Pagination = {
offset: 0,
count: 0,
- });
+ };
private readonly rowsMonitor: Monitor;
private readonly filterMonitor: Monitor;
private readonly queryLimiter = new AsyncLimiter();
@@ -81,7 +78,6 @@ export class LogPanel implements m.ClassComponent<LogPanelAttrs> {
() => attrs.filterStore.state,
() => globals.state.frontendLocalState.visibleState.start,
() => globals.state.frontendLocalState.visibleState.end,
- () => this.paginationStore.state,
]);
this.filterMonitor = new Monitor([() => attrs.filterStore.state]);
@@ -89,148 +85,104 @@ export class LogPanel implements m.ClassComponent<LogPanelAttrs> {
view({attrs}: m.CVnode<LogPanelAttrs>) {
if (this.rowsMonitor.ifStateChanged()) {
- this.queryLimiter.schedule(async () => {
- this.isStale = true;
- raf.scheduleFullRedraw();
-
- const visibleState = globals.state.frontendLocalState.visibleState;
- const visibleSpan = new TimeSpan(visibleState.start, visibleState.end);
-
- if (this.filterMonitor.ifStateChanged()) {
- await updateLogView(attrs.engine, attrs.filterStore.state);
- }
-
- this.entries = await updateLogEntries(
- attrs.engine,
- visibleSpan,
- this.paginationStore.state,
- );
-
- raf.scheduleFullRedraw();
- this.isStale = false;
- });
+ this.reloadData(attrs);
}
const hasProcessNames =
this.entries &&
this.entries.processName.filter((name) => name).length > 0;
+ const totalEvents = this.entries?.totalEvents ?? 0;
- const rows: m.Children = [];
- rows.push(
- m(
- `.row`,
- m('.cell.row-header', 'Timestamp'),
- m('.cell.row-header', 'Level'),
- m('.cell.row-header', 'Tag'),
- hasProcessNames
- ? m('.cell.with-process.row-header', 'Process name')
- : undefined,
- hasProcessNames
- ? m('.cell.with-process.row-header', 'Message')
- : m('.cell.no-process.row-header', 'Message'),
- m('br'),
- ),
+ return m(
+ DetailsShell,
+ {
+ title: 'Android Logs',
+ description: `Total messages: ${totalEvents}`,
+ buttons: m(LogsFilters, {store: attrs.filterStore}),
+ },
+ m(VirtualTable, {
+ className: 'pf-android-logs-table',
+ columns: [
+ {header: 'Timestamp', width: '7rem'},
+ {header: 'Level', width: '4rem'},
+ {header: 'Tag', width: '13rem'},
+ ...(hasProcessNames ? [{header: 'Process', width: '18rem'}] : []),
+ {header: 'Message', width: '42rem'},
+ ],
+ rows: this.renderRows(hasProcessNames),
+ firstRowOffset: this.entries?.offset ?? 0,
+ numRows: this.entries?.totalEvents ?? 0,
+ rowHeight: ROW_H,
+ onReload: (offset, count) => {
+ this.pagination = {offset, count};
+ this.reloadData(attrs);
+ },
+ onRowHover: (id) => {
+ const timestamp = this.entries?.timestamps[id];
+ if (timestamp !== undefined) {
+ globals.dispatch(Actions.setHoverCursorTimestamp({ts: timestamp}));
+ }
+ },
+ onRowOut: () => {
+ globals.dispatch(Actions.setHoverCursorTimestamp({ts: Time.INVALID}));
+ },
+ }),
);
- if (this.entries) {
- const offset = this.entries.offset;
- const timestamps = this.entries.timestamps;
- const priorities = this.entries.priorities;
- const tags = this.entries.tags;
- const messages = this.entries.messages;
- const processNames = this.entries.processName;
- const totalEvents = this.entries.totalEvents;
-
- for (let i = 0; i < this.entries.timestamps.length; i++) {
- const priorityLetter = LOG_PRIORITIES[priorities[i]][0];
- const ts = timestamps[i];
- const prioClass = priorityLetter || '';
- const style: {top: string; backgroundColor?: string} = {
- // 1.5 is for the width of the header
- top: `${(offset + i + 1.5) * ROW_H}px`,
- };
- if (this.entries.isHighlighted[i]) {
- style.backgroundColor = SELECTED_LOG_ROWS_COLOR;
- }
-
- rows.push(
- m(
- `.row.${prioClass}`,
- {
- class: this.isStale ? 'stale' : '',
- style,
- onmouseover: () => {
- globals.dispatch(Actions.setHoverCursorTimestamp({ts}));
- },
- onmouseout: () => {
- globals.dispatch(
- Actions.setHoverCursorTimestamp({ts: Time.INVALID}),
- );
- },
- },
- m('.cell', m(Timestamp, {ts})),
- m('.cell', priorityLetter || '?'),
- m('.cell', tags[i]),
- hasProcessNames
- ? m('.cell.with-process', processNames[i])
- : undefined,
- hasProcessNames
- ? m('.cell.with-process', messages[i])
- : m('.cell.no-process', messages[i]),
- m('br'),
- ),
- );
+ }
+
+ private reloadData(attrs: LogPanelAttrs) {
+ this.queryLimiter.schedule(async () => {
+ const visibleState = globals.state.frontendLocalState.visibleState;
+ const visibleSpan = new TimeSpan(visibleState.start, visibleState.end);
+
+ if (this.filterMonitor.ifStateChanged()) {
+ await updateLogView(attrs.engine, attrs.filterStore.state);
}
- return m(
- DetailsShell,
- {
- title: 'Android Logs',
- description: `[${this.viewportBounds.top}, ${this.viewportBounds.bottom}] / ${totalEvents}`,
- buttons: m(LogsFilters, {store: attrs.filterStore}),
- },
- m(
- VirtualScrollContainer,
- {
- onScroll: (scrollContainer: HTMLElement) => {
- this.recomputeVisibleRowsAndUpdate(scrollContainer);
- raf.scheduleFullRedraw();
- },
- },
- m(
- '.log-panel',
- m('.rows', {style: {height: `${totalEvents * ROW_H}px`}}, rows),
- ),
- ),
+ this.entries = await updateLogEntries(
+ attrs.engine,
+ visibleSpan,
+ this.pagination,
);
- }
- return null;
+ raf.scheduleFullRedraw();
+ });
}
- recomputeVisibleRowsAndUpdate(scrollContainer: HTMLElement) {
- const viewportTop = Math.floor(scrollContainer.scrollTop / ROW_H);
- const viewportHeight = Math.ceil(scrollContainer.clientHeight / ROW_H);
- const viewportBottom = viewportTop + viewportHeight;
-
- this.viewportBounds = {
- top: viewportTop,
- bottom: viewportBottom,
- };
-
- const curPage = this.paginationStore.state;
-
- if (
- viewportTop < curPage.offset ||
- viewportBottom >= curPage.offset + curPage.count
- ) {
- this.paginationStore.edit((draft) => {
- const offset = Math.max(0, viewportTop - this.SKIRT_SIZE);
- // Make it even so alternating coloured rows line up
- const offsetEven = Math.floor(offset / 2) * 2;
- draft.offset = offsetEven;
- draft.count = viewportHeight + this.SKIRT_SIZE * 2;
+ private renderRows(hasProcessNames: boolean | undefined): VirtualTableRow[] {
+ if (!this.entries) {
+ return [];
+ }
+
+ const timestamps = this.entries.timestamps;
+ const priorities = this.entries.priorities;
+ const tags = this.entries.tags;
+ const messages = this.entries.messages;
+ const processNames = this.entries.processName;
+
+ const rows: VirtualTableRow[] = [];
+ for (let i = 0; i < this.entries.timestamps.length; i++) {
+ const priorityLetter = LOG_PRIORITIES[priorities[i]][0];
+ const ts = timestamps[i];
+ const prioClass = priorityLetter || '';
+
+ rows.push({
+ id: i,
+ className: classNames(
+ prioClass,
+ this.entries.isHighlighted[i] && 'pf-highlighted',
+ ),
+ cells: [
+ m(Timestamp, {ts}),
+ priorityLetter || '?',
+ tags[i],
+ ...(hasProcessNames ? [processNames[i]] : []),
+ messages[i],
+ ],
});
}
+
+ return rows;
}
}
@@ -460,7 +412,7 @@ async function updateLogEntries(
prio: NUM,
tag: STR,
msg: STR,
- isMsgHighlighted: NUM,
+ isMsgHighlighted: NUM_NULL,
isProcessHighlighted: NUM,
processName: STR,
});