@logtape/adaptor-log4js 2.0.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mod.d.ts ADDED
@@ -0,0 +1,325 @@
1
+ import log4js from "log4js";
2
+ import { LogLevel, Sink } from "@logtape/logtape";
3
+
4
+ //#region src/mod.d.ts
5
+
6
+ /**
7
+ * Logger interface for log4js-compatible loggers.
8
+ * @since 2.0.0
9
+ */
10
+ interface Logger {
11
+ trace: LogMethod;
12
+ debug: LogMethod;
13
+ info: LogMethod;
14
+ warn: LogMethod;
15
+ error: LogMethod;
16
+ fatal: LogMethod;
17
+ addContext: (key: string, value: unknown) => void;
18
+ removeContext: (key: string) => void;
19
+ clearContext: () => void;
20
+ }
21
+ /**
22
+ * Log method signature for log4js.
23
+ * @since 2.0.0
24
+ */
25
+ interface LogMethod {
26
+ (message: string, ...args: unknown[]): void;
27
+ }
28
+ /**
29
+ * log4js log level type.
30
+ * @since 2.0.0
31
+ */
32
+ type Log4jsLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
33
+ /**
34
+ * Strategy for handling LogTape properties in log4js.
35
+ * @since 2.0.0
36
+ */
37
+ type ContextStrategy = "mdc" | "args";
38
+ /**
39
+ * Strategy for handling existing log4js context.
40
+ * @since 2.0.0
41
+ */
42
+ type ContextPreservation = "preserve" | "merge" | "replace";
43
+ /**
44
+ * Base configuration options shared by all context strategies.
45
+ * @since 2.0.0
46
+ */
47
+ interface Log4jsSinkOptionsBase {
48
+ /**
49
+ * Mapping between LogTape log levels and log4js log levels.
50
+ *
51
+ * By default, LogTape levels are mapped as follows:
52
+ *
53
+ * - `trace` → `trace`
54
+ * - `debug` → `debug`
55
+ * - `info` → `info`
56
+ * - `warning` → `warn`
57
+ * - `error` → `error`
58
+ * - `fatal` → `fatal`
59
+ */
60
+ readonly levelsMap?: Readonly<Record<LogLevel, Log4jsLevel>>;
61
+ /**
62
+ * Custom mapper function to convert LogTape categories to log4js category strings.
63
+ *
64
+ * By default, LogTape categories are joined with dots (e.g., `["app", "db"]` becomes `"app.db"`).
65
+ *
66
+ * @param category The LogTape category array
67
+ * @returns The log4js category string
68
+ * @default (category) => category.join(".")
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Use :: as separator
73
+ * categoryMapper: (cat) => cat.join("::")
74
+ * ```
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // Custom logic for category mapping
79
+ * categoryMapper: (cat) => {
80
+ * if (cat.length === 0) return "default";
81
+ * return cat.join(".");
82
+ * }
83
+ * ```
84
+ */
85
+ readonly categoryMapper?: (category: readonly string[]) => string;
86
+ /**
87
+ * Custom formatter for interpolated values in log messages.
88
+ *
89
+ * This function is used to convert values that are interpolated into
90
+ * log messages (e.g., the `name` in
91
+ * `logger.info("Hello, {name}!", { name: "world" })`).
92
+ *
93
+ * @param value The value to format
94
+ * @returns A string representation of the value
95
+ * @default `inspect` (from `node:util` module)
96
+ */
97
+ readonly valueFormatter?: (value: unknown) => string;
98
+ }
99
+ /**
100
+ * Configuration options for log4js sink with MDC (Mapped Diagnostic Context) strategy.
101
+ * @since 2.0.0
102
+ */
103
+ interface Log4jsSinkOptionsMdc extends Log4jsSinkOptionsBase {
104
+ /**
105
+ * Strategy for handling LogTape properties.
106
+ *
107
+ * Use `"mdc"` to leverage log4js's built-in MDC (Mapped Diagnostic Context) feature.
108
+ *
109
+ * @default "mdc"
110
+ */
111
+ readonly contextStrategy?: "mdc";
112
+ /**
113
+ * Strategy for handling existing log4js context when using MDC strategy.
114
+ *
115
+ * - `"preserve"` (default): Preserve existing context by saving and restoring it
116
+ * - `"merge"`: Merge LogTape properties with existing context (existing values take precedence)
117
+ * - `"replace"`: Replace existing context with LogTape properties
118
+ *
119
+ * @default "preserve"
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // Preserve existing context (default)
124
+ * const sink = getLog4jsSink(undefined, undefined, {
125
+ * contextStrategy: "mdc",
126
+ * contextPreservation: "preserve"
127
+ * });
128
+ * ```
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // Merge with existing context
133
+ * const sink = getLog4jsSink(undefined, undefined, {
134
+ * contextStrategy: "mdc",
135
+ * contextPreservation: "merge"
136
+ * });
137
+ * ```
138
+ */
139
+ readonly contextPreservation?: ContextPreservation;
140
+ }
141
+ /**
142
+ * Configuration options for log4js sink with args strategy.
143
+ * @since 2.0.0
144
+ */
145
+ interface Log4jsSinkOptionsArgs extends Log4jsSinkOptionsBase {
146
+ /**
147
+ * Strategy for handling LogTape properties.
148
+ *
149
+ * Use `"args"` to pass properties as additional arguments to log methods.
150
+ */
151
+ readonly contextStrategy: "args";
152
+ }
153
+ /**
154
+ * Configuration options for the log4js sink.
155
+ *
156
+ * This is a discriminated union type based on the `contextStrategy` option.
157
+ * When `contextStrategy` is `"mdc"` (or omitted), the `contextPreservation` option
158
+ * is available. When `contextStrategy` is `"args"`, the `contextPreservation` option
159
+ * is not allowed.
160
+ *
161
+ * @example Basic usage with default options (MDC strategy)
162
+ * ```typescript
163
+ * const sink = getLog4jsSink();
164
+ * ```
165
+ *
166
+ * @example Custom level mapping
167
+ * ```typescript
168
+ * const sink = getLog4jsSink(undefined, undefined, {
169
+ * levelsMap: {
170
+ * "trace": "debug",
171
+ * "debug": "debug",
172
+ * "info": "info",
173
+ * "warning": "warn",
174
+ * "error": "error",
175
+ * "fatal": "fatal"
176
+ * }
177
+ * });
178
+ * ```
179
+ *
180
+ * @example Custom category mapping
181
+ * ```typescript
182
+ * const sink = getLog4jsSink(undefined, undefined, {
183
+ * categoryMapper: (category) => category.join("::")
184
+ * });
185
+ * ```
186
+ *
187
+ * @example Using MDC strategy with preserve (default)
188
+ * ```typescript
189
+ * const sink = getLog4jsSink(undefined, undefined, {
190
+ * contextStrategy: "mdc",
191
+ * contextPreservation: "preserve"
192
+ * });
193
+ * ```
194
+ *
195
+ * @example Using args strategy
196
+ * ```typescript
197
+ * const sink = getLog4jsSink(undefined, undefined, {
198
+ * contextStrategy: "args"
199
+ * // contextPreservation is not allowed here
200
+ * });
201
+ * ```
202
+ *
203
+ * @since 2.0.0
204
+ */
205
+ type Log4jsSinkOptions = Log4jsSinkOptionsMdc | Log4jsSinkOptionsArgs;
206
+ /**
207
+ * Creates a LogTape sink that forwards log records to a log4js logger.
208
+ *
209
+ * This function creates a sink function that can be used with LogTape's
210
+ * configuration system. The sink will format LogTape log records and
211
+ * forward them to the provided log4js logger instance or create one
212
+ * based on the LogTape category.
213
+ *
214
+ * @example Basic usage with default log4js logger
215
+ * ```typescript
216
+ * import log4js from "log4js";
217
+ * import { configure } from "@logtape/logtape";
218
+ * import { getLog4jsSink } from "@logtape/adaptor-log4js";
219
+ *
220
+ * log4js.configure({
221
+ * appenders: { out: { type: "stdout" } },
222
+ * categories: { default: { appenders: ["out"], level: "info" } }
223
+ * });
224
+ *
225
+ * await configure({
226
+ * sinks: {
227
+ * log4js: getLog4jsSink()
228
+ * },
229
+ * loggers: [
230
+ * { category: ["myapp"], sinks: ["log4js"] }
231
+ * ]
232
+ * });
233
+ * ```
234
+ *
235
+ * @example With custom log4js logger
236
+ * ```typescript
237
+ * import log4js from "log4js";
238
+ * import { getLog4jsSink } from "@logtape/adaptor-log4js";
239
+ *
240
+ * const logger = log4js.getLogger("custom");
241
+ * const sink = getLog4jsSink(logger);
242
+ * ```
243
+ *
244
+ * @example With custom options
245
+ * ```typescript
246
+ * const sink = getLog4jsSink(undefined, {
247
+ * categoryMapper: (cat) => cat.join("::"),
248
+ * contextStrategy: "args",
249
+ * levelsMap: {
250
+ * "trace": "debug",
251
+ * "debug": "debug",
252
+ * "info": "info",
253
+ * "warning": "warn",
254
+ * "error": "error",
255
+ * "fatal": "fatal"
256
+ * }
257
+ * });
258
+ * ```
259
+ *
260
+ * @param log4jsModule The log4js module instance. If not provided, log4js will be imported dynamically.
261
+ * @param logger The log4js logger instance to forward logs to. If not provided,
262
+ * a logger will be created for each LogTape category using log4js.getLogger().
263
+ * @param options Configuration options for the sink behavior.
264
+ * @returns A sink function that can be used with LogTape's configure() function.
265
+ * @since 2.0.0
266
+ */
267
+ declare function getLog4jsSink(log4jsModule?: typeof log4js, logger?: Logger, options?: Log4jsSinkOptions): Sink;
268
+ /**
269
+ * Automatically configures LogTape to route all logs to log4js.
270
+ *
271
+ * This is a convenience function that automatically sets up LogTape to forward
272
+ * all log records to log4js. By default, it creates loggers based on LogTape
273
+ * categories using log4js.getLogger(), but you can provide a custom logger
274
+ * as the second parameter.
275
+ *
276
+ * @param log4jsModule The log4js module instance. If not provided, log4js will be imported dynamically.
277
+ * @param logger Optional log4js logger instance to use for all logs.
278
+ * @param options Configuration options for the log4js sink behavior.
279
+ *
280
+ * @example Basic auto-configuration
281
+ * ```typescript
282
+ * import log4js from "log4js";
283
+ * import { install } from "@logtape/adaptor-log4js";
284
+ *
285
+ * log4js.configure({
286
+ * appenders: { out: { type: "stdout" } },
287
+ * categories: { default: { appenders: ["out"], level: "info" } }
288
+ * });
289
+ *
290
+ * // Automatically route all LogTape logs to log4js
291
+ * install(log4js);
292
+ *
293
+ * // Now any LogTape-enabled library will log through log4js
294
+ * import { getLogger } from "@logtape/logtape";
295
+ * const logger = getLogger("my-app");
296
+ * logger.info("This will be logged through log4js");
297
+ * ```
298
+ *
299
+ * @example Auto-configuration with custom logger
300
+ * ```typescript
301
+ * import log4js from "log4js";
302
+ * import { install } from "@logtape/adaptor-log4js";
303
+ *
304
+ * const customLogger = log4js.getLogger("myapp");
305
+ *
306
+ * // Install with custom logger
307
+ * install(log4js, customLogger);
308
+ * ```
309
+ *
310
+ * @example Auto-configuration with custom options
311
+ * ```typescript
312
+ * import log4js from "log4js";
313
+ * import { install } from "@logtape/adaptor-log4js";
314
+ *
315
+ * install(log4js, undefined, {
316
+ * categoryMapper: (cat) => cat.join("::"),
317
+ * contextStrategy: "args"
318
+ * });
319
+ * ```
320
+ *
321
+ * @since 2.0.0
322
+ */
323
+ declare function install(log4jsModule: typeof log4js, logger?: Logger, options?: Log4jsSinkOptions): void;
324
+ //#endregion
325
+ export { ContextPreservation, ContextStrategy, Log4jsLevel, Log4jsSinkOptions, Log4jsSinkOptionsArgs, Log4jsSinkOptionsBase, Log4jsSinkOptionsMdc, LogMethod, Logger, getLog4jsSink, install };
package/dist/mod.js ADDED
@@ -0,0 +1,3 @@
1
+ import { getLog4jsSink, install } from "./src-Cb2nEPMQ.js";
2
+
3
+ export { getLog4jsSink, install };
@@ -0,0 +1,192 @@
1
+ import { configureSync } from "@logtape/logtape";
2
+ import { inspect } from "node:util";
3
+
4
+ //#region src/mod.ts
5
+ const DEFAULT_LEVELS_MAP = {
6
+ "trace": "trace",
7
+ "debug": "debug",
8
+ "info": "info",
9
+ "warning": "warn",
10
+ "error": "error",
11
+ "fatal": "fatal"
12
+ };
13
+ const DEFAULT_CATEGORY_MAPPER = (category) => category.length === 0 ? "" : category.join(".");
14
+ /**
15
+ * Creates a LogTape sink that forwards log records to a log4js logger.
16
+ *
17
+ * This function creates a sink function that can be used with LogTape's
18
+ * configuration system. The sink will format LogTape log records and
19
+ * forward them to the provided log4js logger instance or create one
20
+ * based on the LogTape category.
21
+ *
22
+ * @example Basic usage with default log4js logger
23
+ * ```typescript
24
+ * import log4js from "log4js";
25
+ * import { configure } from "@logtape/logtape";
26
+ * import { getLog4jsSink } from "@logtape/adaptor-log4js";
27
+ *
28
+ * log4js.configure({
29
+ * appenders: { out: { type: "stdout" } },
30
+ * categories: { default: { appenders: ["out"], level: "info" } }
31
+ * });
32
+ *
33
+ * await configure({
34
+ * sinks: {
35
+ * log4js: getLog4jsSink()
36
+ * },
37
+ * loggers: [
38
+ * { category: ["myapp"], sinks: ["log4js"] }
39
+ * ]
40
+ * });
41
+ * ```
42
+ *
43
+ * @example With custom log4js logger
44
+ * ```typescript
45
+ * import log4js from "log4js";
46
+ * import { getLog4jsSink } from "@logtape/adaptor-log4js";
47
+ *
48
+ * const logger = log4js.getLogger("custom");
49
+ * const sink = getLog4jsSink(logger);
50
+ * ```
51
+ *
52
+ * @example With custom options
53
+ * ```typescript
54
+ * const sink = getLog4jsSink(undefined, {
55
+ * categoryMapper: (cat) => cat.join("::"),
56
+ * contextStrategy: "args",
57
+ * levelsMap: {
58
+ * "trace": "debug",
59
+ * "debug": "debug",
60
+ * "info": "info",
61
+ * "warning": "warn",
62
+ * "error": "error",
63
+ * "fatal": "fatal"
64
+ * }
65
+ * });
66
+ * ```
67
+ *
68
+ * @param log4jsModule The log4js module instance. If not provided, log4js will be imported dynamically.
69
+ * @param logger The log4js logger instance to forward logs to. If not provided,
70
+ * a logger will be created for each LogTape category using log4js.getLogger().
71
+ * @param options Configuration options for the sink behavior.
72
+ * @returns A sink function that can be used with LogTape's configure() function.
73
+ * @since 2.0.0
74
+ */
75
+ function getLog4jsSink(log4jsModule, logger, options = {}) {
76
+ const { levelsMap = DEFAULT_LEVELS_MAP, categoryMapper = DEFAULT_CATEGORY_MAPPER, valueFormatter = inspect } = options;
77
+ const contextStrategy = options.contextStrategy ?? "mdc";
78
+ const contextPreservation = contextStrategy === "mdc" && "contextPreservation" in options ? options.contextPreservation ?? "preserve" : "preserve";
79
+ const loggerCache = /* @__PURE__ */ new Map();
80
+ const getLoggerForCategory = (category) => {
81
+ if (logger) return logger;
82
+ const categoryStr = categoryMapper(category);
83
+ if (loggerCache.has(categoryStr)) return loggerCache.get(categoryStr);
84
+ if (!log4jsModule) throw new Error("log4js module must be provided when not using a fixed logger");
85
+ const newLogger = categoryStr ? log4jsModule.getLogger(categoryStr) : log4jsModule.getLogger();
86
+ loggerCache.set(categoryStr, newLogger);
87
+ return newLogger;
88
+ };
89
+ return (record) => {
90
+ const targetLogger = getLoggerForCategory(record.category);
91
+ const level = levelsMap[record.level];
92
+ let message = "";
93
+ for (let i = 0; i < record.message.length; i += 2) {
94
+ message += record.message[i];
95
+ if (i + 1 < record.message.length) message += valueFormatter(record.message[i + 1]);
96
+ }
97
+ if (contextStrategy === "mdc") {
98
+ const propertiesToAdd = Object.entries(record.properties);
99
+ if (contextPreservation === "preserve") {
100
+ propertiesToAdd.forEach(([key, value]) => {
101
+ targetLogger.addContext(key, value);
102
+ });
103
+ targetLogger[level](message);
104
+ propertiesToAdd.forEach(([key]) => {
105
+ targetLogger.removeContext(key);
106
+ });
107
+ } else if (contextPreservation === "merge") {
108
+ propertiesToAdd.forEach(([key, value]) => {
109
+ targetLogger.addContext(key, value);
110
+ });
111
+ targetLogger[level](message);
112
+ } else {
113
+ targetLogger.clearContext();
114
+ propertiesToAdd.forEach(([key, value]) => {
115
+ targetLogger.addContext(key, value);
116
+ });
117
+ targetLogger[level](message);
118
+ }
119
+ } else targetLogger[level](message, record.properties);
120
+ };
121
+ }
122
+ /**
123
+ * Automatically configures LogTape to route all logs to log4js.
124
+ *
125
+ * This is a convenience function that automatically sets up LogTape to forward
126
+ * all log records to log4js. By default, it creates loggers based on LogTape
127
+ * categories using log4js.getLogger(), but you can provide a custom logger
128
+ * as the second parameter.
129
+ *
130
+ * @param log4jsModule The log4js module instance. If not provided, log4js will be imported dynamically.
131
+ * @param logger Optional log4js logger instance to use for all logs.
132
+ * @param options Configuration options for the log4js sink behavior.
133
+ *
134
+ * @example Basic auto-configuration
135
+ * ```typescript
136
+ * import log4js from "log4js";
137
+ * import { install } from "@logtape/adaptor-log4js";
138
+ *
139
+ * log4js.configure({
140
+ * appenders: { out: { type: "stdout" } },
141
+ * categories: { default: { appenders: ["out"], level: "info" } }
142
+ * });
143
+ *
144
+ * // Automatically route all LogTape logs to log4js
145
+ * install(log4js);
146
+ *
147
+ * // Now any LogTape-enabled library will log through log4js
148
+ * import { getLogger } from "@logtape/logtape";
149
+ * const logger = getLogger("my-app");
150
+ * logger.info("This will be logged through log4js");
151
+ * ```
152
+ *
153
+ * @example Auto-configuration with custom logger
154
+ * ```typescript
155
+ * import log4js from "log4js";
156
+ * import { install } from "@logtape/adaptor-log4js";
157
+ *
158
+ * const customLogger = log4js.getLogger("myapp");
159
+ *
160
+ * // Install with custom logger
161
+ * install(log4js, customLogger);
162
+ * ```
163
+ *
164
+ * @example Auto-configuration with custom options
165
+ * ```typescript
166
+ * import log4js from "log4js";
167
+ * import { install } from "@logtape/adaptor-log4js";
168
+ *
169
+ * install(log4js, undefined, {
170
+ * categoryMapper: (cat) => cat.join("::"),
171
+ * contextStrategy: "args"
172
+ * });
173
+ * ```
174
+ *
175
+ * @since 2.0.0
176
+ */
177
+ function install(log4jsModule, logger, options) {
178
+ configureSync({
179
+ sinks: { log4js: getLog4jsSink(log4jsModule, logger, options) },
180
+ loggers: [{
181
+ category: ["logtape", "meta"],
182
+ sinks: ["log4js"],
183
+ lowestLevel: "warning"
184
+ }, {
185
+ category: [],
186
+ sinks: ["log4js"]
187
+ }]
188
+ });
189
+ }
190
+
191
+ //#endregion
192
+ export { getLog4jsSink, install };