diff --git a/src/plugins/logs/__tests__/index.node.js b/src/plugins/logs/__tests__/index.node.js new file mode 100644 index 000000000..89b987112 --- /dev/null +++ b/src/plugins/logs/__tests__/index.node.js @@ -0,0 +1,58 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import {addEntriesToState, processEntry} from '../index'; + +const entry = { + tag: 'OpenGLRenderer', + pid: 18384, + + tid: 18409, + message: 'Swap behavior 1', + date: new Date('Feb 28 2013 19:00:00 EST'), + type: 'debug', +}; + +test('processEntry', () => { + const key = 'key'; + const processedEntry = processEntry(entry, key); + expect(processedEntry.entry).toEqual(entry); + expect(processedEntry.row.key).toBe(key); + expect(typeof processedEntry.row.height).toBe('number'); +}); + +test('addEntriesToState without current state', () => { + const processedEntry = processEntry(entry, 'key'); + const newState = addEntriesToState([processedEntry]); + + expect(newState.rows.length).toBe(1); + expect(newState.entries.length).toBe(1); + expect(newState.entries[0]).toEqual(processedEntry); +}); + +test('addEntriesToState with current state', () => { + const currentState = addEntriesToState([processEntry(entry, 'key1')]); + const processedEntry = processEntry( + { + ...entry, + message: 'new message', + }, + 'key2', + ); + const newState = addEntriesToState([processedEntry], currentState); + expect(newState.rows.length).toBe(2); + expect(newState.entries.length).toBe(2); +}); + +test('addEntriesToState increase counter on duplicate message', () => { + const currentState = addEntriesToState([processEntry(entry, 'key1')]); + const processedEntry = processEntry(entry, 'key2'); + const newState = addEntriesToState([processedEntry], currentState); + expect(newState.rows.length).toBe(1); + expect(newState.entries.length).toBe(2); + expect(newState.rows[0].columns.type.value.props.children).toBe(2); +}); diff --git a/src/plugins/logs/index.js b/src/plugins/logs/index.js index f2a80e420..34df1dc04 100644 --- a/src/plugins/logs/index.js +++ b/src/plugins/logs/index.js @@ -243,6 +243,136 @@ function pad(chunk: mixed, len: number): string { return str; } +export function addEntriesToState( + items: Entries, + state: $Shape = { + rows: [], + entries: [], + key2entry: {}, + }, +): $Shape { + const rows = [...state.rows]; + const entries = [...state.entries]; + const key2entry = {...state.key2entry}; + + for (let i = 0; i < items.length; i++) { + const {entry, row} = items[i]; + entries.push({row, entry}); + key2entry[row.key] = entry; + + let previousEntry: ?DeviceLogEntry = null; + + if (i > 0) { + previousEntry = items[i - 1].entry; + } else if (state.rows.length > 0 && state.entries.length > 0) { + previousEntry = state.entries[state.entries.length - 1].entry; + } + + addRowIfNeeded(rows, row, entry, previousEntry); + } + + return { + entries, + rows, + key2entry, + }; +} + +export function addRowIfNeeded( + rows: Array, + row: TableBodyRow, + entry: DeviceLogEntry, + previousEntry: ?DeviceLogEntry, +) { + const previousRow = rows.length > 0 ? rows[rows.length - 1] : null; + if ( + previousRow && + previousEntry && + entry.message === previousEntry.message && + entry.tag === previousEntry.tag && + previousRow.type != null + ) { + // duplicate log, increase counter + const count = + previousRow.columns.type.value && + previousRow.columns.type.value.props && + typeof previousRow.columns.type.value.props.children === 'number' + ? previousRow.columns.type.value.props.children + 1 + : 2; + const type = LOG_TYPES[previousRow.type] || LOG_TYPES.debug; + previousRow.columns.type.value = ( + {count} + ); + } else { + rows.push(row); + } +} + +export function processEntry( + entry: DeviceLogEntry, + key: string, +): { + row: TableBodyRow, + entry: DeviceLogEntry, +} { + console.log(JSON.stringify(entry)); + const {icon, style} = LOG_TYPES[(entry.type: string)] || LOG_TYPES.debug; + + // clean message + const message = entry.message.trim(); + entry.type === 'error'; + + // build the item, it will either be batched or added straight away + return { + entry, + row: { + columns: { + type: { + value: icon, + align: 'center', + }, + time: { + value: ( + + {entry.date.toTimeString().split(' ')[0] + + '.' + + pad(entry.date.getMilliseconds(), 3)} + + ), + }, + message: { + value: {message}, + }, + tag: { + value: {entry.tag}, + isFilterable: true, + }, + pid: { + value: ( + {String(entry.pid)} + ), + isFilterable: true, + }, + tid: { + value: ( + {String(entry.tid)} + ), + isFilterable: true, + }, + app: { + value: {entry.app}, + isFilterable: true, + }, + }, + height: getLineCount(message) * 15 + 10, // 15px per line height + 8px padding + style, + type: entry.type, + filterValue: entry.message, + key, + }, + }; +} + export default class LogTable extends FlipperDevicePlugin< State, Actions, @@ -322,8 +452,10 @@ export default class LogTable extends FlipperDevicePlugin< supportedColumns.includes(obj.key), ); - const initialState = this.addEntriesToState( - this.device.getLogs().map(this.processEntry), + const initialState = addEntriesToState( + this.device + .getLogs() + .map(log => processEntry(log, String(this.counter++))), ); this.state = { ...initialState, @@ -335,7 +467,7 @@ export default class LogTable extends FlipperDevicePlugin< }; this.logListener = this.device.addLogListener((entry: DeviceLogEntry) => { - const processedEntry = this.processEntry(entry); + const processedEntry = processEntry(entry, String(this.counter++)); this.incrementCounterIfNeeded(processedEntry.entry); this.scheudleEntryForBatch(processedEntry); }); @@ -365,73 +497,6 @@ export default class LogTable extends FlipperDevicePlugin< } }; - processEntry = ( - entry: DeviceLogEntry, - ): { - row: TableBodyRow, - entry: DeviceLogEntry, - } => { - const {icon, style} = LOG_TYPES[(entry.type: string)] || LOG_TYPES.debug; - - // clean message - const message = entry.message.trim(); - entry.type === 'error'; - - // build the item, it will either be batched or added straight away - return { - entry, - row: { - columns: { - type: { - value: icon, - align: 'center', - }, - time: { - value: ( - - {entry.date.toTimeString().split(' ')[0] + - '.' + - pad(entry.date.getMilliseconds(), 3)} - - ), - }, - message: { - value: {message}, - }, - tag: { - value: {entry.tag}, - isFilterable: true, - }, - pid: { - value: ( - - {String(entry.pid)} - - ), - isFilterable: true, - }, - tid: { - value: ( - - {String(entry.tid)} - - ), - isFilterable: true, - }, - app: { - value: {entry.app}, - isFilterable: true, - }, - }, - height: getLineCount(message) * 15 + 10, // 15px per line height + 8px padding - style, - type: entry.type, - filterValue: entry.message, - key: String(this.counter++), - }, - }; - }; - scheudleEntryForBatch = (item: { row: TableBodyRow, entry: DeviceLogEntry, @@ -448,46 +513,11 @@ export default class LogTable extends FlipperDevicePlugin< const thisBatch = this.batch; this.batch = []; this.queued = false; - this.setState(state => this.addEntriesToState(thisBatch, state)); + this.setState(state => addEntriesToState(thisBatch, state)); }, 100); } }; - addEntriesToState = ( - items: Entries, - state: $Shape = { - rows: [], - entries: [], - key2entry: {}, - }, - ): $Shape => { - const rows = [...state.rows]; - const entries = [...state.entries]; - const key2entry = {...state.key2entry}; - - for (let i = 0; i < items.length; i++) { - const {entry, row} = items[i]; - entries.push({row, entry}); - key2entry[row.key] = entry; - - let previousEntry: ?DeviceLogEntry = null; - - if (i > 0) { - previousEntry = items[i - 1].entry; - } else if (state.rows.length > 0 && state.entries.length > 0) { - previousEntry = state.entries[state.entries.length - 1].entry; - } - - this.addRowIfNeeded(rows, row, entry, previousEntry); - } - - return { - entries, - rows, - key2entry, - }; - }; - componentWillUnmount() { if (this.batchTimer) { clearTimeout(this.batchTimer); @@ -498,36 +528,6 @@ export default class LogTable extends FlipperDevicePlugin< } } - addRowIfNeeded( - rows: Array, - row: TableBodyRow, - entry: DeviceLogEntry, - previousEntry: ?DeviceLogEntry, - ) { - const previousRow = rows.length > 0 ? rows[rows.length - 1] : null; - if ( - previousRow && - previousEntry && - entry.message === previousEntry.message && - entry.tag === previousEntry.tag && - previousRow.type != null - ) { - // duplicate log, increase counter - const count = - previousRow.columns.type.value && - previousRow.columns.type.value.props && - typeof previousRow.columns.type.value.props.children === 'number' - ? previousRow.columns.type.value.props.children + 1 - : 2; - const type = LOG_TYPES[previousRow.type] || LOG_TYPES.debug; - previousRow.columns.type.value = ( - {count} - ); - } else { - rows.push(row); - } - } - clearLogs = () => { this.setState({ entries: [],