@likec4/log 1.21.1 → 1.22.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/README.md +34 -1
- package/dist/index.cjs +157 -1528
- package/dist/index.d.cts +17 -464
- package/dist/index.d.mts +17 -464
- package/dist/index.d.ts +17 -464
- package/dist/index.mjs +146 -1507
- package/package.json +7 -17
- package/src/formatters.ts +194 -0
- package/src/index.ts +80 -32
- package/src/utils.ts +37 -0
- package/dist/browser.cjs +0 -1938
- package/dist/browser.mjs +0 -1932
- package/dist/chunks/prompt.cjs +0 -288
- package/dist/chunks/prompt.mjs +0 -280
- package/src/browser.ts +0 -16
- package/src/format.ts +0 -60
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/log",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.22.0",
|
|
5
5
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
6
6
|
"homepage": "https://likec4.dev",
|
|
7
7
|
"author": "Denis Davydkov <denis@davydkov.com>",
|
|
@@ -18,23 +18,10 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
20
|
"sources": "./src/index.ts",
|
|
21
|
-
"
|
|
21
|
+
"default": {
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"import": "./dist/index.mjs",
|
|
24
24
|
"require": "./dist/index.cjs"
|
|
25
|
-
},
|
|
26
|
-
"default": {
|
|
27
|
-
"types": "./dist/index.d.ts",
|
|
28
|
-
"import": "./dist/browser.mjs",
|
|
29
|
-
"require": "./dist/browser.cjs"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"./browser": {
|
|
33
|
-
"sources": "./src/browser.ts",
|
|
34
|
-
"default": {
|
|
35
|
-
"types": "./dist/index.d.ts",
|
|
36
|
-
"import": "./dist/browser.mjs",
|
|
37
|
-
"require": "./dist/browser.cjs"
|
|
38
25
|
}
|
|
39
26
|
}
|
|
40
27
|
},
|
|
@@ -46,9 +33,12 @@
|
|
|
46
33
|
"typecheck": "tsc --noEmit",
|
|
47
34
|
"build": "unbuild"
|
|
48
35
|
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@logtape/logtape": "^0.8.2"
|
|
38
|
+
},
|
|
49
39
|
"devDependencies": {
|
|
50
|
-
"@likec4/tsconfig": "1.
|
|
51
|
-
"@types/node": "^20.17.
|
|
40
|
+
"@likec4/tsconfig": "1.22.0",
|
|
41
|
+
"@types/node": "^20.17.17",
|
|
52
42
|
"consola": "^3.4.0",
|
|
53
43
|
"merge-error-cause": "^5.0.0",
|
|
54
44
|
"safe-stringify": "^1.1.1",
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnsiColorFormatterOptions,
|
|
3
|
+
type ConsoleSinkOptions,
|
|
4
|
+
type FormattedValues,
|
|
5
|
+
type LogLevel,
|
|
6
|
+
type LogRecord,
|
|
7
|
+
type Sink,
|
|
8
|
+
type TextFormatter,
|
|
9
|
+
type TextFormatterOptions,
|
|
10
|
+
getAnsiColorFormatter as getLogtapeAnsiColorFormatter,
|
|
11
|
+
getConsoleSink as getLogtapeConsoleSink,
|
|
12
|
+
getTextFormatter as getLogtapeTextFormatter,
|
|
13
|
+
} from '@logtape/logtape'
|
|
14
|
+
import mergeErrorCause from 'merge-error-cause'
|
|
15
|
+
import { ident, parseStack } from './utils'
|
|
16
|
+
|
|
17
|
+
// export function formatProperties(properties: Record<string, unknown>): {
|
|
18
|
+
// properties: Record<string, unknown>
|
|
19
|
+
// output?: string
|
|
20
|
+
// error?: never
|
|
21
|
+
// } | {
|
|
22
|
+
// properties: Record<string, unknown>
|
|
23
|
+
// output: string
|
|
24
|
+
// error?: {
|
|
25
|
+
// message: string
|
|
26
|
+
// name: string
|
|
27
|
+
// stack?: string
|
|
28
|
+
// }
|
|
29
|
+
// } {
|
|
30
|
+
// let error: {
|
|
31
|
+
// message: string
|
|
32
|
+
// name: string
|
|
33
|
+
// stack?: string
|
|
34
|
+
// } | undefined
|
|
35
|
+
// let totalProps = 0
|
|
36
|
+
// const formattedProperties = {} as Record<string, unknown>
|
|
37
|
+
// for (const [key, value] of Object.entries(properties)) {
|
|
38
|
+
// if (value instanceof Error) {
|
|
39
|
+
// const mergedErr = mergeErrorCause(value)
|
|
40
|
+
// const stack = mergedErr.stack ? parseStack(mergedErr.stack) : undefined
|
|
41
|
+
// const formattedError = {
|
|
42
|
+
// message: mergedErr.message,
|
|
43
|
+
// name: mergedErr.name,
|
|
44
|
+
// ...stack && { stack: stack.join('\n') },
|
|
45
|
+
// }
|
|
46
|
+
// error ??= formattedError
|
|
47
|
+
// formattedProperties[key] = formattedError
|
|
48
|
+
// } else {
|
|
49
|
+
// formattedProperties[key] = value
|
|
50
|
+
// }
|
|
51
|
+
// totalProps++
|
|
52
|
+
// }
|
|
53
|
+
// if (totalProps === 0) {
|
|
54
|
+
// return {
|
|
55
|
+
// properties: formattedProperties,
|
|
56
|
+
// }
|
|
57
|
+
// }
|
|
58
|
+
// if (error) {
|
|
59
|
+
// return {
|
|
60
|
+
// error: error,
|
|
61
|
+
// output: error.stack ? error.message + '\n' + ident(error.stack.slice(1)) : error.message,
|
|
62
|
+
// properties: formattedProperties,
|
|
63
|
+
// }
|
|
64
|
+
// }
|
|
65
|
+
// return {
|
|
66
|
+
// ...(error && { error: error }),
|
|
67
|
+
// output: safeStringify(formattedProperties, { indentation: '\t' }),
|
|
68
|
+
// properties: formattedProperties,
|
|
69
|
+
// }
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
export function errorFromLogRecord(record: LogRecord): Error | null {
|
|
73
|
+
const errors = Object
|
|
74
|
+
.values(record.properties)
|
|
75
|
+
.filter((v) => v instanceof Error)
|
|
76
|
+
.map(err => {
|
|
77
|
+
const mergedErr = mergeErrorCause(err)
|
|
78
|
+
if (mergedErr.stack) {
|
|
79
|
+
mergedErr.stack = parseStack(mergedErr.stack).join('\n')
|
|
80
|
+
}
|
|
81
|
+
return mergedErr
|
|
82
|
+
})
|
|
83
|
+
if (errors.length === 0) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
return errors.length === 1 ? errors[0]! : new AggregateError(errors)
|
|
87
|
+
// if (typeof record.rawMessage === 'string') {
|
|
88
|
+
// return wrapErrorMessage(error, record.rawMessage + Z'\n')
|
|
89
|
+
// }
|
|
90
|
+
// return error
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function appendErrorToMessage(values: FormattedValues, color = false): FormattedValues {
|
|
94
|
+
const error = errorFromLogRecord(values.record)
|
|
95
|
+
if (error) {
|
|
96
|
+
let errorMessge = error.message
|
|
97
|
+
if (error.stack) {
|
|
98
|
+
errorMessge = errorMessge + '\n' + ident(error.stack.split('\n').slice(1))
|
|
99
|
+
}
|
|
100
|
+
if (color) {
|
|
101
|
+
errorMessge = `RED: ${ansiColors.red}${errorMessge}${RESET}`
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...values,
|
|
105
|
+
message: values.message + '\n' + ident(errorMessge),
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return values
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// export function formatRecord(values: FormattedValues): FormattedValues {
|
|
112
|
+
// const props = formatProperties(values.record.properties)
|
|
113
|
+
// if (props.output) {
|
|
114
|
+
// props.output = ' \n' + ident(props.output)
|
|
115
|
+
// return {
|
|
116
|
+
// ...values,
|
|
117
|
+
// record: {
|
|
118
|
+
// ...values.record,
|
|
119
|
+
// properties: props.properties,
|
|
120
|
+
// },
|
|
121
|
+
// message: `${values.message}${props.output}`,
|
|
122
|
+
// }
|
|
123
|
+
// }
|
|
124
|
+
// return values
|
|
125
|
+
// }
|
|
126
|
+
|
|
127
|
+
const levelAbbreviations: Record<LogLevel, string> = {
|
|
128
|
+
'debug': 'DEBUG',
|
|
129
|
+
'info': 'INFO ',
|
|
130
|
+
'warning': 'WARN ',
|
|
131
|
+
'error': 'ERROR',
|
|
132
|
+
'fatal': 'FATAL',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getMessageOnlyFormatter(): TextFormatter {
|
|
136
|
+
return getTextFormatter({
|
|
137
|
+
format: ({ message }): string => {
|
|
138
|
+
return message
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const level = (l: LogLevel): string => levelAbbreviations[l]
|
|
144
|
+
|
|
145
|
+
export function getTextFormatter(options?: TextFormatterOptions): TextFormatter {
|
|
146
|
+
const format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
|
|
147
|
+
return `${timestamp} ${level} ${category} ${message}`
|
|
148
|
+
})
|
|
149
|
+
// const format = options?.format
|
|
150
|
+
return getLogtapeTextFormatter({
|
|
151
|
+
timestamp: 'time',
|
|
152
|
+
level,
|
|
153
|
+
category: '.',
|
|
154
|
+
...options,
|
|
155
|
+
format: (values) => format(appendErrorToMessage(values)),
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const RESET = '\x1b[0m'
|
|
160
|
+
|
|
161
|
+
const ansiColors = {
|
|
162
|
+
// black: "\x1b[30m",
|
|
163
|
+
red: '\x1b[31m',
|
|
164
|
+
// green: "\x1b[32m",
|
|
165
|
+
// yellow: "\x1b[33m",
|
|
166
|
+
// blue: "\x1b[34m",
|
|
167
|
+
// magenta: "\x1b[35m",
|
|
168
|
+
// cyan: "\x1b[36m",
|
|
169
|
+
// white: "\x1b[37m",
|
|
170
|
+
} as const
|
|
171
|
+
|
|
172
|
+
export function getAnsiColorFormatter(options?: AnsiColorFormatterOptions): TextFormatter {
|
|
173
|
+
const format = options?.format ?? (({ timestamp, level, category, message }: FormattedValues): string => {
|
|
174
|
+
return `${timestamp} ${level} ${category} ${message}`
|
|
175
|
+
})
|
|
176
|
+
return getLogtapeAnsiColorFormatter({
|
|
177
|
+
timestamp: 'time',
|
|
178
|
+
level,
|
|
179
|
+
categoryStyle: 'bold',
|
|
180
|
+
categoryColor: 'cyan',
|
|
181
|
+
category: '.',
|
|
182
|
+
...options,
|
|
183
|
+
format: (values) => {
|
|
184
|
+
return format(appendErrorToMessage(values, true))
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getConsoleSink(options?: ConsoleSinkOptions): Sink {
|
|
190
|
+
return getLogtapeConsoleSink({
|
|
191
|
+
formatter: getAnsiColorFormatter(),
|
|
192
|
+
...options,
|
|
193
|
+
})
|
|
194
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,36 +1,84 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import {
|
|
2
|
+
type Config,
|
|
3
|
+
configure as configureLogtape,
|
|
4
|
+
getLogger,
|
|
5
|
+
} from '@logtape/logtape'
|
|
6
|
+
import { getConsoleSink } from './formatters'
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
Filter,
|
|
10
|
+
Logger,
|
|
11
|
+
LogLevel,
|
|
12
|
+
LogRecord,
|
|
13
|
+
Sink,
|
|
14
|
+
TextFormatter,
|
|
15
|
+
} from '@logtape/logtape'
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
errorFromLogRecord,
|
|
19
|
+
// formatProperties,
|
|
20
|
+
// formatRecord,
|
|
21
|
+
getAnsiColorFormatter,
|
|
22
|
+
getConsoleSink,
|
|
23
|
+
getMessageOnlyFormatter,
|
|
24
|
+
getTextFormatter,
|
|
25
|
+
} from './formatters'
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
logger as consola,
|
|
29
|
+
logger as rootLogger,
|
|
14
30
|
}
|
|
15
31
|
|
|
16
|
-
export
|
|
17
|
-
|
|
32
|
+
export {
|
|
33
|
+
loggable,
|
|
34
|
+
} from './utils'
|
|
35
|
+
|
|
36
|
+
export const logger = getLogger('likec4')
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get a child logger with the given subcategory.
|
|
40
|
+
*
|
|
41
|
+
* @param subcategory The subcategory.
|
|
42
|
+
* @returns The child logger.
|
|
43
|
+
*/
|
|
44
|
+
export function createLogger(subcategory: string | readonly [string] | readonly [string, ...string[]]) {
|
|
45
|
+
return logger.getChild(subcategory)
|
|
18
46
|
}
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
let configureWasCalled = false
|
|
49
|
+
|
|
50
|
+
export async function configureLogger<TSinkId extends string, TFilterId extends string>(
|
|
51
|
+
config?: Config<TSinkId, TFilterId>,
|
|
52
|
+
) {
|
|
53
|
+
try {
|
|
54
|
+
configureWasCalled = true
|
|
55
|
+
const sinks = config?.sinks ?? {}
|
|
56
|
+
await configureLogtape<any, any>({
|
|
57
|
+
...config,
|
|
58
|
+
sinks: {
|
|
59
|
+
...sinks,
|
|
60
|
+
// @ts-expect-error console is not a valid sink id
|
|
61
|
+
console: sinks['console'] ?? getConsoleSink(),
|
|
62
|
+
},
|
|
63
|
+
loggers: [
|
|
64
|
+
{ category: ['logtape', 'meta'], sinks: ['console' as any], lowestLevel: 'warning' },
|
|
65
|
+
...(config?.loggers ?? [
|
|
66
|
+
{
|
|
67
|
+
category: 'likec4',
|
|
68
|
+
sinks: ['console' as any],
|
|
69
|
+
lowestLevel: 'debug',
|
|
70
|
+
},
|
|
71
|
+
]),
|
|
72
|
+
],
|
|
73
|
+
})
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function ensureLoggerIsConfigured() {
|
|
80
|
+
if (!configureWasCalled) {
|
|
81
|
+
configureLogger()
|
|
82
|
+
console.warn('logger automatically configured with default settings')
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import mergeErrorCause from 'merge-error-cause'
|
|
2
|
+
import safeStringify from 'safe-stringify'
|
|
3
|
+
|
|
4
|
+
export const parseStack = (stack: string): string[] => {
|
|
5
|
+
const lines = stack
|
|
6
|
+
.split('\n')
|
|
7
|
+
.map((l) => {
|
|
8
|
+
let replaced = l.trim()
|
|
9
|
+
.replace('file://', '')
|
|
10
|
+
// // Remove c:\Users\<user>... -> @vscode...
|
|
11
|
+
// .replace(/[A-Za-z]:\\Users\\[^\\]+\\/g, '@vscode\\')
|
|
12
|
+
// // Remove /Users/<user>/... -> @vscode/...
|
|
13
|
+
// .replace(/\/Users\/[^/]+\//g, '@vscode/')
|
|
14
|
+
return replaced
|
|
15
|
+
})
|
|
16
|
+
return lines
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ident(value: string | string[], identation = 2): string {
|
|
20
|
+
value = Array.isArray(value) ? value : value.split('\n')
|
|
21
|
+
return value.map((l) => `${' '.repeat(identation)}${l}`).join('\n')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function loggable(error: unknown): string {
|
|
25
|
+
if (typeof error === 'string') {
|
|
26
|
+
return error
|
|
27
|
+
}
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
const mergedErr = mergeErrorCause(error)
|
|
30
|
+
if (mergedErr.stack) {
|
|
31
|
+
const stack = parseStack(mergedErr.stack)
|
|
32
|
+
return mergedErr.message + '\n' + ident(stack.slice(1))
|
|
33
|
+
}
|
|
34
|
+
return mergedErr.message
|
|
35
|
+
}
|
|
36
|
+
return safeStringify(error)
|
|
37
|
+
}
|