增加feifei 日志
This commit is contained in:
@@ -28,7 +28,10 @@ test('shouldWriteLog respects configured minimum log level', () => {
|
||||
|
||||
test('resolveLogFilePath uses daily log file names by channel', () => {
|
||||
assert.match(resolveLogFilePath('app', '2026-05-28T00:00:19.194+08:00'), /app-2026-05-28\.log$/)
|
||||
assert.match(resolveLogFilePath('integration', '2026-05-28T00:00:19.194+08:00'), /integration-2026-05-28\.log$/)
|
||||
assert.match(
|
||||
resolveLogFilePath('integration', '2026-05-28T00:00:19.194+08:00'),
|
||||
/integration-2026-05-28\.log$/,
|
||||
)
|
||||
})
|
||||
|
||||
test('resolveLogFilePath uses local date for utc timestamps', () => {
|
||||
@@ -36,15 +39,18 @@ test('resolveLogFilePath uses local date for utc timestamps', () => {
|
||||
})
|
||||
|
||||
test('resolveExpiredLogFilenames keeps latest thirty days by default and ignores unknown files', () => {
|
||||
const expired = resolveExpiredLogFilenames([
|
||||
'app-2026-05-30.log',
|
||||
'app-2026-05-01.log',
|
||||
'app-2026-04-30.log',
|
||||
'integration-2026-04-29.log',
|
||||
'app.log',
|
||||
'integration.log',
|
||||
'random.txt',
|
||||
], '2026-05-30')
|
||||
const expired = resolveExpiredLogFilenames(
|
||||
[
|
||||
'app-2026-05-30.log',
|
||||
'app-2026-05-01.log',
|
||||
'app-2026-04-30.log',
|
||||
'integration-2026-04-29.log',
|
||||
'app.log',
|
||||
'integration.log',
|
||||
'random.txt',
|
||||
],
|
||||
'2026-05-30',
|
||||
)
|
||||
|
||||
assert.deepEqual(expired, [
|
||||
'app-2026-04-30.log',
|
||||
@@ -55,15 +61,13 @@ test('resolveExpiredLogFilenames keeps latest thirty days by default and ignores
|
||||
})
|
||||
|
||||
test('resolveExpiredLogFilenames supports explicit retention days', () => {
|
||||
const expired = resolveExpiredLogFilenames([
|
||||
'app-2026-04-14.log',
|
||||
'app-2026-04-08.log',
|
||||
'app-2026-04-07.log',
|
||||
], '2026-04-14', 7)
|
||||
const expired = resolveExpiredLogFilenames(
|
||||
['app-2026-04-14.log', 'app-2026-04-08.log', 'app-2026-04-07.log'],
|
||||
'2026-04-14',
|
||||
7,
|
||||
)
|
||||
|
||||
assert.deepEqual(expired, [
|
||||
'app-2026-04-07.log',
|
||||
])
|
||||
assert.deepEqual(expired, ['app-2026-04-07.log'])
|
||||
})
|
||||
|
||||
test('formatLogTimestamp renders readable local timestamp with timezone offset', () => {
|
||||
@@ -72,18 +76,21 @@ test('formatLogTimestamp renders readable local timestamp with timezone offset',
|
||||
})
|
||||
|
||||
test('formatLogEntry prints readable one-line output for flat details', () => {
|
||||
const text = formatLogEntry({
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'info',
|
||||
scope: '[http/access]',
|
||||
message: 'request completed',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
requestId: 'req-123',
|
||||
method: 'GET',
|
||||
statusCode: 401,
|
||||
const text = formatLogEntry(
|
||||
{
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'info',
|
||||
scope: '[http/access]',
|
||||
message: 'request completed',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
requestId: 'req-123',
|
||||
method: 'GET',
|
||||
statusCode: 401,
|
||||
},
|
||||
},
|
||||
}, { color: false })
|
||||
{ color: false },
|
||||
)
|
||||
|
||||
assert.match(text, /INFO\s+\[http\/access\] request completed/)
|
||||
assert.match(text, /pid=4671/)
|
||||
@@ -94,21 +101,24 @@ test('formatLogEntry prints readable one-line output for flat details', () => {
|
||||
})
|
||||
|
||||
test('formatLogEntry prints nested detail blocks without ansi colors in file mode', () => {
|
||||
const text = formatLogEntry({
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'warn',
|
||||
scope: '[admin/auth]',
|
||||
message: '后台鉴权失败',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
statusCode: 401,
|
||||
errorCode: 'admin_auth_required',
|
||||
error: {
|
||||
message: '未登录或登录已失效',
|
||||
stack: 'Error: test',
|
||||
const text = formatLogEntry(
|
||||
{
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'warn',
|
||||
scope: '[admin/auth]',
|
||||
message: '后台鉴权失败',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
statusCode: 401,
|
||||
errorCode: 'admin_auth_required',
|
||||
error: {
|
||||
message: '未登录或登录已失效',
|
||||
stack: 'Error: test',
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { color: false })
|
||||
{ color: false },
|
||||
)
|
||||
|
||||
assert.match(text, /WARN\s+\[admin\/auth\] 后台鉴权失败/)
|
||||
assert.match(text, /statusCode=401/)
|
||||
@@ -119,24 +129,38 @@ test('formatLogEntry prints nested detail blocks without ansi colors in file mod
|
||||
})
|
||||
|
||||
test('formatLogEntry masks sensitive fields in inline and nested details', () => {
|
||||
const text = formatLogEntry({
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'info',
|
||||
scope: '[security]',
|
||||
message: 'masked',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
token: 'abcdef1234567890',
|
||||
authorization: 'Bearer abcdef1234567890',
|
||||
nested: {
|
||||
cookie: 'sessionid=abcdef1234567890',
|
||||
note: 'password=super-secret-value',
|
||||
const text = formatLogEntry(
|
||||
{
|
||||
time: '2026-04-14T10:36:24.974Z',
|
||||
level: 'info',
|
||||
scope: '[security]',
|
||||
message: 'masked',
|
||||
pid: 4671,
|
||||
detail: {
|
||||
token: 'abcdef1234567890',
|
||||
authorization: 'Bearer abcdef1234567890',
|
||||
sign: '0123456789abcdef',
|
||||
jumpLink:
|
||||
'https://feifei.example.com/h5/bind?code=very-secret-code&token=very-secret-token',
|
||||
rawText:
|
||||
'{"token":"raw-json-token","sign":"raw-json-sign","h5":{"recharge_url":"https://feifei.example.com/h5/bind?code=raw-secret-code"}}',
|
||||
nested: {
|
||||
cookie: 'sessionid=abcdef1234567890',
|
||||
note: 'password=super-secret-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
}, { color: false })
|
||||
{ color: false },
|
||||
)
|
||||
|
||||
assert.doesNotMatch(text, /abcdef1234567890/)
|
||||
assert.doesNotMatch(text, /super-secret-value/)
|
||||
assert.doesNotMatch(text, /0123456789abcdef/)
|
||||
assert.doesNotMatch(text, /very-secret-code/)
|
||||
assert.doesNotMatch(text, /very-secret-token/)
|
||||
assert.doesNotMatch(text, /raw-json-token/)
|
||||
assert.doesNotMatch(text, /raw-json-sign/)
|
||||
assert.doesNotMatch(text, /raw-secret-code/)
|
||||
assert.match(text, /abcdef\*\*\*\*567890/)
|
||||
assert.match(text, /Bearer\*\*\*\*567890/)
|
||||
assert.match(text, /password=super-\*\*\*\*-value/)
|
||||
|
||||
Reference in New Issue
Block a user