142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import { LogLevel, LOG_LEVEL, LOG_TO_REALTIME } from './config'
|
|
|
|
/**
|
|
* 全项目统一日志出口。
|
|
*
|
|
* 用法:logger.debug('模块名', '消息', 任意参数)
|
|
* 级别:DEBUG < INFO < WARN < ERROR < SILENT,低于当前生效级别的调用直接返回,
|
|
* 且级别判断前置,不做任何字符串拼接,热路径(如每次请求)零开销。
|
|
*
|
|
* 输出:
|
|
* 1. console —— 按级别选用 log / warn / error,开发者工具与真机调试可见
|
|
* 2. 微信实时日志 —— 仅 WARN 及以上写入,可在小程序后台「实时日志」查询线上问题
|
|
*/
|
|
|
|
// 手动覆盖的级别(LOG_LEVEL 为 null 时按环境自动推导)
|
|
let manualLevel: LogLevel | null = LOG_LEVEL
|
|
|
|
// 解析后的生效级别缓存,避免每次调用都读环境信息
|
|
let resolvedLevel: LogLevel | null = null
|
|
|
|
const LEVEL_NAMES: Record<number, string> = {
|
|
[LogLevel.DEBUG]: 'DEBUG',
|
|
[LogLevel.INFO]: 'INFO',
|
|
[LogLevel.WARN]: 'WARN',
|
|
[LogLevel.ERROR]: 'ERROR',
|
|
}
|
|
|
|
// 按运行环境推导默认级别:开发版最详细,正式版只看异常
|
|
function resolveByEnv(): LogLevel {
|
|
try {
|
|
const info = wx.getAccountInfoSync()
|
|
const env = info.miniProgram && info.miniProgram.envVersion
|
|
if (env === 'develop') return LogLevel.DEBUG
|
|
if (env === 'trial') return LogLevel.INFO
|
|
return LogLevel.WARN
|
|
} catch (e) {
|
|
// 取不到环境信息时(低版本基础库等)退化为 DEBUG,保证开发期可见
|
|
return LogLevel.DEBUG
|
|
}
|
|
}
|
|
|
|
// 当前生效级别
|
|
export function getLevel(): LogLevel {
|
|
if (resolvedLevel === null) {
|
|
resolvedLevel = manualLevel === null ? resolveByEnv() : manualLevel
|
|
}
|
|
return resolvedLevel
|
|
}
|
|
|
|
/**
|
|
* 运行时调整日志级别(调级后对全局生效)。
|
|
* 传 null 恢复为「按环境自动」。
|
|
*/
|
|
export function setLevel(level: LogLevel | null): void {
|
|
manualLevel = level
|
|
resolvedLevel = null
|
|
}
|
|
|
|
// 实时日志单例(惰性初始化,取不到时降级为仅 console,绝不因日志抛错影响业务)
|
|
let realtimeManager: any = null
|
|
let realtimeInited = false
|
|
|
|
function getRealtimeManager(): any {
|
|
if (realtimeInited) return realtimeManager
|
|
realtimeInited = true
|
|
try {
|
|
if (typeof wx.getRealtimeLogManager === 'function') {
|
|
realtimeManager = wx.getRealtimeLogManager()
|
|
}
|
|
} catch (e) {
|
|
realtimeManager = null
|
|
}
|
|
return realtimeManager
|
|
}
|
|
|
|
// 序列化日志参数:实时日志有单条长度限制,做截断保护
|
|
function stringify(args: any[]): string {
|
|
try {
|
|
return args
|
|
.map((a) => {
|
|
if (typeof a === 'string') return a
|
|
try {
|
|
return JSON.stringify(a)
|
|
} catch (e) {
|
|
return String(a)
|
|
}
|
|
})
|
|
.join(' ')
|
|
.slice(0, 1000)
|
|
} catch (e) {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
function emit(level: LogLevel, module: string, args: any[]): void {
|
|
if (level < getLevel()) return
|
|
|
|
const prefix = `[${LEVEL_NAMES[level]}][${module}]`
|
|
|
|
// console 通道
|
|
if (level === LogLevel.ERROR) {
|
|
console.error(prefix, ...args)
|
|
} else if (level === LogLevel.WARN) {
|
|
console.warn(prefix, ...args)
|
|
} else {
|
|
console.log(prefix, ...args)
|
|
}
|
|
|
|
// 实时日志通道:仅 WARN 及以上,避免击穿配额
|
|
if (!LOG_TO_REALTIME || level < LogLevel.WARN) return
|
|
const manager = getRealtimeManager()
|
|
if (!manager) return
|
|
const text = `${prefix} ${stringify(args)}`
|
|
try {
|
|
if (level === LogLevel.ERROR && typeof manager.error === 'function') manager.error(text)
|
|
else if (typeof manager.warn === 'function') manager.warn(text)
|
|
} catch (e) {
|
|
// 日志写入失败不影响业务
|
|
}
|
|
}
|
|
|
|
export const logger = {
|
|
/** 开发期流程细节:页面入参、请求发起/成功、数据条数 */
|
|
debug(module: string, ...args: any[]): void {
|
|
emit(LogLevel.DEBUG, module, args)
|
|
},
|
|
/** 关键流程节点:登录成功、刷新成功、下单/上传/分享成功 */
|
|
info(module: string, ...args: any[]): void {
|
|
emit(LogLevel.INFO, module, args)
|
|
},
|
|
/** 可恢复异常与静默降级:静默 catch、跳转失败、非关键请求失败 */
|
|
warn(module: string, ...args: any[]): void {
|
|
emit(LogLevel.WARN, module, args)
|
|
},
|
|
/** 真正的错误:网络异常、业务错误码、账号禁用、登录态失效 */
|
|
error(module: string, ...args: any[]): void {
|
|
emit(LogLevel.ERROR, module, args)
|
|
},
|
|
}
|
|
|
|
export default logger
|