forked from y-scope/yscope-log-viewer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
108 lines (91 loc) · 4.05 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import {Nullable} from "../../../typings/common";
import {
FIELD_PLACEHOLDER_REGEX,
Formatter,
FormatterOptionsType,
REPLACEMENT_CHARACTER,
YscopeFieldFormatter,
YscopeFieldPlaceholder,
} from "../../../typings/formatters";
import {LogEvent} from "../../../typings/logs";
import {
getFormattedField,
jsonValueToString,
removeEscapeCharacters,
replaceDoubleBacklash,
splitFieldPlaceholder,
YSCOPE_FIELD_FORMATTER_MAP,
} from "./utils";
/**
* A formatter that uses a YScope format string to format log events into a string. See
* `YscopeFormatterOptionsType` for details about the format string.
*/
class YscopeFormatter implements Formatter {
readonly #processedFormatString: string;
#fieldPlaceholders: YscopeFieldPlaceholder[] = [];
constructor (options: FormatterOptionsType) {
if (options.formatString.includes(REPLACEMENT_CHARACTER)) {
console.warn("Unicode replacement character `U+FFFD` is found in Decoder Format" +
' String, which will appear as "\\".');
}
this.#processedFormatString = replaceDoubleBacklash(options.formatString);
this.#parseFieldPlaceholder();
}
formatLogEvent (logEvent: LogEvent): string {
// Empty format string is special case where formatter returns all fields as JSON.
if ("" === this.#processedFormatString) {
return jsonValueToString(logEvent.fields);
}
const formattedLogFragments: string[] = [];
let lastIndex = 0;
for (const fieldPlaceholder of this.#fieldPlaceholders) {
const formatStringFragment =
this.#processedFormatString.slice(lastIndex, fieldPlaceholder.range.start);
formattedLogFragments.push(removeEscapeCharacters(formatStringFragment));
formattedLogFragments.push(getFormattedField(logEvent, fieldPlaceholder));
lastIndex = fieldPlaceholder.range.end;
}
const remainder = this.#processedFormatString.slice(lastIndex);
formattedLogFragments.push(removeEscapeCharacters(remainder));
return `${formattedLogFragments.join("")}\n`;
}
/**
* Parses field placeholders in format string. For each field placeholder, creates a
* corresponding `YscopeFieldFormatter` using the placeholder's field name, formatter type,
* and formatter options. Each `YscopeFieldFormatter` is then stored on the
* class-level array `#fieldPlaceholders`.
*
* @throws Error if `FIELD_PLACEHOLDER_REGEX` does not contain a capture group.
* @throws Error if a formatter type is not supported.
*/
#parseFieldPlaceholder () {
const placeholderPattern = new RegExp(FIELD_PLACEHOLDER_REGEX, "g");
const it = this.#processedFormatString.matchAll(placeholderPattern);
for (const match of it) {
// `fullMatch` includes braces and `groupMatch` excludes them.
const [fullMatch, groupMatch]: (string | undefined) [] = match;
if ("undefined" === typeof groupMatch) {
throw Error("Field placeholder regex is invalid and does not have a capture group");
}
const {fieldNameKeys, formatterName, formatterOptions} =
splitFieldPlaceholder(groupMatch);
let fieldFormatter: Nullable<YscopeFieldFormatter> = null;
if (null !== formatterName) {
const FieldFormatterConstructor = YSCOPE_FIELD_FORMATTER_MAP[formatterName];
if ("undefined" === typeof FieldFormatterConstructor) {
throw Error(`Formatter ${formatterName} is not currently supported`);
}
fieldFormatter = new FieldFormatterConstructor(formatterOptions);
}
this.#fieldPlaceholders.push({
fieldNameKeys: fieldNameKeys,
fieldFormatter: fieldFormatter,
range: {
start: match.index,
end: match.index + fullMatch.length,
},
});
}
}
}
export default YscopeFormatter;