对接日志默认仅记录告警与错误
新增 LOG_INTEGRATION_LEVEL(默认 warn),压低 integration-*.log 体积; 失败类文案自动提升为 error,避免异常被过滤。
This commit is contained in:
@@ -19,6 +19,8 @@ POSTGRES_PORT=5432
|
|||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
# integration-*.log 默认 warn,只记对接异常;排查全量时改为 info
|
||||||
|
LOG_INTEGRATION_LEVEL=warn
|
||||||
LOG_RETENTION_DAYS=30
|
LOG_RETENTION_DAYS=30
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ DATABASE_URL=postgres://postgres:change-me-postgres-password@postgres:5432/order
|
|||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
# integration-*.log:对接日志级别(debug|info|warn|error),默认 warn 只记异常
|
||||||
|
LOG_INTEGRATION_LEVEL=warn
|
||||||
LOG_RETENTION_DAYS=30
|
LOG_RETENTION_DAYS=30
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export function createDefaultRuntimeConfig(projectRoot: string): RuntimeConfig {
|
|||||||
|
|
||||||
logging: {
|
logging: {
|
||||||
level: "info",
|
level: "info",
|
||||||
|
// 对接日志默认只保留 warn/error,避免 integration-*.log 被 info 刷爆
|
||||||
|
integrationLevel: "warn",
|
||||||
retentionDays: 30,
|
retentionDays: 30,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ function createRuntimeConfig(): RuntimeConfig {
|
|||||||
},
|
},
|
||||||
logging: {
|
logging: {
|
||||||
level: 'info',
|
level: 'info',
|
||||||
|
integrationLevel: 'warn',
|
||||||
retentionDays: 30,
|
retentionDays: 30,
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const ENV_OVERRIDES: readonly EnvOverride[] = [
|
|||||||
integerEnv("PORT", ["server", "port"]),
|
integerEnv("PORT", ["server", "port"]),
|
||||||
stringEnv("DATA_ROOT", ["data", "root"], path.resolve),
|
stringEnv("DATA_ROOT", ["data", "root"], path.resolve),
|
||||||
stringEnv("LOG_LEVEL", ["logging", "level"]),
|
stringEnv("LOG_LEVEL", ["logging", "level"]),
|
||||||
|
stringEnv("LOG_INTEGRATION_LEVEL", ["logging", "integrationLevel"]),
|
||||||
integerEnv("LOG_RETENTION_DAYS", ["logging", "retentionDays"]),
|
integerEnv("LOG_RETENTION_DAYS", ["logging", "retentionDays"]),
|
||||||
stringEnv("DATABASE_URL", ["database", "url"]),
|
stringEnv("DATABASE_URL", ["database", "url"]),
|
||||||
booleanEnv("DATABASE_SSL", ["database", "ssl"]),
|
booleanEnv("DATABASE_SSL", ["database", "ssl"]),
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ function createRuntimeConfig(overrides: Record<string, unknown> = {}): RuntimeCo
|
|||||||
},
|
},
|
||||||
logging: {
|
logging: {
|
||||||
level: 'info',
|
level: 'info',
|
||||||
|
integrationLevel: 'warn',
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
url: 'postgres://postgres:postgres@postgres:5432/order_site',
|
url: 'postgres://postgres:postgres@postgres:5432/order_site',
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export type RuntimeConfig = {
|
|||||||
};
|
};
|
||||||
logging: {
|
logging: {
|
||||||
level: string;
|
level: string;
|
||||||
|
/** integration 通道(外部对接日志文件)最低级别,默认 warn */
|
||||||
|
integrationLevel: string;
|
||||||
retentionDays: number;
|
retentionDays: number;
|
||||||
};
|
};
|
||||||
database: {
|
database: {
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ test('shouldWriteLog respects configured minimum log level', () => {
|
|||||||
assert.equal(shouldWriteLog('error', 'warn'), true)
|
assert.equal(shouldWriteLog('error', 'warn'), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('shouldWriteLog at warn filters info-style integration noise', () => {
|
||||||
|
// 与默认 LOG_INTEGRATION_LEVEL=warn 行为一致
|
||||||
|
assert.equal(shouldWriteLog('info', 'warn'), false)
|
||||||
|
assert.equal(shouldWriteLog('warn', 'warn'), true)
|
||||||
|
assert.equal(shouldWriteLog('error', 'warn'), true)
|
||||||
|
})
|
||||||
|
|
||||||
test('resolveLogFilePath uses daily log file names by channel', () => {
|
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('app', '2026-05-28T00:00:19.194+08:00'), /app-2026-05-28\.log$/)
|
||||||
assert.match(
|
assert.match(
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ const ANSI = {
|
|||||||
const DATA_ROOT = resolveDataRoot()
|
const DATA_ROOT = resolveDataRoot()
|
||||||
const LOG_DIR = path.join(DATA_ROOT, 'logs')
|
const LOG_DIR = path.join(DATA_ROOT, 'logs')
|
||||||
const ACTIVE_LOG_LEVEL = normalizeLogLevel(runtimeConfig.logging?.level)
|
const ACTIVE_LOG_LEVEL = normalizeLogLevel(runtimeConfig.logging?.level)
|
||||||
|
/** integration 通道独立阈值,默认 warn(仅错误/告警落盘) */
|
||||||
|
const ACTIVE_INTEGRATION_LOG_LEVEL = normalizeLogLevel(
|
||||||
|
runtimeConfig.logging?.integrationLevel ?? 'warn',
|
||||||
|
)
|
||||||
const DEFAULT_LOG_RETENTION_DAYS = 30
|
const DEFAULT_LOG_RETENTION_DAYS = 30
|
||||||
const LOG_RETENTION_DAYS = normalizeLogRetentionDays(runtimeConfig.logging?.retentionDays)
|
const LOG_RETENTION_DAYS = normalizeLogRetentionDays(runtimeConfig.logging?.retentionDays)
|
||||||
const LEGACY_LOG_FILES = new Set(['app.log', 'integration.log'])
|
const LEGACY_LOG_FILES = new Set(['app.log', 'integration.log'])
|
||||||
@@ -90,7 +94,24 @@ export function logIntegration(
|
|||||||
detail: LogDetail = undefined,
|
detail: LogDetail = undefined,
|
||||||
{ level = 'info' }: { level?: LogLevel } = {},
|
{ level = 'info' }: { level?: LogLevel } = {},
|
||||||
) {
|
) {
|
||||||
return writeLog(level, scope, message, detail, { channel: 'integration' })
|
// 默认 integration 阈值为 warn:失败类文案即使调用方传 info 也提升,避免漏记异常
|
||||||
|
return writeLog(resolveIntegrationLogLevel(level, message), scope, message, detail, {
|
||||||
|
channel: 'integration',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveIntegrationLogLevel(level: LogLevel, message: unknown): LogLevel {
|
||||||
|
const normalized = normalizeLogLevel(level)
|
||||||
|
if (normalized === 'warn' || normalized === 'error') {
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = String(message || '')
|
||||||
|
if (/失败|异常|错误|超时|拒绝|expired|fail|error|timeout|denied/i.test(text)) {
|
||||||
|
return 'error'
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logExternalHttpPacket(
|
export function logExternalHttpPacket(
|
||||||
@@ -109,7 +130,9 @@ function writeLog(
|
|||||||
detail: LogDetail,
|
detail: LogDetail,
|
||||||
{ channel = 'app' }: { channel?: LogChannel } = {},
|
{ channel = 'app' }: { channel?: LogChannel } = {},
|
||||||
): LogEntry | null {
|
): LogEntry | null {
|
||||||
if (!shouldWriteLog(level, ACTIVE_LOG_LEVEL)) {
|
const configuredLevel =
|
||||||
|
channel === 'integration' ? ACTIVE_INTEGRATION_LOG_LEVEL : ACTIVE_LOG_LEVEL
|
||||||
|
if (!shouldWriteLog(level, configuredLevel)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user