|
| 1 | +import path from "node:path"; |
| 2 | +import url from "node:url"; |
1 | 3 | import util from "node:util";
|
2 | 4 | import { isNativeError } from "node:util/types";
|
3 | 5 |
|
4 | 6 | import type log4js from "log4js";
|
5 | 7 |
|
6 | 8 | interface Output {
|
7 |
| - time: Date; // ISO-8601 format |
8 |
| - category: string; // categoy name of log4js instance |
9 |
| - level: string; // log4js.Level.levelStr; |
10 |
| - msg: string; // data passed to log argument, formated using `util.format` |
| 9 | + /** |
| 10 | + * ISO-8601 format |
| 11 | + */ |
| 12 | + time: Date; |
| 13 | + /** |
| 14 | + * Category name of log4js instance. |
| 15 | + */ |
| 16 | + category: string; |
| 17 | + /** |
| 18 | + * log4js.Level.levelStr |
| 19 | + */ |
| 20 | + level: string; |
| 21 | + /** |
| 22 | + * Data passed to log argument, formatted using `util.format`. |
| 23 | + */ |
| 24 | + msg?: string; |
| 25 | + /** |
| 26 | + * The name of the file where the log message originated |
| 27 | + */ |
| 28 | + file_name?: string; |
| 29 | + /** |
| 30 | + * The name of the function where the log message originated |
| 31 | + */ |
| 32 | + function_name?: string; |
| 33 | +} |
| 34 | + |
| 35 | +/** |
| 36 | + * Parses the file name from a logging event |
| 37 | + */ |
| 38 | +function parseFileName(loggingEvent: log4js.LoggingEvent): string | undefined { |
| 39 | + let filename = loggingEvent.fileName || ""; |
| 40 | + if (filename.startsWith("file://")) { |
| 41 | + filename = url.fileURLToPath(filename); |
| 42 | + } |
| 43 | + |
| 44 | + return filename.split(path.sep).at(-1); |
11 | 45 | }
|
12 | 46 |
|
13 |
| -function formatter( |
14 |
| - event: log4js.LoggingEvent, |
15 |
| - config: Config, |
16 |
| -): Output { |
17 |
| - let output = { |
| 47 | +/** |
| 48 | + * Formats a log event into the desired output format |
| 49 | + */ |
| 50 | +function format(event: log4js.LoggingEvent, config?: Config): Output { |
| 51 | + const output: Output = { |
18 | 52 | time: event.startTime,
|
19 | 53 | category: event.categoryName,
|
20 | 54 | level: event.level.levelStr,
|
21 |
| - msg: "", |
22 |
| - } as Output; |
| 55 | + }; |
23 | 56 |
|
24 |
| - if (config.withContext) { |
| 57 | + if (config?.includeContext) { |
25 | 58 | Object.assign(output, event.context);
|
26 | 59 | }
|
27 | 60 |
|
28 |
| - let messages: Array<any> | undefined; |
| 61 | + if (config?.includeFunctionName && event.functionName) { |
| 62 | + output.function_name = event.functionName; |
| 63 | + } |
| 64 | + |
| 65 | + if (config?.includeFileName) { |
| 66 | + const filename = parseFileName(event); |
| 67 | + if (filename) { |
| 68 | + output.file_name = filename; |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + let msgs: Array<any> | undefined; |
29 | 73 | if (Array.isArray(event.data)) {
|
30 |
| - messages = event.data; |
| 74 | + msgs = event.data; |
31 | 75 | } else {
|
32 |
| - messages = [event.data]; |
| 76 | + msgs = [event.data]; |
33 | 77 | }
|
34 | 78 |
|
35 |
| - messages = messages |
| 79 | + msgs = msgs |
36 | 80 | .filter((m) => isNativeError(m) || typeof m !== "object")
|
37 | 81 | .filter(Boolean);
|
38 | 82 |
|
39 |
| - output.msg = util.format(...messages); |
| 83 | + output.msg = util.format(...msgs); |
| 84 | + |
| 85 | + if (output.msg === undefined) { |
| 86 | + delete output.msg; |
| 87 | + } |
40 | 88 |
|
41 | 89 | return output;
|
42 | 90 | }
|
43 | 91 |
|
44 | 92 | export interface Config {
|
45 | 93 | /**
|
46 | 94 | * Include context added using `log.addContext()`
|
| 95 | + * |
47 | 96 | * @default true
|
48 | 97 | */
|
49 |
| - withContext?: boolean; |
| 98 | + includeContext?: boolean; |
| 99 | + /** |
| 100 | + * Include function name in json output. |
| 101 | + * |
| 102 | + * @default false |
| 103 | + */ |
| 104 | + includeFileName?: boolean; |
| 105 | + /** |
| 106 | + * Include function name in json output. |
| 107 | + * |
| 108 | + * @requires log4js>=6.7 |
| 109 | + * @default false |
| 110 | + */ |
| 111 | + includeFunctionName?: boolean; |
50 | 112 | }
|
51 | 113 |
|
52 |
| -const defaults: Config = { |
53 |
| - withContext: true, |
54 |
| -}; |
| 114 | +const defaults = { |
| 115 | + includeContext: true, |
| 116 | + includeFileName: false, |
| 117 | + includeFunctionName: false, |
| 118 | +} satisfies Config; |
55 | 119 |
|
56 |
| -export function jsonLayout(config?: Config): log4js.LayoutFunction { |
57 |
| - config = Object.assign({}, config, defaults); |
| 120 | +/** |
| 121 | + * Creates a JSON layout function for log4js. |
| 122 | + */ |
| 123 | +export function layout(config?: Config): log4js.LayoutFunction { |
| 124 | + config = Object.assign({}, defaults, config); |
58 | 125 |
|
59 | 126 | return function layout(event: log4js.LoggingEvent): string {
|
60 |
| - const formated = formatter(event, config); |
61 |
| - |
| 127 | + const formated = format(event, config); |
62 | 128 | const output = JSON.stringify(formated);
|
| 129 | + |
63 | 130 | return output;
|
64 | 131 | };
|
65 | 132 | }
|
66 |
| - |
67 |
| -export default jsonLayout; |
68 |
| -module.exports = jsonLayout; |
|
0 commit comments