@likec4/log 1.49.0 → 1.50.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/index.d.mts +37 -0
- package/dist/index.mjs +119 -14
- package/package.json +4 -4
- package/src/formatters.ts +36 -10
- package/src/index.ts +13 -7
- package/src/sink.ts +7 -0
- package/src/utils.ts +16 -0
package/dist/index.d.mts
CHANGED
|
@@ -2,28 +2,61 @@ import * as _logtape_logtape0 from "@logtape/logtape";
|
|
|
2
2
|
import { AnsiColorFormatterOptions, Config, ConsoleFormatter, ConsoleSinkOptions, Filter, LogLevel, LogRecord, LogRecord as LogRecord$1, Logger, Sink, Sink as Sink$1, TextFormatter, TextFormatter as TextFormatter$1, TextFormatterOptions, withFilter } from "@logtape/logtape";
|
|
3
3
|
|
|
4
4
|
//#region src/formatters.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Extract a single Error from a log record (from properties or rawMessage).
|
|
7
|
+
* @param record - Log record that may contain error in properties or rawMessage
|
|
8
|
+
* @returns Merged/wrapped Error or null if none found
|
|
9
|
+
*/
|
|
5
10
|
declare function errorFromLogRecord(record: LogRecord$1): Error | null;
|
|
11
|
+
/**
|
|
12
|
+
* Formatter that outputs only the log message (no timestamp/level/category).
|
|
13
|
+
* @returns TextFormatter that returns just the message string
|
|
14
|
+
*/
|
|
6
15
|
declare function getMessageOnlyFormatter(): TextFormatter$1;
|
|
16
|
+
/**
|
|
17
|
+
* Build a text formatter with optional custom format; appends error from record.
|
|
18
|
+
* @param options - Optional format and logtape options
|
|
19
|
+
* @returns TextFormatter
|
|
20
|
+
*/
|
|
7
21
|
declare function getTextFormatter(options?: TextFormatterOptions): TextFormatter$1;
|
|
22
|
+
/**
|
|
23
|
+
* Build an ANSI-colored text formatter (level/category colors); appends error in red.
|
|
24
|
+
* @param options - Optional format and logtape ANSI options
|
|
25
|
+
* @returns TextFormatter with ANSI colors
|
|
26
|
+
*/
|
|
8
27
|
declare function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter$1;
|
|
9
28
|
/**
|
|
10
29
|
* The formatter returns an array where:
|
|
11
30
|
* - First element is the formatted message string
|
|
12
31
|
* - Second element is the record properties object
|
|
32
|
+
* @param options - Optional messageFormatter (TextFormatter)
|
|
33
|
+
* @returns ConsoleFormatter for console.log/error
|
|
13
34
|
*/
|
|
14
35
|
declare function getConsoleFormatter(options?: {
|
|
15
36
|
messageFormatter?: TextFormatter$1;
|
|
16
37
|
}): ConsoleFormatter;
|
|
17
38
|
//#endregion
|
|
18
39
|
//#region src/sink.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* Create a sink that writes formatted log records to stdout (default formatter).
|
|
42
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
43
|
+
* @returns Sink that writes to stdout
|
|
44
|
+
*/
|
|
19
45
|
declare function getConsoleSink(options?: ConsoleSinkOptions): Sink$1;
|
|
20
46
|
/**
|
|
21
47
|
* Creates a console sink that writes to stderr.
|
|
22
48
|
* (MCP protocol requires stderr to be used for logging)
|
|
49
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
50
|
+
* @returns Sink that writes to stderr
|
|
23
51
|
*/
|
|
24
52
|
declare function getConsoleStderrSink(options?: ConsoleSinkOptions): Sink$1;
|
|
25
53
|
//#endregion
|
|
26
54
|
//#region src/utils.d.ts
|
|
55
|
+
/**
|
|
56
|
+
* Serialize unknown to a string (Error → message + stack; else safe-stringify).
|
|
57
|
+
* @param error - Caught value (Error, string, or arbitrary object)
|
|
58
|
+
* @returns Human-readable string for logging
|
|
59
|
+
*/
|
|
27
60
|
declare function loggable(error: unknown): string;
|
|
28
61
|
type NormalizeError<ErrorArg> = ErrorArg extends Error ? ErrorArg : Error;
|
|
29
62
|
/**
|
|
@@ -70,6 +103,10 @@ declare const logger: _logtape_logtape0.Logger;
|
|
|
70
103
|
* @returns The child logger.
|
|
71
104
|
*/
|
|
72
105
|
declare function createLogger(subcategory: string | readonly [string] | readonly [string, ...string[]]): _logtape_logtape0.Logger;
|
|
106
|
+
/**
|
|
107
|
+
* Configure the global logger: sinks, loggers, and lowest level per category.
|
|
108
|
+
* @param config - Optional partial config (sinks, loggers). Merged with defaults.
|
|
109
|
+
*/
|
|
73
110
|
declare function configureLogger<TSinkId extends string, TFilterId extends string>(config?: Partial<Config<TSinkId, TFilterId>>): void;
|
|
74
111
|
//#endregion
|
|
75
112
|
export { type Filter, type LogLevel, type LogRecord, type Logger, type Sink, type TextFormatter, configureLogger, logger as consola, logger, logger as rootLogger, createLogger, errorFromLogRecord, getAnsiColorFormatter, getConsoleFormatter, getConsoleSink, getConsoleStderrSink, getMessageOnlyFormatter, getTextFormatter, loggable, withFilter, wrapError };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import { n as wrapErrorMessage, t as mergeErrorCause } from "./_chunks/libs/merge-error-cause.mjs";
|
|
2
|
+
import "./_chunks/libs/is-error-instance.mjs";
|
|
3
|
+
import "./_chunks/libs/is-plain-obj.mjs";
|
|
2
4
|
import { t as safeStringify } from "./_chunks/libs/safe-stringify.mjs";
|
|
3
5
|
import { configureSync, getAnsiColorFormatter as getAnsiColorFormatter$1, getConsoleSink as getConsoleSink$1, getLogger, getTextFormatter as getTextFormatter$1, withFilter } from "@logtape/logtape";
|
|
6
|
+
/**
|
|
7
|
+
* Split stack string into lines and normalize (e.g. strip file://).
|
|
8
|
+
* @param stack - Error stack string
|
|
9
|
+
* @returns Array of normalized lines
|
|
10
|
+
*/
|
|
4
11
|
const parseStack = (stack) => {
|
|
5
12
|
return stack.split("\n").map((l) => {
|
|
6
13
|
return l.trim().replace("file://", "");
|
|
7
14
|
});
|
|
8
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Indent each line of value by the given number of spaces.
|
|
18
|
+
* @param value - String or array of lines to indent
|
|
19
|
+
* @param indentation - Number of spaces (default 2)
|
|
20
|
+
* @returns Indented string
|
|
21
|
+
*/
|
|
9
22
|
function indent(value, indentation = 2) {
|
|
10
23
|
value = Array.isArray(value) ? value : value.split("\n");
|
|
11
24
|
const prefix = " ".repeat(indentation);
|
|
12
25
|
return value.map((l) => `${prefix}${l}`).join("\n");
|
|
13
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Serialize unknown to a string (Error → message + stack; else safe-stringify).
|
|
29
|
+
* @param error - Caught value (Error, string, or arbitrary object)
|
|
30
|
+
* @returns Human-readable string for logging
|
|
31
|
+
*/
|
|
14
32
|
function loggable(error) {
|
|
15
33
|
if (typeof error === "string") return error;
|
|
16
34
|
if (error instanceof Error) {
|
|
@@ -23,36 +41,80 @@ function loggable(error) {
|
|
|
23
41
|
}
|
|
24
42
|
return safeStringify(error, { indentation: " " });
|
|
25
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Appends `message` to `error.message`. If `message` ends with `:` or `:\n`,
|
|
46
|
+
* prepends it instead.
|
|
47
|
+
*
|
|
48
|
+
* Returns `error`. If `error` is not an `Error` instance, it is converted to
|
|
49
|
+
* one.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```js
|
|
53
|
+
* wrapErrorMessage(new Error('Message.'), 'Additional message.')
|
|
54
|
+
* // Error: Message.
|
|
55
|
+
* // Additional message.
|
|
56
|
+
*
|
|
57
|
+
* wrapErrorMessage(new Error('Message.'), 'Additional message:')
|
|
58
|
+
* // Error: Additional message: Message.
|
|
59
|
+
*
|
|
60
|
+
* wrapErrorMessage(new Error('Message.'), 'Additional message:\n')
|
|
61
|
+
* // Error: Additional message:
|
|
62
|
+
* // Message.
|
|
63
|
+
*
|
|
64
|
+
* wrapErrorMessage(new Error('Message.'), '')
|
|
65
|
+
* // Error: Message.
|
|
66
|
+
*
|
|
67
|
+
* const invalidError = 'Message.'
|
|
68
|
+
* wrapErrorMessage(invalidError, 'Additional message.')
|
|
69
|
+
* // Error: Message.
|
|
70
|
+
* // Additional message.
|
|
71
|
+
*
|
|
72
|
+
* wrapErrorMessage(new Error(' Message with spaces '), ' Additional message ')
|
|
73
|
+
* // Error: Message with spaces
|
|
74
|
+
* // Additional message
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
26
77
|
function wrapError(error, newMessage) {
|
|
27
78
|
return wrapErrorMessage(error, newMessage);
|
|
28
79
|
}
|
|
29
|
-
function
|
|
80
|
+
function getErrorFromLogRecord(record) {
|
|
30
81
|
const errors = Object.entries(record.properties).flatMap(([k, err]) => {
|
|
31
82
|
if (err instanceof Error) {
|
|
32
83
|
const mergedErr = mergeErrorCause(err);
|
|
33
84
|
if (mergedErr.stack) mergedErr.stack = parseStack(mergedErr.stack).join("\n");
|
|
34
85
|
return [mergedErr];
|
|
35
86
|
}
|
|
36
|
-
if (k === "error" || k === "err") return [
|
|
87
|
+
if (k === "error" || k === "err") return [new Error(loggable(err))];
|
|
37
88
|
return [];
|
|
38
89
|
});
|
|
39
90
|
if (errors.length === 0) return null;
|
|
40
91
|
return errors.length === 1 ? errors[0] : new AggregateError(errors);
|
|
41
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Extract a single Error from a log record (from properties or rawMessage).
|
|
95
|
+
* @param record - Log record that may contain error in properties or rawMessage
|
|
96
|
+
* @returns Merged/wrapped Error or null if none found
|
|
97
|
+
*/
|
|
42
98
|
function errorFromLogRecord(record) {
|
|
43
|
-
const error =
|
|
99
|
+
const error = getErrorFromLogRecord(record);
|
|
44
100
|
if (error && typeof record.rawMessage === "string") return wrapErrorMessage(error, record.rawMessage + "\n");
|
|
45
101
|
return error;
|
|
46
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Append error from record properties to the formatted message (optionally colored).
|
|
105
|
+
* @param values - Formatted values (record + message)
|
|
106
|
+
* @param color - When true, wrap error text in ANSI red
|
|
107
|
+
* @returns Updated FormattedValues with error appended to message
|
|
108
|
+
*/
|
|
47
109
|
function appendErrorToMessage(values, color = false) {
|
|
48
|
-
const error =
|
|
110
|
+
const error = getErrorFromLogRecord(values.record);
|
|
49
111
|
if (error) {
|
|
50
|
-
let
|
|
51
|
-
if (error.stack)
|
|
52
|
-
if (color)
|
|
112
|
+
let errorMessage = error.message;
|
|
113
|
+
if (error.stack) errorMessage = errorMessage + "\n" + indent(error.stack.split("\n").slice(1));
|
|
114
|
+
if (color) errorMessage = `${ansiColors.red}${errorMessage}${RESET}`;
|
|
53
115
|
return {
|
|
54
116
|
...values,
|
|
55
|
-
message: values.message + "\n" + indent(
|
|
117
|
+
message: values.message + "\n" + indent(errorMessage)
|
|
56
118
|
};
|
|
57
119
|
}
|
|
58
120
|
return values;
|
|
@@ -65,12 +127,21 @@ const levelAbbreviations = {
|
|
|
65
127
|
"error": "ERROR",
|
|
66
128
|
"fatal": "FATAL"
|
|
67
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* Formatter that outputs only the log message (no timestamp/level/category).
|
|
132
|
+
* @returns TextFormatter that returns just the message string
|
|
133
|
+
*/
|
|
68
134
|
function getMessageOnlyFormatter() {
|
|
69
135
|
return getTextFormatter({ format: ({ message }) => {
|
|
70
136
|
return message;
|
|
71
137
|
} });
|
|
72
138
|
}
|
|
73
139
|
const level = (l) => levelAbbreviations[l];
|
|
140
|
+
/**
|
|
141
|
+
* Build a text formatter with optional custom format; appends error from record.
|
|
142
|
+
* @param options - Optional format and logtape options
|
|
143
|
+
* @returns TextFormatter
|
|
144
|
+
*/
|
|
74
145
|
function getTextFormatter(options) {
|
|
75
146
|
const _format = options?.format ?? (({ timestamp, level, category, message }) => {
|
|
76
147
|
return `${timestamp} ${level} ${category} ${message}`;
|
|
@@ -87,6 +158,11 @@ function getTextFormatter(options) {
|
|
|
87
158
|
}
|
|
88
159
|
const RESET = "\x1B[0m";
|
|
89
160
|
const ansiColors = { red: "\x1B[31m" };
|
|
161
|
+
/**
|
|
162
|
+
* Build an ANSI-colored text formatter (level/category colors); appends error in red.
|
|
163
|
+
* @param options - Optional format and logtape ANSI options
|
|
164
|
+
* @returns TextFormatter with ANSI colors
|
|
165
|
+
*/
|
|
90
166
|
function getAnsiColorFormatter(options) {
|
|
91
167
|
const _format = options?.format ?? (({ timestamp, level, category, message }) => {
|
|
92
168
|
return `${timestamp} ${level} ${category} ${message}`;
|
|
@@ -103,6 +179,13 @@ function getAnsiColorFormatter(options) {
|
|
|
103
179
|
}
|
|
104
180
|
});
|
|
105
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* The formatter returns an array where:
|
|
184
|
+
* - First element is the formatted message string
|
|
185
|
+
* - Second element is the record properties object
|
|
186
|
+
* @param options - Optional messageFormatter (TextFormatter)
|
|
187
|
+
* @returns ConsoleFormatter for console.log/error
|
|
188
|
+
*/
|
|
106
189
|
function getConsoleFormatter(options) {
|
|
107
190
|
const formatter = options?.messageFormatter;
|
|
108
191
|
if (formatter) return (record) => {
|
|
@@ -116,12 +199,23 @@ function getConsoleFormatter(options) {
|
|
|
116
199
|
return message;
|
|
117
200
|
};
|
|
118
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Create a sink that writes formatted log records to stdout (default formatter).
|
|
204
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
205
|
+
* @returns Sink that writes to stdout
|
|
206
|
+
*/
|
|
119
207
|
function getConsoleSink(options) {
|
|
120
208
|
return getConsoleSink$1({
|
|
121
209
|
formatter: getConsoleFormatter(),
|
|
122
210
|
...options
|
|
123
211
|
});
|
|
124
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Creates a console sink that writes to stderr.
|
|
215
|
+
* (MCP protocol requires stderr to be used for logging)
|
|
216
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
217
|
+
* @returns Sink that writes to stderr
|
|
218
|
+
*/
|
|
125
219
|
function getConsoleStderrSink(options) {
|
|
126
220
|
const formatter = options?.formatter ?? getConsoleFormatter();
|
|
127
221
|
return (record) => {
|
|
@@ -133,19 +227,30 @@ function getConsoleStderrSink(options) {
|
|
|
133
227
|
};
|
|
134
228
|
}
|
|
135
229
|
const logger = getLogger("likec4");
|
|
230
|
+
/**
|
|
231
|
+
* Get a child logger with the given subcategory.
|
|
232
|
+
*
|
|
233
|
+
* @param subcategory The subcategory.
|
|
234
|
+
* @returns The child logger.
|
|
235
|
+
*/
|
|
136
236
|
function createLogger(subcategory) {
|
|
137
237
|
return logger.getChild(subcategory);
|
|
138
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Configure the global logger: sinks, loggers, and lowest level per category.
|
|
241
|
+
* @param config - Optional partial config (sinks, loggers). Merged with defaults.
|
|
242
|
+
*/
|
|
139
243
|
function configureLogger(config) {
|
|
140
244
|
try {
|
|
141
|
-
const sinks = config
|
|
245
|
+
const { sinks = {}, loggers: _loggers, ...restConfig } = config ?? {};
|
|
246
|
+
const sinksWithConsole = {
|
|
247
|
+
console: getConsoleSink(),
|
|
248
|
+
...sinks
|
|
249
|
+
};
|
|
142
250
|
configureSync({
|
|
143
251
|
reset: true,
|
|
144
|
-
...
|
|
145
|
-
sinks:
|
|
146
|
-
console: getConsoleSink(),
|
|
147
|
-
...sinks
|
|
148
|
-
},
|
|
252
|
+
...restConfig,
|
|
253
|
+
sinks: sinksWithConsole,
|
|
149
254
|
loggers: [{
|
|
150
255
|
category: ["logtape", "meta"],
|
|
151
256
|
sinks: ["console"],
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@likec4/log",
|
|
3
3
|
"description": "Shared interface for logging",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.50.0",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
8
8
|
"author": "Denis Davydkov <denis@davydkov.com>",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"@logtape/logtape": "^1.3.7"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "~22.19.
|
|
38
|
+
"@types/node": "~22.19.11",
|
|
39
39
|
"merge-error-cause": "^5.0.2",
|
|
40
40
|
"safe-stringify": "^1.3.0",
|
|
41
41
|
"typescript": "5.9.3",
|
|
42
|
-
"obuild": "^0.4.
|
|
42
|
+
"obuild": "^0.4.31",
|
|
43
43
|
"wrap-error-message": "^3.0.1",
|
|
44
|
-
"@likec4/tsconfig": "1.
|
|
44
|
+
"@likec4/tsconfig": "1.50.0",
|
|
45
45
|
"@likec4/devops": "1.42.0"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
package/src/formatters.ts
CHANGED
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
} from '@logtape/logtape'
|
|
12
12
|
import mergeErrorCause from 'merge-error-cause'
|
|
13
13
|
import wrapErrorMessage from 'wrap-error-message'
|
|
14
|
-
import { indent, parseStack } from './utils'
|
|
14
|
+
import { indent, loggable, parseStack } from './utils'
|
|
15
15
|
|
|
16
|
-
function
|
|
16
|
+
function getErrorFromLogRecord(record: LogRecord): Error | null {
|
|
17
17
|
const errors = Object
|
|
18
18
|
.entries(record.properties)
|
|
19
19
|
.flatMap(([k, err]) => {
|
|
@@ -25,7 +25,7 @@ function gerErrorFromLogRecord(record: LogRecord): Error | null {
|
|
|
25
25
|
return [mergedErr]
|
|
26
26
|
}
|
|
27
27
|
if (k === 'error' || k === 'err') {
|
|
28
|
-
return [new Error(
|
|
28
|
+
return [new Error(loggable(err))]
|
|
29
29
|
}
|
|
30
30
|
return []
|
|
31
31
|
})
|
|
@@ -35,27 +35,38 @@ function gerErrorFromLogRecord(record: LogRecord): Error | null {
|
|
|
35
35
|
return errors.length === 1 ? errors[0]! : new AggregateError(errors)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Extract a single Error from a log record (from properties or rawMessage).
|
|
40
|
+
* @param record - Log record that may contain error in properties or rawMessage
|
|
41
|
+
* @returns Merged/wrapped Error or null if none found
|
|
42
|
+
*/
|
|
38
43
|
export function errorFromLogRecord(record: LogRecord): Error | null {
|
|
39
|
-
const error =
|
|
44
|
+
const error = getErrorFromLogRecord(record)
|
|
40
45
|
if (error && typeof record.rawMessage === 'string') {
|
|
41
46
|
return wrapErrorMessage(error, record.rawMessage + '\n')
|
|
42
47
|
}
|
|
43
48
|
return error
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Append error from record properties to the formatted message (optionally colored).
|
|
53
|
+
* @param values - Formatted values (record + message)
|
|
54
|
+
* @param color - When true, wrap error text in ANSI red
|
|
55
|
+
* @returns Updated FormattedValues with error appended to message
|
|
56
|
+
*/
|
|
46
57
|
export function appendErrorToMessage(values: FormattedValues, color = false): FormattedValues {
|
|
47
|
-
const error =
|
|
58
|
+
const error = getErrorFromLogRecord(values.record)
|
|
48
59
|
if (error) {
|
|
49
|
-
let
|
|
60
|
+
let errorMessage = error.message
|
|
50
61
|
if (error.stack) {
|
|
51
|
-
|
|
62
|
+
errorMessage = errorMessage + '\n' + indent(error.stack.split('\n').slice(1))
|
|
52
63
|
}
|
|
53
64
|
if (color) {
|
|
54
|
-
|
|
65
|
+
errorMessage = `${ansiColors.red}${errorMessage}${RESET}`
|
|
55
66
|
}
|
|
56
67
|
return {
|
|
57
68
|
...values,
|
|
58
|
-
message: values.message + '\n' + indent(
|
|
69
|
+
message: values.message + '\n' + indent(errorMessage),
|
|
59
70
|
}
|
|
60
71
|
}
|
|
61
72
|
return values
|
|
@@ -70,6 +81,10 @@ const levelAbbreviations: Record<LogLevel, string> = {
|
|
|
70
81
|
'fatal': 'FATAL',
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Formatter that outputs only the log message (no timestamp/level/category).
|
|
86
|
+
* @returns TextFormatter that returns just the message string
|
|
87
|
+
*/
|
|
73
88
|
export function getMessageOnlyFormatter(): TextFormatter {
|
|
74
89
|
return getTextFormatter({
|
|
75
90
|
format: ({ message }): string => {
|
|
@@ -80,11 +95,15 @@ export function getMessageOnlyFormatter(): TextFormatter {
|
|
|
80
95
|
|
|
81
96
|
const level = (l: LogLevel): string => levelAbbreviations[l]
|
|
82
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Build a text formatter with optional custom format; appends error from record.
|
|
100
|
+
* @param options - Optional format and logtape options
|
|
101
|
+
* @returns TextFormatter
|
|
102
|
+
*/
|
|
83
103
|
export function getTextFormatter(options?: TextFormatterOptions): TextFormatter {
|
|
84
104
|
const _format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
|
|
85
105
|
return `${timestamp} ${level} ${category} ${message}`
|
|
86
106
|
})
|
|
87
|
-
// const format = options?.format
|
|
88
107
|
return getLogtapeTextFormatter({
|
|
89
108
|
timestamp: 'time',
|
|
90
109
|
level,
|
|
@@ -109,6 +128,11 @@ const ansiColors = {
|
|
|
109
128
|
// white: "\x1b[37m",
|
|
110
129
|
} as const
|
|
111
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Build an ANSI-colored text formatter (level/category colors); appends error in red.
|
|
133
|
+
* @param options - Optional format and logtape ANSI options
|
|
134
|
+
* @returns TextFormatter with ANSI colors
|
|
135
|
+
*/
|
|
112
136
|
export function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter {
|
|
113
137
|
const _format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
|
|
114
138
|
return `${timestamp} ${level} ${category} ${message}`
|
|
@@ -130,6 +154,8 @@ export function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): Text
|
|
|
130
154
|
* The formatter returns an array where:
|
|
131
155
|
* - First element is the formatted message string
|
|
132
156
|
* - Second element is the record properties object
|
|
157
|
+
* @param options - Optional messageFormatter (TextFormatter)
|
|
158
|
+
* @returns ConsoleFormatter for console.log/error
|
|
133
159
|
*/
|
|
134
160
|
export function getConsoleFormatter(options?: {
|
|
135
161
|
messageFormatter?: TextFormatter
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Config,
|
|
3
|
+
type Sink,
|
|
3
4
|
configureSync as configureLogtape,
|
|
4
5
|
getLogger,
|
|
5
6
|
} from '@logtape/logtape'
|
|
@@ -53,18 +54,23 @@ export function createLogger(subcategory: string | readonly [string] | readonly
|
|
|
53
54
|
return logger.getChild(subcategory)
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Configure the global logger: sinks, loggers, and lowest level per category.
|
|
59
|
+
* @param config - Optional partial config (sinks, loggers). Merged with defaults.
|
|
60
|
+
*/
|
|
56
61
|
export function configureLogger<TSinkId extends string, TFilterId extends string>(
|
|
57
62
|
config?: Partial<Config<TSinkId, TFilterId>>,
|
|
58
63
|
) {
|
|
59
64
|
try {
|
|
60
|
-
const sinks = config
|
|
61
|
-
|
|
65
|
+
const { sinks = {}, loggers: _loggers, ...restConfig } = config ?? {}
|
|
66
|
+
const sinksWithConsole: Record<TSinkId | 'console', Sink> = {
|
|
67
|
+
console: getConsoleSink(),
|
|
68
|
+
...sinks,
|
|
69
|
+
} as Record<TSinkId | 'console', Sink>
|
|
70
|
+
configureLogtape<TSinkId | 'console', TFilterId>({
|
|
62
71
|
reset: true,
|
|
63
|
-
...
|
|
64
|
-
sinks:
|
|
65
|
-
console: getConsoleSink(),
|
|
66
|
-
...sinks,
|
|
67
|
-
},
|
|
72
|
+
...restConfig,
|
|
73
|
+
sinks: sinksWithConsole,
|
|
68
74
|
loggers: [
|
|
69
75
|
{ category: ['logtape', 'meta'], sinks: ['console'], lowestLevel: 'warning' },
|
|
70
76
|
...(config?.loggers ?? [
|
package/src/sink.ts
CHANGED
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
} from '@logtape/logtape'
|
|
7
7
|
import { getConsoleFormatter } from './formatters'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Create a sink that writes formatted log records to stdout (default formatter).
|
|
11
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
12
|
+
* @returns Sink that writes to stdout
|
|
13
|
+
*/
|
|
9
14
|
export function getConsoleSink(options?: ConsoleSinkOptions): Sink {
|
|
10
15
|
return getLogtapeConsoleSink({
|
|
11
16
|
formatter: getConsoleFormatter(),
|
|
@@ -16,6 +21,8 @@ export function getConsoleSink(options?: ConsoleSinkOptions): Sink {
|
|
|
16
21
|
/**
|
|
17
22
|
* Creates a console sink that writes to stderr.
|
|
18
23
|
* (MCP protocol requires stderr to be used for logging)
|
|
24
|
+
* @param options - Optional console sink options (e.g. custom formatter)
|
|
25
|
+
* @returns Sink that writes to stderr
|
|
19
26
|
*/
|
|
20
27
|
export function getConsoleStderrSink(options?: ConsoleSinkOptions): Sink {
|
|
21
28
|
const formatter = options?.formatter ?? getConsoleFormatter()
|
package/src/utils.ts
CHANGED
|
@@ -2,6 +2,11 @@ import mergeErrorCause from 'merge-error-cause'
|
|
|
2
2
|
import safeStringify from 'safe-stringify'
|
|
3
3
|
import wrapErrorMessage from 'wrap-error-message'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Split stack string into lines and normalize (e.g. strip file://).
|
|
7
|
+
* @param stack - Error stack string
|
|
8
|
+
* @returns Array of normalized lines
|
|
9
|
+
*/
|
|
5
10
|
export const parseStack = (stack: string): string[] => {
|
|
6
11
|
const lines = stack
|
|
7
12
|
.split('\n')
|
|
@@ -17,12 +22,23 @@ export const parseStack = (stack: string): string[] => {
|
|
|
17
22
|
return lines
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Indent each line of value by the given number of spaces.
|
|
27
|
+
* @param value - String or array of lines to indent
|
|
28
|
+
* @param indentation - Number of spaces (default 2)
|
|
29
|
+
* @returns Indented string
|
|
30
|
+
*/
|
|
20
31
|
export function indent(value: string | string[], indentation = 2): string {
|
|
21
32
|
value = Array.isArray(value) ? value : value.split('\n')
|
|
22
33
|
const prefix = ' '.repeat(indentation)
|
|
23
34
|
return value.map((l) => `${prefix}${l}`).join('\n')
|
|
24
35
|
}
|
|
25
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Serialize unknown to a string (Error → message + stack; else safe-stringify).
|
|
39
|
+
* @param error - Caught value (Error, string, or arbitrary object)
|
|
40
|
+
* @returns Human-readable string for logging
|
|
41
|
+
*/
|
|
26
42
|
export function loggable(error: unknown): string {
|
|
27
43
|
if (typeof error === 'string') {
|
|
28
44
|
return error
|