快手发码通知采集到独立日志文件,修复 integration 日志阈值问题
This commit is contained in:
@@ -8,7 +8,7 @@ import { handleDestroyCode } from '../services/platforms/kuaishou-industry/destr
|
|||||||
import { handleQueryCode } from '../services/platforms/kuaishou-industry/query-code-service.js'
|
import { handleQueryCode } from '../services/platforms/kuaishou-industry/query-code-service.js'
|
||||||
import { handleConsumeCode } from '../services/platforms/kuaishou-industry/consume-code-service.js'
|
import { handleConsumeCode } from '../services/platforms/kuaishou-industry/consume-code-service.js'
|
||||||
import { buildIndustryErrorResponse } from '../services/platforms/kuaishou-industry/response.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()
|
const router = Router()
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ const industryRateLimit = createRateLimitMiddleware({
|
|||||||
router.post('/send-code', industryRateLimit, async (req, res) => {
|
router.post('/send-code', industryRateLimit, async (req, res) => {
|
||||||
const requestId = createRequestId('ksind')
|
const requestId = createRequestId('ksind')
|
||||||
const startedAt = Date.now()
|
const startedAt = Date.now()
|
||||||
|
const mergedParams = mergeRequestParams(req)
|
||||||
|
|
||||||
logIntegration('[kuaishou-industry/send-code]', '收到快手行业电子凭证发码请求', {
|
logIntegration('[kuaishou-industry/send-code]', '收到快手行业电子凭证发码请求', {
|
||||||
requestId,
|
requestId,
|
||||||
@@ -34,9 +35,23 @@ router.post('/send-code', industryRateLimit, async (req, res) => {
|
|||||||
body: req.body,
|
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 {
|
try {
|
||||||
const params = mergeRequestParams(req)
|
const params = mergeRequestParams(req)
|
||||||
const result = await handleSendCode(params)
|
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]', '发码处理完成', {
|
logIntegration('[kuaishou-industry/send-code]', '发码处理完成', {
|
||||||
requestId,
|
requestId,
|
||||||
durationMs: Date.now() - startedAt,
|
durationMs: Date.now() - startedAt,
|
||||||
@@ -45,6 +60,14 @@ router.post('/send-code', industryRateLimit, async (req, res) => {
|
|||||||
res.status(200).json(result)
|
res.status(200).json(result)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : '系统异常'
|
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]', '发码处理失败', {
|
logIntegration('[kuaishou-industry/send-code]', '发码处理失败', {
|
||||||
requestId,
|
requestId,
|
||||||
durationMs: Date.now() - startedAt,
|
durationMs: Date.now() - startedAt,
|
||||||
|
|||||||
@@ -123,6 +123,25 @@ export function logExternalHttpPacket(
|
|||||||
return logIntegration(scope, message, normalizeExternalHttpPacketDetail(detail), { level })
|
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(
|
function writeLog(
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
scope: unknown,
|
scope: unknown,
|
||||||
@@ -146,12 +165,19 @@ function writeLog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeConsole(entry)
|
writeConsole(entry)
|
||||||
enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time))
|
enqueueFileWrite(entry, resolveLogFilePath(channel, entry.time), true)
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
function enqueueFileWrite(entry: LogEntry, filePath: string): void {
|
function enqueueFileWrite(
|
||||||
const line = formatLogEntry(entry, { color: false })
|
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)
|
const dateKey = extractLogDateKey(entry.time)
|
||||||
|
|
||||||
writeQueue = writeQueue
|
writeQueue = writeQueue
|
||||||
@@ -204,7 +230,7 @@ export function shouldWriteLog(
|
|||||||
|
|
||||||
export function formatLogEntry(
|
export function formatLogEntry(
|
||||||
entry: Partial<LogEntry>,
|
entry: Partial<LogEntry>,
|
||||||
{ color = false }: { color?: boolean } = {},
|
{ color = false, sanitize = true }: { color?: boolean; sanitize?: boolean } = {},
|
||||||
): string {
|
): string {
|
||||||
const normalizedEntry = {
|
const normalizedEntry = {
|
||||||
time: String(entry?.time || new Date().toISOString()),
|
time: String(entry?.time || new Date().toISOString()),
|
||||||
@@ -212,7 +238,7 @@ export function formatLogEntry(
|
|||||||
scope: String(entry?.scope || 'app').trim() || 'app',
|
scope: String(entry?.scope || 'app').trim() || 'app',
|
||||||
message: String(entry?.message || '').trim() || '-',
|
message: String(entry?.message || '').trim() || '-',
|
||||||
pid: Number(entry?.pid || process.pid),
|
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 timestamp = colorize(formatLogTimestamp(normalizedEntry.time), ANSI.gray, color)
|
||||||
const level = colorize(
|
const level = colorize(
|
||||||
@@ -248,10 +274,21 @@ export function formatLogEntry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resolveLogFilePath(channel = 'app', time = new Date().toISOString()): string {
|
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`)
|
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(
|
export function resolveExpiredLogFilenames(
|
||||||
fileNames: string[] = [],
|
fileNames: string[] = [],
|
||||||
referenceTime = new Date().toISOString(),
|
referenceTime = new Date().toISOString(),
|
||||||
@@ -280,37 +317,37 @@ function resolveDataRoot(): string {
|
|||||||
return configured ? path.resolve(configured) : path.resolve(PROJECT_ROOT, 'data')
|
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') {
|
if (typeof value === 'undefined') {
|
||||||
return 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 {
|
try {
|
||||||
return sanitizeLogValue(
|
return sanitize
|
||||||
JSON.parse(
|
? sanitizeLogValue(JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(current))))
|
||||||
JSON.stringify(value, (_key, current) => {
|
: JSON.parse(JSON.stringify(value, (_key, current) => normalizeCurrent(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
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} catch {
|
} 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
|
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) {
|
if (!matched) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
Reference in New Issue
Block a user