@taicode/common-base 1.2.0 → 1.5.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/output/error/error.d.ts +71 -9
- package/output/error/error.d.ts.map +1 -1
- package/output/error/error.js +62 -6
- package/output/error/error.test.js +68 -0
- package/output/error/index.d.ts +1 -1
- package/output/error/index.d.ts.map +1 -1
- package/output/logger/formatter.d.ts +71 -0
- package/output/logger/formatter.d.ts.map +1 -0
- package/output/logger/formatter.js +222 -0
- package/output/logger/formatter.test.d.ts +26 -0
- package/output/logger/formatter.test.d.ts.map +1 -0
- package/output/logger/formatter.test.js +315 -0
- package/output/logger/index.d.ts +3 -17
- package/output/logger/index.d.ts.map +1 -1
- package/output/logger/index.js +3 -34
- package/output/logger/logger.d.ts +51 -0
- package/output/logger/logger.d.ts.map +1 -0
- package/output/logger/logger.js +118 -0
- package/output/logger/logger.test.d.ts +19 -0
- package/output/logger/logger.test.d.ts.map +1 -1
- package/output/logger/logger.test.js +201 -15
- package/output/logger/transport.d.ts +124 -0
- package/output/logger/transport.d.ts.map +1 -0
- package/output/logger/transport.js +153 -0
- package/output/logger/transport.test.d.ts +2 -0
- package/output/logger/transport.test.d.ts.map +1 -0
- package/output/logger/transport.test.js +309 -0
- package/output/ring-cache/index.d.ts +6 -0
- package/output/ring-cache/index.d.ts.map +1 -0
- package/output/ring-cache/index.js +5 -0
- package/output/ring-cache/ring-cache.d.ts +125 -0
- package/output/ring-cache/ring-cache.d.ts.map +1 -0
- package/output/ring-cache/ring-cache.js +197 -0
- package/output/ring-cache/ring-cache.test.d.ts +2 -0
- package/output/ring-cache/ring-cache.test.d.ts.map +1 -0
- package/output/ring-cache/ring-cache.test.js +195 -0
- package/package.json +1 -1
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { DefaultLogFormatter } from './formatter';
|
|
3
|
+
import { UserError, SystemError, UnknownError } from '../error';
|
|
4
|
+
describe('DefaultLogFormatter', () => {
|
|
5
|
+
let formatter;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
formatter = new DefaultLogFormatter();
|
|
8
|
+
});
|
|
9
|
+
describe('基本格式化功能', () => {
|
|
10
|
+
it('应该格式化字符串', () => {
|
|
11
|
+
const result = formatter.format('测试字符串');
|
|
12
|
+
expect(result).toBe('测试字符串');
|
|
13
|
+
});
|
|
14
|
+
it('应该格式化数字', () => {
|
|
15
|
+
const result = formatter.format(123);
|
|
16
|
+
expect(result).toBe("123");
|
|
17
|
+
});
|
|
18
|
+
it('应该格式化布尔值', () => {
|
|
19
|
+
const trueResult = formatter.format(true);
|
|
20
|
+
const falseResult = formatter.format(false);
|
|
21
|
+
expect(trueResult).toBe("true");
|
|
22
|
+
expect(falseResult).toBe("false");
|
|
23
|
+
});
|
|
24
|
+
it('应该格式化 null', () => {
|
|
25
|
+
const result = formatter.format(null);
|
|
26
|
+
expect(result).toBe('[null]');
|
|
27
|
+
});
|
|
28
|
+
it('应该格式化 undefined', () => {
|
|
29
|
+
const result = formatter.format(undefined);
|
|
30
|
+
expect(result).toBe('[undefined]');
|
|
31
|
+
});
|
|
32
|
+
it('应该格式化 Symbol', () => {
|
|
33
|
+
const symbol = Symbol('test');
|
|
34
|
+
const result = formatter.format(symbol);
|
|
35
|
+
expect(result).toBe(`[symbol] ${symbol.toString()}`);
|
|
36
|
+
});
|
|
37
|
+
it('应该格式化 BigInt', () => {
|
|
38
|
+
const bigint = BigInt(123);
|
|
39
|
+
const result = formatter.format(bigint);
|
|
40
|
+
expect(result).toBe('[bigint] 123n');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('日期格式化', () => {
|
|
44
|
+
it('应该格式化日期为 ISO 字符串', () => {
|
|
45
|
+
const date = new Date('2023-01-01T00:00:00.000Z');
|
|
46
|
+
const result = formatter.format(date);
|
|
47
|
+
expect(result).toBe('2023-01-01T00:00:00.000Z');
|
|
48
|
+
});
|
|
49
|
+
it('应该支持自定义日期格式化器', () => {
|
|
50
|
+
const customFormatter = new DefaultLogFormatter({
|
|
51
|
+
date: (date) => date.toLocaleDateString()
|
|
52
|
+
});
|
|
53
|
+
const date = new Date('2023-01-01T00:00:00.000Z');
|
|
54
|
+
const result = customFormatter.format(date);
|
|
55
|
+
expect(result).toBe(date.toLocaleDateString());
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('对象格式化', () => {
|
|
59
|
+
it('应该格式化普通对象', () => {
|
|
60
|
+
const obj = { name: '测试', age: 25 };
|
|
61
|
+
const result = formatter.format(obj);
|
|
62
|
+
expect(result).toBe(JSON.stringify(obj, null, 2));
|
|
63
|
+
});
|
|
64
|
+
it('应该处理循环引用对象', () => {
|
|
65
|
+
const obj = { name: '测试' };
|
|
66
|
+
obj.self = obj;
|
|
67
|
+
const result = formatter.format(obj);
|
|
68
|
+
expect(typeof result).toBe('string');
|
|
69
|
+
expect(result).toBe('[object Object]');
|
|
70
|
+
});
|
|
71
|
+
it('应该支持自定义对象格式化器', () => {
|
|
72
|
+
const customFormatter = new DefaultLogFormatter({
|
|
73
|
+
object: (obj) => `Custom: ${JSON.stringify(obj)}`
|
|
74
|
+
});
|
|
75
|
+
const obj = { name: '测试' };
|
|
76
|
+
const result = customFormatter.format(obj);
|
|
77
|
+
expect(result).toBe('Custom: {"name":"测试"}');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('数组格式化', () => {
|
|
81
|
+
it('应该格式化数组', () => {
|
|
82
|
+
const arr = [1, '测试', true];
|
|
83
|
+
const result = formatter.format(arr);
|
|
84
|
+
expect(result).toBe(JSON.stringify(arr, null, 2));
|
|
85
|
+
});
|
|
86
|
+
it('应该格式化空数组', () => {
|
|
87
|
+
const arr = [];
|
|
88
|
+
const result = formatter.format(arr);
|
|
89
|
+
expect(result).toBe('[]');
|
|
90
|
+
});
|
|
91
|
+
it('应该支持自定义数组格式化器', () => {
|
|
92
|
+
const customFormatter = new DefaultLogFormatter({
|
|
93
|
+
array: (arr) => `Array[${arr.length}]: ${arr.join(', ')}`
|
|
94
|
+
});
|
|
95
|
+
const arr = [1, 2, 3];
|
|
96
|
+
const result = customFormatter.format(arr);
|
|
97
|
+
expect(result).toBe('Array[3]: 1, 2, 3');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('函数格式化', () => {
|
|
101
|
+
it('应该格式化命名函数', () => {
|
|
102
|
+
function testFunction() { }
|
|
103
|
+
const result = formatter.format(testFunction);
|
|
104
|
+
expect(result).toBe('[Function: testFunction]');
|
|
105
|
+
});
|
|
106
|
+
it('应该格式化匿名函数', () => {
|
|
107
|
+
const result = formatter.format((() => { }));
|
|
108
|
+
expect(result).toBe('[Function: anonymous]');
|
|
109
|
+
});
|
|
110
|
+
it('应该支持自定义函数格式化器', () => {
|
|
111
|
+
const customFormatter = new DefaultLogFormatter({
|
|
112
|
+
function: (fn) => `Custom Function: ${fn.name || 'unnamed'}`
|
|
113
|
+
});
|
|
114
|
+
function namedFunction() { }
|
|
115
|
+
const result = customFormatter.format(namedFunction);
|
|
116
|
+
expect(result).toBe('Custom Function: namedFunction');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('错误格式化', () => {
|
|
120
|
+
describe('UserError 格式化', () => {
|
|
121
|
+
it('应该格式化带有完整信息的 UserError', () => {
|
|
122
|
+
const error = new UserError('validation-failed', '验证失败', {
|
|
123
|
+
field: 'email',
|
|
124
|
+
value: 'invalid-email',
|
|
125
|
+
rule: 'email-format'
|
|
126
|
+
});
|
|
127
|
+
const result = formatter.format(error);
|
|
128
|
+
expect(result).toContain('🚫 User Error [validation-failed]');
|
|
129
|
+
expect(result).toContain('Message: 验证失败');
|
|
130
|
+
expect(result).toContain('Context:');
|
|
131
|
+
expect(result).toContain('Stack:');
|
|
132
|
+
});
|
|
133
|
+
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
134
|
+
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
135
|
+
const error = new UserError('test-error', '测试错误');
|
|
136
|
+
const result = formatterWithoutColors.format(error);
|
|
137
|
+
expect(result).not.toContain('\x1b[');
|
|
138
|
+
expect(result).toContain('🚫 User Error [test-error]');
|
|
139
|
+
expect(result).toContain('Message: 测试错误');
|
|
140
|
+
});
|
|
141
|
+
it('应该在禁用堆栈时不显示堆栈信息', () => {
|
|
142
|
+
const formatterWithoutStack = new DefaultLogFormatter({}, { showStack: false });
|
|
143
|
+
const error = new UserError('test-error', '测试错误');
|
|
144
|
+
const result = formatterWithoutStack.format(error);
|
|
145
|
+
expect(result).not.toContain('Stack:');
|
|
146
|
+
expect(result).toContain('Message: 测试错误');
|
|
147
|
+
});
|
|
148
|
+
it('应该在禁用上下文时不显示上下文信息', () => {
|
|
149
|
+
const formatterWithoutContext = new DefaultLogFormatter({}, { showContext: false });
|
|
150
|
+
const error = new UserError('test-error', '测试错误', { field: 'test' });
|
|
151
|
+
const result = formatterWithoutContext.format(error);
|
|
152
|
+
expect(result).not.toContain('Context:');
|
|
153
|
+
expect(result).toContain('Message: 测试错误');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('SystemError 格式化', () => {
|
|
157
|
+
it('应该格式化带有完整信息的 SystemError', () => {
|
|
158
|
+
const error = new SystemError('database-error', '数据库连接失败', {
|
|
159
|
+
table: 'users',
|
|
160
|
+
query: 'SELECT * FROM users',
|
|
161
|
+
operation: 'select'
|
|
162
|
+
});
|
|
163
|
+
const result = formatter.format(error);
|
|
164
|
+
expect(result).toContain('⚠️ System Error [database-error]');
|
|
165
|
+
expect(result).toContain('Message: 数据库连接失败');
|
|
166
|
+
expect(result).toContain('Context:');
|
|
167
|
+
expect(result).toContain('Stack:');
|
|
168
|
+
});
|
|
169
|
+
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
170
|
+
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
171
|
+
const error = new SystemError('network-error', '网络错误');
|
|
172
|
+
const result = formatterWithoutColors.format(error);
|
|
173
|
+
expect(result).not.toContain('\x1b[');
|
|
174
|
+
expect(result).toContain('⚠️ System Error [network-error]');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe('UnknownError 格式化', () => {
|
|
178
|
+
it('应该格式化带有完整信息的 UnknownError', () => {
|
|
179
|
+
const error = new UnknownError('未知错误', {
|
|
180
|
+
cause: 'something went wrong'
|
|
181
|
+
});
|
|
182
|
+
const result = formatter.format(error);
|
|
183
|
+
expect(result).toContain('❓ Unknown Error');
|
|
184
|
+
expect(result).toContain('Message: 未知错误');
|
|
185
|
+
expect(result).toContain('Context:');
|
|
186
|
+
expect(result).toContain('Stack:');
|
|
187
|
+
});
|
|
188
|
+
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
189
|
+
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
190
|
+
const error = new UnknownError('测试未知错误');
|
|
191
|
+
const result = formatterWithoutColors.format(error);
|
|
192
|
+
expect(result).not.toContain('\x1b[');
|
|
193
|
+
expect(result).toContain('❓ Unknown Error');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('通用 Error 格式化', () => {
|
|
197
|
+
it('应该格式化普通 Error', () => {
|
|
198
|
+
const error = new Error('普通错误');
|
|
199
|
+
const result = formatter.format(error);
|
|
200
|
+
expect(result).toContain('💥 Error');
|
|
201
|
+
expect(result).toContain('Message: 普通错误');
|
|
202
|
+
expect(result).toContain('Stack:');
|
|
203
|
+
});
|
|
204
|
+
it('应该格式化带有额外属性的 Error', () => {
|
|
205
|
+
const error = new Error('测试错误');
|
|
206
|
+
error.code = 'TEST_ERROR';
|
|
207
|
+
error.statusCode = 500;
|
|
208
|
+
const result = formatter.format(error);
|
|
209
|
+
expect(result).toContain('💥 Error');
|
|
210
|
+
expect(result).toContain('Message: 测试错误');
|
|
211
|
+
expect(result).toContain('Additional:');
|
|
212
|
+
expect(result).toContain('code: TEST_ERROR');
|
|
213
|
+
expect(result).toContain('statusCode: 500');
|
|
214
|
+
});
|
|
215
|
+
it('应该格式化自定义错误类型', () => {
|
|
216
|
+
class CustomError extends Error {
|
|
217
|
+
code;
|
|
218
|
+
constructor(message, code) {
|
|
219
|
+
super(message);
|
|
220
|
+
this.code = code;
|
|
221
|
+
this.name = 'CustomError';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const error = new CustomError('自定义错误', 'CUSTOM_001');
|
|
225
|
+
const result = formatter.format(error);
|
|
226
|
+
expect(result).toContain('💥 CustomError');
|
|
227
|
+
expect(result).toContain('Message: 自定义错误');
|
|
228
|
+
expect(result).toContain('Additional:');
|
|
229
|
+
expect(result).toContain('code: CUSTOM_001');
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
describe('未知值格式化', () => {
|
|
233
|
+
it('应该格式化非错误对象', () => {
|
|
234
|
+
const unknownValue = { message: '这不是错误对象' };
|
|
235
|
+
const result = formatter.format(unknownValue);
|
|
236
|
+
expect(result).toBe(JSON.stringify(unknownValue, null, 2));
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
describe('参数数组格式化', () => {
|
|
241
|
+
it('应该格式化混合类型参数数组', () => {
|
|
242
|
+
const args = [
|
|
243
|
+
'字符串',
|
|
244
|
+
123,
|
|
245
|
+
true,
|
|
246
|
+
{ name: '对象' },
|
|
247
|
+
[1, 2, 3],
|
|
248
|
+
new Date('2023-01-01T00:00:00.000Z'),
|
|
249
|
+
new Error('测试错误')
|
|
250
|
+
];
|
|
251
|
+
const result = formatter.formatArguments(args);
|
|
252
|
+
expect(result).toHaveLength(7);
|
|
253
|
+
expect(result[0]).toBe('字符串');
|
|
254
|
+
expect(result[1]).toBe('123');
|
|
255
|
+
expect(result[2]).toBe('true');
|
|
256
|
+
expect(result[3]).toBe(JSON.stringify({ name: '对象' }, null, 2));
|
|
257
|
+
expect(result[4]).toBe(JSON.stringify([1, 2, 3], null, 2));
|
|
258
|
+
expect(result[5]).toBe('2023-01-01T00:00:00.000Z');
|
|
259
|
+
expect(result[6]).toContain('💥 Error');
|
|
260
|
+
});
|
|
261
|
+
it('应该格式化空参数数组', () => {
|
|
262
|
+
const result = formatter.formatArguments([]);
|
|
263
|
+
expect(result).toEqual([]);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
describe('自定义格式化器配置', () => {
|
|
267
|
+
it('应该支持完全自定义的格式化器', () => {
|
|
268
|
+
const customFormatter = new DefaultLogFormatter({
|
|
269
|
+
date: (date) => `日期: ${date.getFullYear()}`,
|
|
270
|
+
object: (obj) => `对象: ${Object.keys(obj).join(', ')}`,
|
|
271
|
+
array: (arr) => `数组长度: ${arr.length}`,
|
|
272
|
+
function: (fn) => `函数名: ${fn.name}`,
|
|
273
|
+
other: (value) => `其他类型: ${typeof value}`
|
|
274
|
+
});
|
|
275
|
+
const date = new Date('2023-01-01');
|
|
276
|
+
const obj = { a: 1, b: 2 };
|
|
277
|
+
const arr = [1, 2, 3];
|
|
278
|
+
const fn = function testFn() { };
|
|
279
|
+
const symbol = Symbol('test');
|
|
280
|
+
expect(customFormatter.format(date)).toBe('日期: 2023');
|
|
281
|
+
expect(customFormatter.format(obj)).toBe('对象: a, b');
|
|
282
|
+
expect(customFormatter.format(arr)).toBe('数组长度: 3');
|
|
283
|
+
expect(customFormatter.format(fn)).toBe('函数名: testFn');
|
|
284
|
+
expect(customFormatter.format(symbol)).toBe('其他类型: symbol');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe('错误选项配置', () => {
|
|
288
|
+
it('应该支持完全禁用错误格式化选项', () => {
|
|
289
|
+
const minimalistFormatter = new DefaultLogFormatter({}, {
|
|
290
|
+
showStack: false,
|
|
291
|
+
showContext: false,
|
|
292
|
+
useColors: false
|
|
293
|
+
});
|
|
294
|
+
const error = new UserError('test-error', '测试错误', { field: 'test' });
|
|
295
|
+
const result = minimalistFormatter.format(error);
|
|
296
|
+
expect(result).not.toContain('Stack:');
|
|
297
|
+
expect(result).not.toContain('Context:');
|
|
298
|
+
expect(result).not.toContain('\x1b[');
|
|
299
|
+
expect(result).toContain('🚫 User Error [test-error]');
|
|
300
|
+
expect(result).toContain('Message: 测试错误');
|
|
301
|
+
});
|
|
302
|
+
it('应该支持只显示特定错误信息', () => {
|
|
303
|
+
const stackOnlyFormatter = new DefaultLogFormatter({}, {
|
|
304
|
+
showStack: true,
|
|
305
|
+
showContext: false,
|
|
306
|
+
useColors: false
|
|
307
|
+
});
|
|
308
|
+
const error = new SystemError('database-error', '数据库错误', { table: 'users' });
|
|
309
|
+
const result = stackOnlyFormatter.format(error);
|
|
310
|
+
expect(result).toContain('Stack:');
|
|
311
|
+
expect(result).not.toContain('Context:');
|
|
312
|
+
expect(result).not.toContain('\x1b[');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
});
|
package/output/logger/index.d.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
handler?: (level: LogLevel, ...args: any[]) => void;
|
|
5
|
-
}
|
|
6
|
-
export declare class Logger {
|
|
7
|
-
private level;
|
|
8
|
-
private handler;
|
|
9
|
-
constructor(options?: LoggerOptions);
|
|
10
|
-
private defaultHandler;
|
|
11
|
-
private shouldLog;
|
|
12
|
-
debug(...args: any[]): void;
|
|
13
|
-
info(...args: any[]): void;
|
|
14
|
-
warn(...args: any[]): void;
|
|
15
|
-
error(...args: any[]): void;
|
|
16
|
-
}
|
|
17
|
-
export declare const logger: Logger;
|
|
1
|
+
export * from './logger';
|
|
2
|
+
export * from './formatter';
|
|
3
|
+
export * from './transport';
|
|
18
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/logger/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/logger/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
package/output/logger/index.js
CHANGED
|
@@ -1,34 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
constructor(options = {}) {
|
|
5
|
-
this.level = options.level || 'info';
|
|
6
|
-
this.handler = options.handler || this.defaultHandler;
|
|
7
|
-
}
|
|
8
|
-
defaultHandler(level, ...args) {
|
|
9
|
-
const prefix = `[${level.toUpperCase()}]`;
|
|
10
|
-
// eslint-disable-next-line no-console
|
|
11
|
-
(console[level] || console.log)(prefix, ...args);
|
|
12
|
-
}
|
|
13
|
-
shouldLog(level) {
|
|
14
|
-
const order = ['debug', 'info', 'warn', 'error'];
|
|
15
|
-
return order.indexOf(level) >= order.indexOf(this.level);
|
|
16
|
-
}
|
|
17
|
-
debug(...args) {
|
|
18
|
-
if (this.shouldLog('debug'))
|
|
19
|
-
this.handler('debug', ...args);
|
|
20
|
-
}
|
|
21
|
-
info(...args) {
|
|
22
|
-
if (this.shouldLog('info'))
|
|
23
|
-
this.handler('info', ...args);
|
|
24
|
-
}
|
|
25
|
-
warn(...args) {
|
|
26
|
-
if (this.shouldLog('warn'))
|
|
27
|
-
this.handler('warn', ...args);
|
|
28
|
-
}
|
|
29
|
-
error(...args) {
|
|
30
|
-
if (this.shouldLog('error'))
|
|
31
|
-
this.handler('error', ...args);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export const logger = new Logger();
|
|
1
|
+
export * from './logger';
|
|
2
|
+
export * from './formatter';
|
|
3
|
+
export * from './transport';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { LogFormatter } from './formatter';
|
|
2
|
+
import { LogTransport } from './transport';
|
|
3
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
export interface LoggerOptions {
|
|
5
|
+
level?: LogLevel;
|
|
6
|
+
/** LogTransport 实例数组,用于处理结构化日志数据 */
|
|
7
|
+
transports?: LogTransport[];
|
|
8
|
+
/** 格式化器实例 */
|
|
9
|
+
formatter?: LogFormatter;
|
|
10
|
+
/** 传统的 print 函数,用于向后兼容 */
|
|
11
|
+
print?: (level: LogLevel, ...args: any[]) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare class Logger {
|
|
14
|
+
private level;
|
|
15
|
+
private transports;
|
|
16
|
+
private formatter;
|
|
17
|
+
private print?;
|
|
18
|
+
constructor(options?: LoggerOptions);
|
|
19
|
+
private defaultPrint;
|
|
20
|
+
private shouldLog;
|
|
21
|
+
/**
|
|
22
|
+
* 记录日志到所有 transports
|
|
23
|
+
*/
|
|
24
|
+
private logToTransports;
|
|
25
|
+
/**
|
|
26
|
+
* 使用传统 print 方式记录日志(向后兼容)
|
|
27
|
+
*/
|
|
28
|
+
private logToPrint;
|
|
29
|
+
debug(...args: any[]): void;
|
|
30
|
+
info(...args: any[]): void;
|
|
31
|
+
warn(...args: any[]): void;
|
|
32
|
+
error(...args: any[]): void;
|
|
33
|
+
/**
|
|
34
|
+
* 添加 LogTransport
|
|
35
|
+
*/
|
|
36
|
+
addTransport(transport: LogTransport): void;
|
|
37
|
+
/**
|
|
38
|
+
* 移除 LogTransport
|
|
39
|
+
*/
|
|
40
|
+
removeTransport(transportName: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* 获取所有 LogTransports
|
|
43
|
+
*/
|
|
44
|
+
getTransports(): readonly LogTransport[];
|
|
45
|
+
/**
|
|
46
|
+
* 关闭所有 LogTransports
|
|
47
|
+
*/
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export declare const logger: Logger;
|
|
51
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../source/logger/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,YAAY,EAAa,MAAM,aAAa,CAAA;AAErD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE1D,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,oCAAoC;IACpC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAA;IAC3B,aAAa;IACb,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAClD;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,KAAK,CAAC,CAA2C;gBAE7C,OAAO,GAAE,aAAkB;IAYvC,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,SAAS;IAKjB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOnB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOnB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,YAAY,GAAG,IAAI;IAI3C;;OAEG;IACH,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;IACH,aAAa,IAAI,SAAS,YAAY,EAAE;IAIxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B;AAED,eAAO,MAAM,MAAM,QAAe,CAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// 基础 Logger 实现,支持简单的日志输出和扩展能力
|
|
2
|
+
import { defaultLogFormatter } from './formatter';
|
|
3
|
+
export class Logger {
|
|
4
|
+
level;
|
|
5
|
+
transports;
|
|
6
|
+
formatter;
|
|
7
|
+
print;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.level = options.level || 'info';
|
|
10
|
+
this.formatter = options.formatter || defaultLogFormatter;
|
|
11
|
+
this.transports = options.transports || [];
|
|
12
|
+
this.print = options.print;
|
|
13
|
+
// 如果没有 transports 且没有 print,使用默认的控制台输出
|
|
14
|
+
if (this.transports.length === 0 && !this.print) {
|
|
15
|
+
this.print = this.defaultPrint;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
defaultPrint(level, ...args) {
|
|
19
|
+
const prefix = `[${level.toUpperCase()}]`;
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
(console[level] || console.log)(prefix, ...args);
|
|
22
|
+
}
|
|
23
|
+
shouldLog(level) {
|
|
24
|
+
const order = ['debug', 'info', 'warn', 'error'];
|
|
25
|
+
return order.indexOf(level) >= order.indexOf(this.level);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 记录日志到所有 transports
|
|
29
|
+
*/
|
|
30
|
+
logToTransports(level, args) {
|
|
31
|
+
if (this.transports.length === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const record = {
|
|
35
|
+
level,
|
|
36
|
+
timestamp: new Date(),
|
|
37
|
+
args: [...args] // 创建副本避免引用问题
|
|
38
|
+
};
|
|
39
|
+
// 并行发送到所有 transports
|
|
40
|
+
this.transports.forEach(transport => {
|
|
41
|
+
try {
|
|
42
|
+
const result = transport.log(record);
|
|
43
|
+
// 如果返回 Promise,静默处理错误
|
|
44
|
+
if (result && typeof result.catch === 'function') {
|
|
45
|
+
result.catch(() => {
|
|
46
|
+
// 静默处理 transport 错误,避免影响主程序
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// 静默处理 transport 错误,避免影响主程序
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 使用传统 print 方式记录日志(向后兼容)
|
|
57
|
+
*/
|
|
58
|
+
logToPrint(level, args) {
|
|
59
|
+
if (!this.print) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// 为了向后兼容,保持原始行为:传递格式化后的参数
|
|
63
|
+
const formattedArgs = this.formatter.formatArguments(args);
|
|
64
|
+
this.print(level, ...formattedArgs);
|
|
65
|
+
}
|
|
66
|
+
debug(...args) {
|
|
67
|
+
if (this.shouldLog('debug')) {
|
|
68
|
+
this.logToTransports('debug', args);
|
|
69
|
+
this.logToPrint('debug', args);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
info(...args) {
|
|
73
|
+
if (this.shouldLog('info')) {
|
|
74
|
+
this.logToTransports('info', args);
|
|
75
|
+
this.logToPrint('info', args);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
warn(...args) {
|
|
79
|
+
if (this.shouldLog('warn')) {
|
|
80
|
+
this.logToTransports('warn', args);
|
|
81
|
+
this.logToPrint('warn', args);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
error(...args) {
|
|
85
|
+
if (this.shouldLog('error')) {
|
|
86
|
+
this.logToTransports('error', args);
|
|
87
|
+
this.logToPrint('error', args);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 添加 LogTransport
|
|
92
|
+
*/
|
|
93
|
+
addTransport(transport) {
|
|
94
|
+
this.transports.push(transport);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 移除 LogTransport
|
|
98
|
+
*/
|
|
99
|
+
removeTransport(transportName) {
|
|
100
|
+
this.transports = this.transports.filter(t => t.name !== transportName);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 获取所有 LogTransports
|
|
104
|
+
*/
|
|
105
|
+
getTransports() {
|
|
106
|
+
return [...this.transports];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 关闭所有 LogTransports
|
|
110
|
+
*/
|
|
111
|
+
async close() {
|
|
112
|
+
const closePromises = this.transports
|
|
113
|
+
.map(transport => transport.close?.())
|
|
114
|
+
.filter(Boolean);
|
|
115
|
+
await Promise.all(closePromises);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export const logger = new Logger();
|
|
@@ -1,2 +1,21 @@
|
|
|
1
|
+
declare module '../error' {
|
|
2
|
+
interface UserErrorTypes {
|
|
3
|
+
'validation-failed': {
|
|
4
|
+
field?: string;
|
|
5
|
+
value?: unknown;
|
|
6
|
+
rule?: string;
|
|
7
|
+
};
|
|
8
|
+
'test-error': {
|
|
9
|
+
field?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
interface SystemErrorTypes {
|
|
13
|
+
'database-error': {
|
|
14
|
+
query?: string;
|
|
15
|
+
table?: string;
|
|
16
|
+
operation?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
1
20
|
export {};
|
|
2
21
|
//# sourceMappingURL=logger.test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../source/logger/logger.test.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../source/logger/logger.test.ts"],"names":[],"mappings":"AAOA,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,cAAc;QACtB,mBAAmB,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;QACvE,YAAY,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KACjC;IAED,UAAU,gBAAgB;QACxB,gBAAgB,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KACzE;CACF"}
|