开始多平台 多店铺整改
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
import { logError, logWarn } from './logger.js'
|
||||
|
||||
const RESPONSE_TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
export function buildSuccessPayload(data, msg = 'ok') {
|
||||
return {
|
||||
code: 0,
|
||||
@@ -21,8 +25,12 @@ export function buildErrorPayload(error, fallbackMessage) {
|
||||
|
||||
export function sendRouteError(res, error, fallbackMessage, scope) {
|
||||
const classification = classifyRouteError(error)
|
||||
const logger = classification.statusCode >= 500 ? console.error : console.warn
|
||||
logger(`${scope} ${fallbackMessage}:`, error)
|
||||
const logger = classification.statusCode >= 500 ? logError : logWarn
|
||||
logger(scope, fallbackMessage, {
|
||||
statusCode: classification.statusCode,
|
||||
errorCode: classification.errorCode,
|
||||
error,
|
||||
})
|
||||
res.status(classification.statusCode).json(buildErrorPayload(error, fallbackMessage))
|
||||
}
|
||||
|
||||
@@ -150,12 +158,22 @@ function formatResponseDateTime(value) {
|
||||
return String(value || '').replace('T', ' ').replace('Z', '')
|
||||
}
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
const parts = new Intl.DateTimeFormat('zh-CN', {
|
||||
timeZone: RESPONSE_TIME_ZONE,
|
||||
hour12: false,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}).formatToParts(date)
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
const tokens = Object.fromEntries(
|
||||
parts
|
||||
.filter((item) => item.type !== 'literal')
|
||||
.map((item) => [item.type, item.value]),
|
||||
)
|
||||
|
||||
return `${tokens.year}-${tokens.month}-${tokens.day} ${tokens.hour}:${tokens.minute}:${tokens.second}`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
|
||||
import { PROJECT_ROOT, runtimeConfig } from '../config/runtime.js'
|
||||
|
||||
const DATA_ROOT = resolveDataRoot()
|
||||
const LOG_DIR = path.join(DATA_ROOT, 'logs')
|
||||
const APP_LOG_FILE = path.join(LOG_DIR, 'app.log')
|
||||
const WEBHOOK_LOG_FILE = path.join(LOG_DIR, 'webhook.log')
|
||||
|
||||
let writeQueue = Promise.resolve()
|
||||
|
||||
export function createRequestId(prefix = 'req') {
|
||||
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
||||
}
|
||||
|
||||
export function logDebug(scope, message, detail) {
|
||||
return writeLog('debug', scope, message, detail)
|
||||
}
|
||||
|
||||
export function logInfo(scope, message, detail) {
|
||||
return writeLog('info', scope, message, detail)
|
||||
}
|
||||
|
||||
export function logWarn(scope, message, detail) {
|
||||
return writeLog('warn', scope, message, detail)
|
||||
}
|
||||
|
||||
export function logError(scope, message, detail) {
|
||||
return writeLog('error', scope, message, detail)
|
||||
}
|
||||
|
||||
export function logWebhook(scope, message, detail, { level = 'info' } = {}) {
|
||||
return writeLog(level, scope, message, detail, { channel: 'webhook' })
|
||||
}
|
||||
|
||||
function writeLog(level, scope, message, detail, { channel = 'app' } = {}) {
|
||||
const entry = {
|
||||
time: new Date().toISOString(),
|
||||
level,
|
||||
scope: String(scope || 'app').trim() || 'app',
|
||||
message: String(message || '').trim() || '-',
|
||||
pid: process.pid,
|
||||
detail: normalizeLogValue(detail),
|
||||
}
|
||||
|
||||
writeConsole(entry)
|
||||
enqueueFileWrite(entry, channel === 'webhook' ? WEBHOOK_LOG_FILE : APP_LOG_FILE)
|
||||
return entry
|
||||
}
|
||||
|
||||
function enqueueFileWrite(entry, filePath) {
|
||||
const line = JSON.stringify(entry)
|
||||
|
||||
writeQueue = writeQueue
|
||||
.then(async () => {
|
||||
await fs.mkdir(LOG_DIR, { recursive: true })
|
||||
await fs.appendFile(filePath, `${line}\n`, 'utf8')
|
||||
})
|
||||
.catch((error) => {
|
||||
const reason = error instanceof Error ? error.message : String(error || '未知错误')
|
||||
console.error('[logger] failed to write log file:', reason)
|
||||
})
|
||||
}
|
||||
|
||||
function writeConsole(entry) {
|
||||
const prefix = `[${entry.time}] [${entry.level}] [${entry.scope}] ${entry.message}`
|
||||
const logger = resolveConsoleMethod(entry.level)
|
||||
|
||||
if (typeof entry.detail === 'undefined') {
|
||||
logger(prefix)
|
||||
return
|
||||
}
|
||||
|
||||
logger(prefix, entry.detail)
|
||||
}
|
||||
|
||||
function resolveConsoleMethod(level) {
|
||||
if (level === 'error') {
|
||||
return console.error
|
||||
}
|
||||
|
||||
if (level === 'warn') {
|
||||
return console.warn
|
||||
}
|
||||
|
||||
return console.log
|
||||
}
|
||||
|
||||
function resolveDataRoot() {
|
||||
const configuredDatabasePath = String(runtimeConfig.database?.filePath || '').trim()
|
||||
|
||||
if (configuredDatabasePath) {
|
||||
const configuredDataRoot = path.dirname(configuredDatabasePath)
|
||||
|
||||
if (existsSync(configuredDataRoot)) {
|
||||
return configuredDataRoot
|
||||
}
|
||||
}
|
||||
|
||||
return path.resolve(PROJECT_ROOT, 'data')
|
||||
}
|
||||
|
||||
function normalizeLogValue(value) {
|
||||
if (typeof value === 'undefined') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(
|
||||
JSON.stringify(value, (_key, current) => {
|
||||
if (current instanceof Error) {
|
||||
return {
|
||||
name: current.name,
|
||||
message: current.message,
|
||||
stack: current.stack,
|
||||
statusCode: current.statusCode,
|
||||
errorCode: current.errorCode,
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof current === 'bigint') {
|
||||
return String(current)
|
||||
}
|
||||
|
||||
return current
|
||||
}),
|
||||
)
|
||||
} catch {
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user