对接日志默认仅记录告警与错误

新增 LOG_INTEGRATION_LEVEL(默认 warn),压低 integration-*.log 体积;
失败类文案自动提升为 error,避免异常被过滤。
This commit is contained in:
yml2213
2026-07-10 21:17:55 +08:00
parent 50d32be0ea
commit 01f46e4fc7
9 changed files with 43 additions and 2 deletions
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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: {
+1
View File
@@ -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',
+2
View File
@@ -24,6 +24,8 @@ export type RuntimeConfig = {
}; };
logging: { logging: {
level: string; level: string;
/** integration 通道(外部对接日志文件)最低级别,默认 warn */
integrationLevel: string;
retentionDays: number; retentionDays: number;
}; };
database: { database: {
+7
View File
@@ -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(
+25 -2
View File
@@ -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
} }