Skip to content

Commit 5a751bd

Browse files
committed
update prototype.
1 parent 4c69bc1 commit 5a751bd

File tree

9 files changed

+105
-31
lines changed

9 files changed

+105
-31
lines changed

src/services/LogFileManager/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint max-lines: ["error", 400] */
22
import {
33
Decoder,
4-
DecoderOptionsType,
4+
DecoderOptions,
55
} from "../../typings/decoders";
66
import {MAX_V8_STRING_LENGTH} from "../../typings/js";
77
import {LogLevelFilter} from "../../typings/logs";
@@ -110,7 +110,7 @@ class LogFileManager {
110110
static async create (
111111
fileSrc: FileSrcType,
112112
pageSize: number,
113-
decoderOptions: DecoderOptionsType,
113+
decoderOptions: DecoderOptions,
114114
onQueryResults: (queryResults: QueryResults) => void,
115115
): Promise<LogFileManager> {
116116
const {fileName, fileData} = await loadFile(fileSrc);
@@ -138,13 +138,13 @@ class LogFileManager {
138138
static async #initDecoder (
139139
fileName: string,
140140
fileData: Uint8Array,
141-
decoderOptions: DecoderOptionsType
141+
decoderOptions: DecoderOptions
142142
): Promise<Decoder> {
143143
let decoder: Decoder;
144144
if (fileName.endsWith(".jsonl")) {
145145
decoder = new JsonlDecoder(fileData, decoderOptions);
146146
} else if (fileName.endsWith(".clp.zst")) {
147-
decoder = await ClpIrDecoder.create(fileData);
147+
decoder = await ClpIrDecoder.create(fileData, decoderOptions);
148148
} else {
149149
throw new Error(`No decoder supports ${fileName}`);
150150
}
@@ -161,7 +161,7 @@ class LogFileManager {
161161
/* Sets any formatter options that exist in the decoder's options.
162162
* @param options
163163
*/
164-
setFormatterOptions (options: DecoderOptionsType) {
164+
setFormatterOptions (options: DecoderOptions) {
165165
this.#decoder.setFormatterOptions(options);
166166
}
167167

src/services/MainWorker.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dayjs from "dayjs";
2+
import dayjsBigIntSupport from "dayjs/plugin/bigIntSupport";
23
import dayjsTimezone from "dayjs/plugin/timezone";
34
import dayjsUtc from "dayjs/plugin/utc";
45

@@ -17,6 +18,7 @@ import LogFileManager from "./LogFileManager";
1718
/* eslint-disable import/no-named-as-default-member */
1819
dayjs.extend(dayjsUtc);
1920
dayjs.extend(dayjsTimezone);
21+
dayjs.extend(dayjsBigIntSupport);
2022
/* eslint-enable import/no-named-as-default-member */
2123

2224
/**

src/services/decoders/ClpIrDecoder.ts

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,61 @@
1-
import clpFfiJsModuleInit, {ClpIrStreamReader} from "clp-ffi-js";
2-
1+
import clpFfiJsModuleInit, {ClpStreamReader} from "../../../deps/ClpFfiJs-worker";
32
import {Nullable} from "../../typings/common";
43
import {
4+
ClpIrDecoderOptions,
55
Decoder,
66
DecodeResultType,
77
FilteredLogEventMap,
88
LogEventCount,
99
} from "../../typings/decoders";
10-
import {LogLevelFilter} from "../../typings/logs";
10+
import {JsonObject} from "../../typings/js";
11+
import {
12+
LOG_LEVEL,
13+
LogLevelFilter,
14+
} from "../../typings/logs";
15+
import LogbackFormatter from "../formatters/LogbackFormatter";
16+
import {
17+
convertToDayjsTimestamp,
18+
isJsonObject,
19+
} from "./JsonlDecoder/utils";
1120

1221

1322
class ClpIrDecoder implements Decoder {
14-
#streamReader: ClpIrStreamReader;
23+
#streamReader: ClpStreamReader;
24+
25+
#formatter: Nullable<LogbackFormatter>;
26+
27+
#logLevelKey: Nullable<string>;
1528

16-
constructor (streamReader: ClpIrStreamReader) {
29+
constructor (streamReader: ClpStreamReader, formatter: Nullable<LogbackFormatter>, logLevelKey: Nullable<string>) {
1730
this.#streamReader = streamReader;
31+
this.#formatter = formatter;
32+
this.#logLevelKey = logLevelKey;
1833
}
1934

2035
/**
2136
* Creates a new ClpIrDecoder instance.
2237
*
2338
* @param dataArray The input data array to be passed to the decoder.
39+
* @param decoderOptions
2440
* @return The created ClpIrDecoder instance.
2541
*/
26-
static async create (dataArray: Uint8Array): Promise<ClpIrDecoder> {
42+
static async create (
43+
dataArray: Uint8Array,
44+
decoderOptions: ClpIrDecoderOptions
45+
): Promise<ClpIrDecoder> {
2746
const module = await clpFfiJsModuleInit();
28-
const streamReader = new module.ClpIrStreamReader(dataArray);
29-
return new ClpIrDecoder(streamReader);
47+
const streamReader = new module.ClpStreamReader(dataArray, decoderOptions);
48+
49+
let formatter: Nullable<LogbackFormatter> = null;
50+
let logLevelKey: Nullable<string> = null;
51+
if (
52+
module.IRProtocolErrorCode.SUPPORTED === streamReader.getIrProtocolErrorCode()
53+
) {
54+
formatter = new LogbackFormatter({formatString: decoderOptions.formatString});
55+
({logLevelKey} = decoderOptions);
56+
}
57+
58+
return new ClpIrDecoder(streamReader, formatter, logLevelKey);
3059
}
3160

3261
getEstimatedNumEvents (): number {
@@ -60,7 +89,36 @@ class ClpIrDecoder implements Decoder {
6089
endIdx: number,
6190
useFilter: boolean
6291
): Nullable<DecodeResultType[]> {
63-
return this.#streamReader.decodeRange(beginIdx, endIdx, useFilter);
92+
const results = this.#streamReader.decodeRange(beginIdx, endIdx, useFilter);
93+
if (null === this.#formatter) {
94+
return results;
95+
}
96+
results.forEach((result) => {
97+
const [message, timestamp] = result;
98+
let fields: JsonObject = {};
99+
try {
100+
fields = JSON.parse(message) as JsonObject;
101+
if (false === isJsonObject(fields)) {
102+
throw new Error("Unexpected non-object.");
103+
} else if (null !== this.#logLevelKey) {
104+
const logLevel = fields[this.#logLevelKey];
105+
fields[this.#logLevelKey] = LOG_LEVEL[logLevel as LOG_LEVEL];
106+
}
107+
} catch (e) {
108+
if (0 === message.length) {
109+
return;
110+
}
111+
console.error(e, message);
112+
}
113+
114+
// @ts-expect-error `this.#formatter` is certainly non-null
115+
result[0] = this.#formatter.formatLogEvent({
116+
fields: fields,
117+
timestamp: convertToDayjsTimestamp(timestamp),
118+
});
119+
});
120+
121+
return results;
64122
}
65123
}
66124

src/services/decoders/JsonlDecoder/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Decoder,
66
DecodeResultType,
77
FilteredLogEventMap,
8-
JsonlDecoderOptionsType,
8+
JsonlDecoderOptions,
99
LogEventCount,
1010
} from "../../../typings/decoders";
1111
import {Formatter} from "../../../typings/formatters";
@@ -25,7 +25,7 @@ import {
2525

2626

2727
/**
28-
* A decoder for JSONL (JSON lines) files that contain log events. See `JsonlDecoderOptionsType` for
28+
* A decoder for JSONL (JSON lines) files that contain log events. See `JsonlDecoderOptions` for
2929
* properties that are specific to log events (compared to generic JSON records).
3030
*/
3131
class JsonlDecoder implements Decoder {
@@ -49,7 +49,7 @@ class JsonlDecoder implements Decoder {
4949
* @param dataArray
5050
* @param decoderOptions
5151
*/
52-
constructor (dataArray: Uint8Array, decoderOptions: JsonlDecoderOptionsType) {
52+
constructor (dataArray: Uint8Array, decoderOptions: JsonlDecoderOptions) {
5353
this.#dataArray = dataArray;
5454
this.#logLevelKey = decoderOptions.logLevelKey;
5555
this.#timestampKey = decoderOptions.timestampKey;
@@ -81,7 +81,7 @@ class JsonlDecoder implements Decoder {
8181
};
8282
}
8383

84-
setFormatterOptions (options: JsonlDecoderOptionsType): boolean {
84+
setFormatterOptions (options: JsonlDecoderOptions): boolean {
8585
this.#formatter = new LogbackFormatter({formatString: options.formatString});
8686

8787
return true;

src/services/decoders/JsonlDecoder/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ const convertToLogLevelValue = (field: JsonValue | undefined): LOG_LEVEL => {
5353
* - the timestamp's value is an unsupported type.
5454
* - the timestamp's value is not a valid dayjs timestamp.
5555
*/
56-
const convertToDayjsTimestamp = (field: JsonValue | undefined): dayjs.Dayjs => {
56+
const convertToDayjsTimestamp = (field: JsonValue | bigint | undefined): dayjs.Dayjs => {
5757
// If the field is an invalid type, then set the timestamp to `INVALID_TIMESTAMP_VALUE`.
5858
// NOTE: dayjs surprisingly thinks `undefined` is a valid date. See
5959
// https://day.js.org/docs/en/parse/now#docsNav
6060
if (("string" !== typeof field &&
61-
"number" !== typeof field) ||
61+
"number" !== typeof field &&
62+
"bigint" !== typeof field) ||
6263
"undefined" === typeof field
6364
) {
6465
// `INVALID_TIMESTAMP_VALUE` is a valid dayjs date. Another potential option is

src/typings/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {JsonlDecoderOptionsType} from "./decoders";
1+
import {JsonlDecoderOptions} from "./decoders";
22
import {TAB_NAME} from "./tab";
33

44

@@ -27,7 +27,7 @@ enum LOCAL_STORAGE_KEY {
2727
/* eslint-enable @typescript-eslint/prefer-literal-enum-member */
2828

2929
type ConfigMap = {
30-
[CONFIG_KEY.DECODER_OPTIONS]: JsonlDecoderOptionsType,
30+
[CONFIG_KEY.DECODER_OPTIONS]: JsonlDecoderOptions,
3131
[CONFIG_KEY.INITIAL_TAB_NAME]: TAB_NAME,
3232
[CONFIG_KEY.THEME]: THEME_NAME,
3333
[CONFIG_KEY.PAGE_SIZE]: number,

src/typings/decoders.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,25 @@ interface LogEventCount {
1414
* @property logLevelKey The key of the kv-pair that contains the log level in every record.
1515
* @property timestampKey The key of the kv-pair that contains the timestamp in every record.
1616
*/
17-
interface JsonlDecoderOptionsType {
17+
interface JsonlDecoderOptions {
1818
formatString: string,
1919
logLevelKey: string,
2020
timestampKey: string,
2121
}
2222

23-
type DecoderOptionsType = JsonlDecoderOptionsType;
23+
/**
24+
* Options for the Structured CLP IR decoder.
25+
*/
26+
interface StructuredClpIrDecoderOptions extends JsonlDecoderOptions {
27+
}
28+
29+
/**
30+
* Options for the CLP IR decoder. Currently, those options are only effective if the stream is
31+
* Structured IR.
32+
*/
33+
type ClpIrDecoderOptions = StructuredClpIrDecoderOptions;
34+
35+
type DecoderOptions = JsonlDecoderOptions | ClpIrDecoderOptions;
2436

2537
/**
2638
* Type of the decoded log event. We use an array rather than object so that it's easier to return
@@ -85,7 +97,7 @@ interface Decoder {
8597
* @param options
8698
* @return Whether the options were successfully set.
8799
*/
88-
setFormatterOptions(options: DecoderOptionsType): boolean;
100+
setFormatterOptions(options: DecoderOptions): boolean;
89101

90102
/**
91103
* Decodes log events in the range `[beginIdx, endIdx)` of the filtered or unfiltered
@@ -106,10 +118,11 @@ interface Decoder {
106118

107119
export type {
108120
ActiveLogCollectionEventIdx,
121+
ClpIrDecoderOptions,
109122
Decoder,
110123
DecodeResultType,
111-
DecoderOptionsType,
124+
DecoderOptions,
112125
FilteredLogEventMap,
113-
JsonlDecoderOptionsType,
126+
JsonlDecoderOptions,
114127
LogEventCount,
115128
};

src/typings/worker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Nullable} from "./common";
22
import {
33
ActiveLogCollectionEventIdx,
4-
DecoderOptionsType,
4+
DecoderOptions,
55
} from "./decoders";
66
import {
77
LOG_LEVEL,
@@ -89,7 +89,7 @@ type WorkerReqMap = {
8989
fileSrc: FileSrcType,
9090
pageSize: number,
9191
cursor: CursorType,
92-
decoderOptions: DecoderOptionsType
92+
decoderOptions: DecoderOptions
9393
},
9494
[WORKER_REQ_CODE.LOAD_PAGE]: {
9595
cursor: CursorType,

src/utils/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
LOCAL_STORAGE_KEY,
77
THEME_NAME,
88
} from "../typings/config";
9-
import {DecoderOptionsType} from "../typings/decoders";
9+
import {DecoderOptions} from "../typings/decoders";
1010
import {TAB_NAME} from "../typings/tab";
1111

1212

@@ -131,7 +131,7 @@ const getConfig = <T extends CONFIG_KEY>(key: T): ConfigMap[T] => {
131131
timestampKey: window.localStorage.getItem(
132132
LOCAL_STORAGE_KEY.DECODER_OPTIONS_TIMESTAMP_KEY
133133
),
134-
} as DecoderOptionsType;
134+
} as DecoderOptions;
135135
break;
136136
case CONFIG_KEY.INITIAL_TAB_NAME:
137137
value = window.localStorage.getItem(LOCAL_STORAGE_KEY.INITIAL_TAB_NAME);

0 commit comments

Comments
 (0)