From b70bdb62084157ef37927d4ce5251f87cf9bdb6d Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Wed, 18 Sep 2024 23:11:01 +0000 Subject: [PATCH] new interface: --- new-log-viewer/src/services/LogFileManager.ts | 30 +++++++++-- .../src/services/decoders/JsonlDecoder.ts | 53 +++++++++++-------- new-log-viewer/src/typings/decoders.ts | 15 +++++- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/new-log-viewer/src/services/LogFileManager.ts b/new-log-viewer/src/services/LogFileManager.ts index 145855a8..41cd98d2 100644 --- a/new-log-viewer/src/services/LogFileManager.ts +++ b/new-log-viewer/src/services/LogFileManager.ts @@ -89,7 +89,7 @@ class LogFileManager { } this.#numEvents = decoder.getEstimatedNumEvents(); - this.#computePageBoundaries(); + this.#computeUnfilteredPageBoundaries(); console.log( `Found ${this.#numEvents} log events.`, ); @@ -183,7 +183,7 @@ class LogFileManager { console.debug(`loadPage: cursor=${JSON.stringify(cursor)}`); const {beginLogEventNum, endLogEventNum} = this.#getCursorRange(cursor); - const results = this.#decoder.decode(beginLogEventNum - 1, endLogEventNum); + const results = this.#decoder.decodeFilteredRange(beginLogEventNum - 1, endLogEventNum); if (null === results) { throw new Error("Error occurred during decoding. " + `beginLogEventNum=${beginLogEventNum}, ` + @@ -226,7 +226,7 @@ class LogFileManager { throw new Error("Error changing log level filter"); } - this.#computePageBoundaries(); + logLevelFilter ? this.#computeFilteredPageBoundaries() : this.#computeUnfilteredPageBoundaries() } /** @@ -234,7 +234,7 @@ class LogFileManager { * boundaries. The first array contains the number of first log event on each page. The * second array contains the number last log event on each page. */ - #computePageBoundaries () { + #computeFilteredPageBoundaries () { this.#firstLogEventNumPerPage.length = 0; this.#lastLogEventNumPerPage.length = 0; @@ -242,6 +242,7 @@ class LogFileManager { this.#numFilteredEvents = filteredLogEventsIndices.length; for (let i = 0; i < this.#numFilteredEvents; i += this.#pageSize) { + const firstLogEventOnPageIdx: number = filteredLogEventsIndices[i] as number; this.#firstLogEventNumPerPage.push(1 + firstLogEventOnPageIdx); @@ -258,6 +259,27 @@ class LogFileManager { } } + #computeUnfilteredPageBoundaries () { + this.#firstLogEventNumPerPage.length = 0; + this.#lastLogEventNumPerPage.length = 0; + + this.#numFilteredEvents = this.#numEvents; + + for (let i = 0; i < this.#numFilteredEvents; i += this.#pageSize) { + this.#firstLogEventNumPerPage.push(1 + i); + + // Need to minus one from page size to get correct index into filtered log events. + let lastPageIdx: number = i + this.#pageSize - 1; + + // Guard to prevent indexing out of array on last page. + if (lastPageIdx >= this.#numFilteredEvents) { + lastPageIdx = this.#numFilteredEvents - 1; + } + + this.#lastLogEventNumPerPage.push(1 + lastPageIdx); + } + } + /** * Gets the range of log event numbers for the page containing the given cursor. * diff --git a/new-log-viewer/src/services/decoders/JsonlDecoder.ts b/new-log-viewer/src/services/decoders/JsonlDecoder.ts index fd28a105..e1498bb6 100644 --- a/new-log-viewer/src/services/decoders/JsonlDecoder.ts +++ b/new-log-viewer/src/services/decoders/JsonlDecoder.ts @@ -20,6 +20,7 @@ import { LogLevelFilter, } from "../../typings/logs"; import LogbackFormatter from "../formatters/LogbackFormatter"; +import { number } from "prop-types"; /** @@ -53,6 +54,8 @@ class JsonlDecoder implements Decoder { #logEvents: JsonLogEvent[] = []; + #isFiltered: boolean; + #filteredLogIndices: number[]; #invalidLogEventIdxToRawLine: Map = new Map(); @@ -66,11 +69,11 @@ class JsonlDecoder implements Decoder { */ constructor (dataArray: Uint8Array, decoderOptions: JsonlDecoderOptions) { this.#filteredLogIndices = []; - this.#logLevelKey = decoderOptions.logLevelKey; this.#timestampKey = decoderOptions.timestampKey; this.#formatter = new LogbackFormatter(decoderOptions); this.#dataArray = dataArray; + this.#isFiltered = false; } getEstimatedNumEvents (): number { @@ -87,7 +90,6 @@ class JsonlDecoder implements Decoder { } this.#deserialize(); - this.#filterLogs(null); const numInvalidEvents = Array.from(this.#invalidLogEventIdxToRawLine.keys()).length; @@ -99,37 +101,48 @@ class JsonlDecoder implements Decoder { changeLogLevelFilter (logLevelFilter: LogLevelFilter): boolean { this.#filterLogs(logLevelFilter); - + this.#isFiltered = logLevelFilter ? true : false; return true; } - decode (beginIdx: number, endIdx: number): Nullable { - if (0 > beginIdx || this.#filteredLogIndices.length < endIdx) { + decodeRange (beginIdx: number, endIdx: number): Nullable { + return this.#decodeAnyRange(beginIdx,endIdx,false) + } + + decodeFilteredRange (beginIdx: number, endIdx: number): Nullable { + return this.#decodeAnyRange(beginIdx,endIdx,this.#isFiltered) + } + + #decodeAnyRange (beginIdx: number, endIdx: number, useFilter: boolean): Nullable { + let length: number = useFilter ? this.#filteredLogIndices.length : this.#logEvents.length + + if (0 > beginIdx || length < endIdx) { return null; } - // eslint-disable-next-line no-warning-comments // TODO We could probably optimize this to avoid checking `#invalidLogEventIdxToRawLine` on // every iteration. const results: DecodeResultType[] = []; - for (let filteredLogEventIdx = beginIdx; - filteredLogEventIdx < endIdx; - filteredLogEventIdx++) { + for (let logEventIdx = beginIdx; logEventIdx < endIdx; logEventIdx++) { + let timestamp: number; + let message: string; + let logLevel: LOG_LEVEL; + // Explicit cast since typescript thinks `#filteredLogIndices[filteredLogEventIdx]` can // be undefined, but it shouldn't be since we performed a bounds check at the beginning // of the method. - const logEventIdx = this.#filteredLogIndices[filteredLogEventIdx] as number; - let logLevel: LOG_LEVEL; - let message: string; - let timestamp: number; - if (this.#invalidLogEventIdxToRawLine.has(logEventIdx)) { - logLevel = LOG_LEVEL.NONE; - message = `${this.#invalidLogEventIdxToRawLine.get(logEventIdx)}\n`; + let filteredIdx: number = useFilter ? + this.#filteredLogIndices[logEventIdx] as number : + logEventIdx + + if (this.#invalidLogEventIdxToRawLine.has(filteredIdx)) { timestamp = INVALID_TIMESTAMP_VALUE; + message = `${this.#invalidLogEventIdxToRawLine.get(filteredIdx)}\n`; + logLevel = LOG_LEVEL.NONE; } else { // Explicit cast since typescript thinks `#logEvents[logEventIdx]` can be undefined, // but it shouldn't be since the index comes from a class-internal filter. - const logEvent: JsonLogEvent = this.#logEvents[logEventIdx] as JsonLogEvent; + const logEvent: JsonLogEvent = this.#logEvents[filteredIdx] as JsonLogEvent; logLevel = logEvent.level; message = this.#formatter.formatLogEvent(logEvent); timestamp = logEvent.timestamp.valueOf(); @@ -139,7 +152,7 @@ class JsonlDecoder implements Decoder { message, timestamp, logLevel, - logEventIdx + 1, + filteredIdx + 1, ]); } @@ -204,9 +217,7 @@ class JsonlDecoder implements Decoder { #filterLogs (logLevelFilter: LogLevelFilter) { this.#filteredLogIndices.length = 0; - if (!logLevelFilter || 0 === logLevelFilter.length) { - this.#filteredLogIndices = createIndicesArray(this.#logEvents.length); - + if (!logLevelFilter) { return; } diff --git a/new-log-viewer/src/typings/decoders.ts b/new-log-viewer/src/typings/decoders.ts index 3f444d02..3a25f2a7 100644 --- a/new-log-viewer/src/typings/decoders.ts +++ b/new-log-viewer/src/typings/decoders.ts @@ -84,14 +84,25 @@ interface Decoder { buildIdx(beginIdx: number, endIdx: number): Nullable; /** - * Decodes the log events in the range `[beginIdx, endIdx)`. + * Decodes filtered log events (i.e. only includes events are included in current filter) in + * the range `[beginIdx, endIdx)`. * * @param beginIdx * @param endIdx * @return The decoded log events on success or null if any log event in the range doesn't exist * (e.g., the range exceeds the number of log events in the file). */ - decode(beginIdx: number, endIdx: number): Nullable; + decodeFilteredRange(beginIdx: number, endIdx: number): Nullable; + + /** + * Decodes the all log events in the range `[beginIdx, endIdx)` irrespective of filter applied. + * + * @param beginIdx + * @param endIdx + * @return The decoded log events on success or null if any log event in the range doesn't exist + * (e.g., the range exceeds the number of log events in the file). + */ + decodeRange(beginIdx: number, endIdx: number): Nullable; } /**