快手发码通知采集到独立日志文件,修复 integration 日志阈值问题

This commit is contained in:
yml2213
2026-08-02 11:43:22 +08:00
parent 9053a06481
commit f839429261
2 changed files with 93 additions and 33 deletions
+24 -1
View File
@@ -8,7 +8,7 @@ import { handleDestroyCode } from '../services/platforms/kuaishou-industry/destr
import { handleQueryCode } from '../services/platforms/kuaishou-industry/query-code-service.js'
import { handleConsumeCode } from '../services/platforms/kuaishou-industry/consume-code-service.js'
import { buildIndustryErrorResponse } from '../services/platforms/kuaishou-industry/response.js'
import { createRequestId, logIntegration } from '../utils/logger.js'
import { createRequestId, logCapture, logIntegration } from '../utils/logger.js'
const router = Router()
@@ -24,6 +24,7 @@ const industryRateLimit = createRateLimitMiddleware({
router.post('/send-code', industryRateLimit, async (req, res) => {
const requestId = createRequestId('ksind')
const startedAt = Date.now()
const mergedParams = mergeRequestParams(req)
logIntegration('[kuaishou-industry/send-code]', '收到快手行业电子凭证发码请求', {
requestId,
@@ -34,9 +35,23 @@ router.post('/send-code', industryRateLimit, async (req, res) => {
body: req.body,
})
logCapture('[kuaishou-industry/send-code]', '快手通知商家发码消息', {
requestId,
receivedAt: new Date().toISOString(),
method: req.method,
originalUrl: req.originalUrl,
ip: req.ip,
params: mergedParams,
}, { channel: 'send-code', sanitize: false })
try {
const params = mergeRequestParams(req)
const result = await handleSendCode(params)
logCapture('[kuaishou-industry/send-code]', '快手通知商家发码处理完成', {
requestId,
durationMs: Date.now() - startedAt,
result,
}, { channel: 'send-code', sanitize: false })
logIntegration('[kuaishou-industry/send-code]', '发码处理完成', {
requestId,
durationMs: Date.now() - startedAt,
@@ -45,6 +60,14 @@ router.post('/send-code', industryRateLimit, async (req, res) => {
res.status(200).json(result)
} catch (error) {
const message = error instanceof Error ? error.message : '系统异常'
logCapture('[kuaishou-industry/send-code]', '快手通知商家发码处理失败', {
requestId,
durationMs: Date.now() - startedAt,
error: {
name: error instanceof Error ? error.name : 'Error',
message,
},
}, { channel: 'send-code', sanitize: false })
logIntegration('[kuaishou-industry/send-code]', '发码处理失败', {
requestId,
durationMs: Date.now() - startedAt,
+69 -32
View File
@@ -123,6 +123,25 @@ export function logExternalHttpPacket(
return logIntegration(scope, message, normalizeExternalHttpPacketDetail(detail), { level })
}
export function logCapture(
scope: unknown,
message: unknown,
detail: LogDetail = undefined,
{ channel = 'capture', sanitize = false }: { channel?: string; sanitize?: boolean } = {},
): LogEntry | null {
const entry = {
time: new Date().toISOString(),
level: 'info' as LogLevel,
scope: String(scope || 'capture').trim() || 'capture',
message: String(message || '').trim() || '-',
pid: process.pid,
detail: normalizeLogValue(detail, sanitize !== false),
}
enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time), sanitize !== false, true)
return entry
}
function writeLog(
level: LogLevel,
scope: unknown,
@@ -146,12 +165,19 @@ function writeLog(
}
writeConsole(entry)
enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time))
enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time), true)
return entry
}
function enqueueFileWrite(entry: LogEntry, filePath: string): void {
const line = formatLogEntry(entry, { color: false })
function enqueueFileWrite(
entry: LogEntry,
filePath: string,
sanitize: boolean,
jsonLine = false,
): void {
const line = jsonLine
? JSON.stringify(entry)
: formatLogEntry(entry, { color: false, sanitize })
const dateKey = extractLogDateKey(entry.time)
writeQueue = writeQueue
@@ -204,7 +230,7 @@ export function shouldWriteLog(
export function formatLogEntry(
entry: Partial<LogEntry>,
{ color = false }: { color?: boolean } = {},
{ color = false, sanitize = true }: { color?: boolean; sanitize?: boolean } = {},
): string {
const normalizedEntry = {
time: String(entry?.time || new Date().toISOString()),
@@ -212,7 +238,7 @@ export function formatLogEntry(
scope: String(entry?.scope || 'app').trim() || 'app',
message: String(entry?.message || '').trim() || '-',
pid: Number(entry?.pid || process.pid),
detail: sanitizeLogValue(entry?.detail),
detail: sanitize !== false ? sanitizeLogValue(entry?.detail) : entry?.detail,
}
const timestamp = colorize(formatLogTimestamp(normalizedEntry.time), ANSI.gray, color)
const level = colorize(
@@ -248,10 +274,21 @@ export function formatLogEntry(
}
export function resolveLogFilePath(channel = 'app', time = new Date().toISOString()): string {
const normalizedChannel = String(channel || '').trim() === 'integration' ? 'integration' : 'app'
const normalizedChannel = normalizeLogChannelName(channel)
return path.join(LOG_DIR, `${normalizedChannel}-${extractLogDateKey(time)}.log`)
}
function normalizeLogChannelName(value: unknown): string {
const normalized = String(value || '').trim().toLowerCase()
if (!normalized) {
return 'app'
}
if (normalized === 'integration') {
return 'integration'
}
return /^[a-z0-9-]{1,40}$/.test(normalized) ? normalized : 'app'
}
export function resolveExpiredLogFilenames(
fileNames: string[] = [],
referenceTime = new Date().toISOString(),
@@ -280,37 +317,37 @@ function resolveDataRoot(): string {
return configured ? path.resolve(configured) : path.resolve(PROJECT_ROOT, 'data')
}
function normalizeLogValue(value: unknown): unknown {
function normalizeLogValue(value: unknown, sanitize = true): unknown {
if (typeof value === 'undefined') {
return undefined
}
const normalizeCurrent = (current: unknown): unknown => {
if (current instanceof Error) {
const currentError = current as Error & HttpErrorLike
return {
name: currentError.name,
message: sanitize ? sanitizeLogString(currentError.message) : currentError.message,
stack: sanitize ? sanitizeLogString(currentError.stack) : currentError.stack,
statusCode: currentError.statusCode,
errorCode: currentError.errorCode,
context: sanitize ? sanitizeLogValue(currentError.context) : currentError.context,
}
}
if (typeof current === 'bigint') {
return String(current)
}
return current
}
try {
return sanitizeLogValue(
JSON.parse(
JSON.stringify(value, (_key, current) => {
if (current instanceof Error) {
const currentError = current as Error & HttpErrorLike
return {
name: current.name,
message: sanitizeLogString(current.message),
stack: sanitizeLogString(current.stack),
statusCode: currentError.statusCode,
errorCode: currentError.errorCode,
context: sanitizeLogValue(currentError.context),
}
}
if (typeof current === 'bigint') {
return String(current)
}
return current
}),
),
)
return sanitize
? sanitizeLogValue(JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(current))))
: JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(current)))
} catch {
return sanitizeLogString(String(value))
return sanitize ? sanitizeLogString(String(value)) : String(value)
}
}
@@ -632,7 +669,7 @@ function shouldDeleteLogFileByDate(fileName: unknown, cutoffDateKey: string): bo
return true
}
const matched = /^(app|integration)-(\d{4}-\d{2}-\d{2})\.log$/.exec(String(fileName || '').trim())
const matched = /^([a-z0-9-]{1,40})-(\d{4}-\d{2}-\d{2})\.log$/.exec(String(fileName || '').trim())
if (!matched) {
return false