From 114af1dd433c168f33f986ecc18fdf6c5166c491 Mon Sep 17 00:00:00 2001 From: Dave Marco Date: Mon, 30 Sep 2024 23:22:11 +0000 Subject: [PATCH] remove 1 type cast --- .../services/decoders/JsonlDecoder/index.ts | 119 +++++++++--------- .../services/decoders/JsonlDecoder/utils.ts | 41 +++--- .../services/formatters/LogbackFormatter.ts | 2 +- new-log-viewer/src/typings/formatters.ts | 3 +- new-log-viewer/src/typings/logs.ts | 15 ++- 5 files changed, 93 insertions(+), 87 deletions(-) diff --git a/new-log-viewer/src/services/decoders/JsonlDecoder/index.ts b/new-log-viewer/src/services/decoders/JsonlDecoder/index.ts index 290e1284..13022be4 100644 --- a/new-log-viewer/src/services/decoders/JsonlDecoder/index.ts +++ b/new-log-viewer/src/services/decoders/JsonlDecoder/index.ts @@ -6,9 +6,11 @@ import { JsonlDecoderOptionsType, LogEventCount, } from "../../../typings/decoders"; +import {Dayjs} from "dayjs"; import {Formatter} from "../../../typings/formatters"; import {JsonValue} from "../../../typings/js"; import { + JsonLogEvent, INVALID_TIMESTAMP_VALUE, LOG_LEVEL, LogLevelFilter, @@ -18,7 +20,6 @@ import { convertToDayjsTimestamp, convertToLogLevelValue, isJsonObject, - JsonLogEvent, } from "./utils"; @@ -63,7 +64,7 @@ class JsonlDecoder implements Decoder { } setLogLevelFilter (logLevelFilter: LogLevelFilter): boolean { - this.#filterLogs(logLevelFilter); + this.#filterLogEvents(logLevelFilter); return true; } @@ -90,16 +91,12 @@ class JsonlDecoder implements Decoder { endIdx: number, useFilter: boolean, ): Nullable { - if (useFilter && null === this.#filteredLogEventMap) { + if (null === this.#filteredLogEventMap && useFilter) { return null; } - // Prevents typescript potential null warning. - const filteredLogEventMap: number[] = this.#filteredLogEventMap as number[]; - - - const length: number = useFilter ? - filteredLogEventMap.length : + const length: number = (useFilter && null !== this.#filteredLogEventMap) ? + this.#filteredLogEventMap.length : this.#logEvents.length; if (0 > beginIdx || length < endIdx) { @@ -111,51 +108,16 @@ class JsonlDecoder implements Decoder { // Explicit cast since typescript thinks `#filteredLogEventMap[i]` // can be undefined, but it shouldn't be since we performed a bounds check at the // beginning of the method. - const logEventIdx: number = useFilter ? - (filteredLogEventMap[i] as number) : + const logEventIdx: number = (useFilter && null !== this.#filteredLogEventMap) ? + (this.#filteredLogEventMap[i] as number) : i; - results.push(this.#getDecodeResult(logEventIdx)); + results.push(this.#decodeLogEvent(logEventIdx)); } return results; } - /** - * Decodes a log event into a `DecodeResultType`. - * - * @param logEventIdx - * @return The decoded log event. - */ - #getDecodeResult = (logEventIdx: number): DecodeResultType => { - let timestamp: number; - let message: string; - let logLevel: LOG_LEVEL; - - // eslint-disable-next-line no-warning-comments - // TODO We could probably optimize this to avoid checking `#invalidLogEventIdxToRawLine` on - // every iteration. - if (this.#invalidLogEventIdxToRawLine.has(logEventIdx)) { - timestamp = INVALID_TIMESTAMP_VALUE; - message = `${this.#invalidLogEventIdxToRawLine.get(logEventIdx)}\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 = this.#logEvents[logEventIdx] as JsonLogEvent; - logLevel = logEvent.level; - message = this.#formatter.formatLogEvent(logEvent); - timestamp = logEvent.timestamp.valueOf(); - } - - return [ - message, - timestamp, - logLevel, - logEventIdx + 1, - ]; - }; - /** * Parses each line from the data array and buffers it internally. * @@ -191,16 +153,16 @@ class JsonlDecoder implements Decoder { * @param line */ #parseJson (line: string) { + let fields: JsonValue; + let level: LOG_LEVEL; + let timestamp: Dayjs; try { - const fields = JSON.parse(line) as JsonValue; + fields = JSON.parse(line) as JsonValue; if (false === isJsonObject(fields)) { throw new Error("Unexpected non-object."); } - this.#logEvents.push({ - fields: fields, - level: convertToLogLevelValue(fields[this.#logLevelKey]), - timestamp: convertToDayjsTimestamp(fields[this.#timestampKey]), - }); + level = convertToLogLevelValue(fields[this.#logLevelKey]); + timestamp = convertToDayjsTimestamp(fields[this.#timestampKey]); } catch (e) { if (0 === line.length) { return; @@ -208,12 +170,15 @@ class JsonlDecoder implements Decoder { console.error(e, line); const currentLogEventIdx = this.#logEvents.length; this.#invalidLogEventIdxToRawLine.set(currentLogEventIdx, line); - this.#logEvents.push({ - fields: {}, - level: LOG_LEVEL.NONE, - timestamp: convertToDayjsTimestamp(INVALID_TIMESTAMP_VALUE), - }); + fields = {}; + level = LOG_LEVEL.NONE; + timestamp = convertToDayjsTimestamp(INVALID_TIMESTAMP_VALUE); } + this.#logEvents.push({ + fields, + level, + timestamp, + }); } /** @@ -222,7 +187,7 @@ class JsonlDecoder implements Decoder { * * @param logLevelFilter */ - #filterLogs (logLevelFilter: LogLevelFilter) { + #filterLogEvents (logLevelFilter: LogLevelFilter) { if (null === logLevelFilter) { this.#filteredLogEventMap = null; return; @@ -235,6 +200,42 @@ class JsonlDecoder implements Decoder { } }); } + + /** + * Decodes a log event into a `DecodeResultType`. + * + * @param logEventIdx + * @return The decoded log event. + */ + #decodeLogEvent = (logEventIdx: number): DecodeResultType => { + let timestamp: number; + let message: string; + let logLevel: LOG_LEVEL; + + // eslint-disable-next-line no-warning-comments + // TODO We could probably optimize this to avoid checking `#invalidLogEventIdxToRawLine` on + // every iteration. + if (this.#invalidLogEventIdxToRawLine.has(logEventIdx)) { + timestamp = INVALID_TIMESTAMP_VALUE; + message = `${this.#invalidLogEventIdxToRawLine.get(logEventIdx)}\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 = this.#logEvents[logEventIdx] as JsonLogEvent; + logLevel = logEvent.level; + message = this.#formatter.formatLogEvent(logEvent); + timestamp = logEvent.timestamp.valueOf(); + } + + return [ + message, + timestamp, + logLevel, + logEventIdx + 1, + ]; + }; } + export default JsonlDecoder; diff --git a/new-log-viewer/src/services/decoders/JsonlDecoder/utils.ts b/new-log-viewer/src/services/decoders/JsonlDecoder/utils.ts index f9fed268..14b93ddf 100644 --- a/new-log-viewer/src/services/decoders/JsonlDecoder/utils.ts +++ b/new-log-viewer/src/services/decoders/JsonlDecoder/utils.ts @@ -14,41 +14,35 @@ import { // eslint-disable-next-line import/no-named-as-default-member dayjs.extend(utc); -interface JsonLogEvent { - timestamp: Dayjs, - level: LOG_LEVEL, - fields: JsonObject -} - /** * Determines whether the given value is a `JsonObject` and applies a TypeScript narrowing * conversion if so. * * Reference: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates * - * @param fields - * @return A TypeScript type predicate indicating whether `fields` is a `JsonObject`. + * @param value + * @return A TypeScript type predicate indicating whether `value` is a `JsonObject`. */ -const isJsonObject = (fields: JsonValue): fields is JsonObject => { - return "object" === typeof fields && null !== fields; +const isJsonObject = (value: JsonValue): value is JsonObject => { + return "object" === typeof value && null !== value; }; /** * Converts a field into a log level if possible. * - * @param logLevelField + * @param field * @return The log level or `LOG_LEVEL.NONE` if the field couldn't be converted. */ -const convertToLogLevelValue = (logLevelField: JsonValue | undefined): LOG_LEVEL => { +const convertToLogLevelValue = (field: JsonValue | undefined): LOG_LEVEL => { let logLevelValue = LOG_LEVEL.NONE; - if ("undefined" === typeof logLevelField) { + if ("undefined" === typeof field) { return logLevelValue; } - const logLevelName = "object" === typeof logLevelField ? - JSON.stringify(logLevelField) : - String(logLevelField); + const logLevelName = "object" === typeof field ? + JSON.stringify(field) : + String(field); const uppercaseLogLevelName = logLevelName.toUpperCase(); if (uppercaseLogLevelName in LOG_LEVEL) { @@ -61,27 +55,27 @@ const convertToLogLevelValue = (logLevelField: JsonValue | undefined): LOG_LEVEL /** * Converts a field into a dayjs timestamp if possible. * - * @param timestampField + * @param field * @return The field as a dayjs timestamp or `dayjs.utc(INVALID_TIMESTAMP_VALUE)` if: * - the timestamp key doesn't exist in the log. * - the timestamp's value is an unsupported type. * - the timestamp's value is not a valid dayjs timestamp. */ -const convertToDayjsTimestamp = (timestampField: JsonValue | undefined): dayjs.Dayjs => { +const convertToDayjsTimestamp = (field: JsonValue | undefined): dayjs.Dayjs => { // If the field is an invalid type, then set the timestamp to `INVALID_TIMESTAMP_VALUE`. - if (("string" !== typeof timestampField && - "number" !== typeof timestampField) || + if (("string" !== typeof field && + "number" !== typeof field) || // dayjs surprisingly thinks `undefined` is a valid date: // https://day.js.org/docs/en/parse/now#docsNav - "undefined" === typeof timestampField + "undefined" === typeof field ) { // `INVALID_TIMESTAMP_VALUE` is a valid dayjs date. Another potential option is // `dayjs(null)` to show "Invalid Date" in the UI. - timestampField = INVALID_TIMESTAMP_VALUE; + field = INVALID_TIMESTAMP_VALUE; } - let dayjsTimestamp: Dayjs = dayjs.utc(timestampField); + let dayjsTimestamp: Dayjs = dayjs.utc(field); // Sanitize invalid (e.g., "deadbeef") timestamps to `INVALID_TIMESTAMP_VALUE`; otherwise // they'll show up in UI as "Invalid Date". @@ -96,4 +90,3 @@ export { convertToLogLevelValue, isJsonObject, }; -export type {JsonLogEvent}; diff --git a/new-log-viewer/src/services/formatters/LogbackFormatter.ts b/new-log-viewer/src/services/formatters/LogbackFormatter.ts index 63769984..93f23eb7 100644 --- a/new-log-viewer/src/services/formatters/LogbackFormatter.ts +++ b/new-log-viewer/src/services/formatters/LogbackFormatter.ts @@ -6,7 +6,7 @@ import { FormatterOptionsType, } from "../../typings/formatters"; import {JsonObject} from "../../typings/js"; -import {JsonLogEvent} from "../decoders/JsonlDecoder/utils"; +import {JsonLogEvent} from "../../typings/logs"; /** diff --git a/new-log-viewer/src/typings/formatters.ts b/new-log-viewer/src/typings/formatters.ts index 95afaafe..6ed90509 100644 --- a/new-log-viewer/src/typings/formatters.ts +++ b/new-log-viewer/src/typings/formatters.ts @@ -1,5 +1,4 @@ -import {JsonLogEvent} from "../services/decoders/JsonlDecoder/utils"; - +import {JsonLogEvent} from "./logs"; /** * Options for the LogbackFormatter. diff --git a/new-log-viewer/src/typings/logs.ts b/new-log-viewer/src/typings/logs.ts index 11ef5703..3c89e7c5 100644 --- a/new-log-viewer/src/typings/logs.ts +++ b/new-log-viewer/src/typings/logs.ts @@ -1,5 +1,10 @@ import {Nullable} from "./common"; +import {Dayjs} from "dayjs"; + +import { + JsonObject, +} from "./js"; enum LOG_LEVEL { NONE = 0, @@ -15,7 +20,15 @@ type LogLevelFilter = Nullable; const INVALID_TIMESTAMP_VALUE = 0; -export type {LogLevelFilter}; +interface JsonLogEvent { + timestamp: Dayjs, + level: LOG_LEVEL, + fields: JsonObject +} + +export type { + JsonLogEvent, + LogLevelFilter}; export { INVALID_TIMESTAMP_VALUE, LOG_LEVEL,