feat: 初始提交(仅核心代码,已排除大文件)
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const logsDir = path.join(process.cwd(), 'logs')
|
||||
if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true })
|
||||
const logFile = path.join(logsDir, 'app.log')
|
||||
if (!fs.existsSync(logFile)) fs.writeFileSync(logFile, '')
|
||||
const channelFileCache = new Map()
|
||||
let pendingWrites = new Map()
|
||||
let flushScheduled = false
|
||||
|
||||
const ts = () => new Date().toISOString()
|
||||
|
||||
const ensureChannel = channel => {
|
||||
const name = String(channel || '').trim()
|
||||
if (!name) return null
|
||||
if (channelFileCache.has(name)) return channelFileCache.get(name)
|
||||
const file = path.join(logsDir, `${name}.log`)
|
||||
if (!fs.existsSync(file)) fs.writeFileSync(file, '')
|
||||
channelFileCache.set(name, file)
|
||||
return file
|
||||
}
|
||||
|
||||
const flushPendingWrites = () => {
|
||||
flushScheduled = false
|
||||
if (pendingWrites.size === 0) return
|
||||
const batch = pendingWrites
|
||||
pendingWrites = new Map()
|
||||
batch.forEach((content, file) => {
|
||||
fs.appendFile(file, content, () => {})
|
||||
})
|
||||
if (pendingWrites.size > 0) scheduleFlush()
|
||||
}
|
||||
|
||||
const scheduleFlush = () => {
|
||||
if (flushScheduled) return
|
||||
flushScheduled = true
|
||||
setImmediate(flushPendingWrites)
|
||||
}
|
||||
|
||||
const enqueueWrite = (file, line) => {
|
||||
const prev = pendingWrites.get(file) || ''
|
||||
pendingWrites.set(file, prev + line)
|
||||
scheduleFlush()
|
||||
}
|
||||
|
||||
const log = (msg, channel) => {
|
||||
const line = `${ts()} ${String(msg)}\n`
|
||||
enqueueWrite(logFile, line)
|
||||
const chFile = ensureChannel(channel)
|
||||
if (chFile) enqueueWrite(chFile, line)
|
||||
}
|
||||
|
||||
const logJSON = (label, obj, channel) => {
|
||||
let s = ''
|
||||
try { s = JSON.stringify(obj) } catch { s = String(obj) }
|
||||
log(`${label} ${s}`, channel)
|
||||
}
|
||||
|
||||
const getLogs = (limit = 2000, channel) => {
|
||||
let file = logFile
|
||||
const chFile = ensureChannel(channel)
|
||||
if (chFile) file = chFile
|
||||
let content = ''
|
||||
try {
|
||||
const stat = fs.statSync(file)
|
||||
const maxReadBytes = Math.min(stat.size, 512 * 1024)
|
||||
const start = Math.max(0, stat.size - maxReadBytes)
|
||||
const fd = fs.openSync(file, 'r')
|
||||
const buf = Buffer.alloc(maxReadBytes)
|
||||
const bytesRead = fs.readSync(fd, buf, 0, maxReadBytes, start)
|
||||
fs.closeSync(fd)
|
||||
content = buf.subarray(0, bytesRead).toString('utf-8')
|
||||
} catch {}
|
||||
const lines = content.split(/\r?\n/).filter(Boolean)
|
||||
const tail = lines.slice(Math.max(0, lines.length - limit))
|
||||
return tail
|
||||
}
|
||||
|
||||
const clearLogs = channel => {
|
||||
const chFile = ensureChannel(channel)
|
||||
if (chFile) fs.truncateSync(chFile, 0)
|
||||
else fs.truncateSync(logFile, 0)
|
||||
}
|
||||
|
||||
module.exports = { log, logJSON, getLogs, clearLogs }
|
||||
Reference in New Issue
Block a user