|
|
const STORAGE_KEY = 'app_runtime_logs'
|
|
|
const MAX_LOGS = 800
|
|
|
|
|
|
let logs = []
|
|
|
let installed = false
|
|
|
let persistTimer = null
|
|
|
const nativeConsole = {
|
|
|
log: typeof console.log === 'function' ? console.log.bind(console) : () => {},
|
|
|
info: typeof console.info === 'function' ? console.info.bind(console) : null,
|
|
|
warn: typeof console.warn === 'function' ? console.warn.bind(console) : null,
|
|
|
error: typeof console.error === 'function' ? console.error.bind(console) : null,
|
|
|
}
|
|
|
|
|
|
const loadFromStorage = () => {
|
|
|
try {
|
|
|
const raw = uni.getStorageSync(STORAGE_KEY)
|
|
|
if (!raw) {
|
|
|
logs = []
|
|
|
return
|
|
|
}
|
|
|
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw
|
|
|
logs = Array.isArray(parsed) ? parsed : []
|
|
|
} catch (error) {
|
|
|
logs = []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const persistNow = () => {
|
|
|
if (persistTimer) {
|
|
|
clearTimeout(persistTimer)
|
|
|
persistTimer = null
|
|
|
}
|
|
|
try {
|
|
|
uni.setStorageSync(STORAGE_KEY, JSON.stringify(logs))
|
|
|
} catch (error) {
|
|
|
// ignore persist errors
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const schedulePersist = () => {
|
|
|
if (persistTimer) return
|
|
|
persistTimer = setTimeout(() => {
|
|
|
persistTimer = null
|
|
|
persistNow()
|
|
|
}, 300)
|
|
|
}
|
|
|
|
|
|
const formatArg = (arg) => {
|
|
|
if (arg === null) return 'null'
|
|
|
if (arg === undefined) return 'undefined'
|
|
|
if (typeof arg === 'string') return arg
|
|
|
if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg)
|
|
|
try {
|
|
|
return JSON.stringify(arg)
|
|
|
} catch (error) {
|
|
|
return String(arg)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
|
|
|
|
const formatTime = (date = new Date()) => {
|
|
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${String(date.getMilliseconds()).padStart(3, '0')}`
|
|
|
}
|
|
|
|
|
|
export const appendLog = (level, args) => {
|
|
|
const message = (Array.isArray(args) ? args : [args])
|
|
|
.map(formatArg)
|
|
|
.join(' ')
|
|
|
logs.push({
|
|
|
time: formatTime(),
|
|
|
level: level || 'log',
|
|
|
message,
|
|
|
})
|
|
|
if (logs.length > MAX_LOGS) {
|
|
|
logs = logs.slice(logs.length - MAX_LOGS)
|
|
|
}
|
|
|
schedulePersist()
|
|
|
}
|
|
|
|
|
|
/** 业务关键日志:写入本地日志,同时输出到原生 console(避免二次拦截) */
|
|
|
export const appLog = (level, ...args) => {
|
|
|
const lv = level || 'info'
|
|
|
appendLog(lv, args)
|
|
|
const printer = nativeConsole[lv] || nativeConsole.log || (() => {})
|
|
|
try {
|
|
|
printer(...args)
|
|
|
} catch (error) {
|
|
|
// ignore
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export const flushLogs = () => {
|
|
|
persistNow()
|
|
|
}
|
|
|
|
|
|
export const getLogs = () => {
|
|
|
if (!logs.length) {
|
|
|
loadFromStorage()
|
|
|
}
|
|
|
return logs.slice()
|
|
|
}
|
|
|
|
|
|
export const getLogsText = () => {
|
|
|
return getLogs()
|
|
|
.map((item) => `[${item.time}] [${item.level}] ${item.message}`)
|
|
|
.join('\n')
|
|
|
}
|
|
|
|
|
|
export const clearLogs = () => {
|
|
|
logs = []
|
|
|
try {
|
|
|
uni.removeStorageSync(STORAGE_KEY)
|
|
|
} catch (error) {
|
|
|
// ignore
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export const installAppLogger = () => {
|
|
|
if (installed) return
|
|
|
installed = true
|
|
|
loadFromStorage()
|
|
|
|
|
|
nativeConsole.log = console.log.bind(console)
|
|
|
nativeConsole.info = console.info ? console.info.bind(console) : nativeConsole.log
|
|
|
nativeConsole.warn = console.warn.bind(console)
|
|
|
nativeConsole.error = console.error.bind(console)
|
|
|
|
|
|
const wrap = (level, original) => {
|
|
|
return (...args) => {
|
|
|
try {
|
|
|
appendLog(level, args)
|
|
|
} catch (error) {
|
|
|
// ignore logger errors
|
|
|
}
|
|
|
if (typeof original === 'function') {
|
|
|
return original.apply(console, args)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
console.log = wrap('log', nativeConsole.log)
|
|
|
console.info = wrap('info', nativeConsole.info)
|
|
|
console.warn = wrap('warn', nativeConsole.warn)
|
|
|
console.error = wrap('error', nativeConsole.error)
|
|
|
|
|
|
appLog('info', '[appLog] logger installed')
|
|
|
}
|