starmusic-pc
1/**
2 * 自定义源运行日志
3 *
4 * 收集脚本 lx.log 输出、宿主 ↔ 脚本的 API 往返、脚本内部 HTTP 请求以及初始化状态,
5 * 供设置页「诊断 → 自定义源运行日志」查看,用于排查播放地址 / 歌词 / 封面获取失败等问题。
6 *
7 * 仅保存在内存(最多 MAX_LOGS 条),应用重启后清空 —— 日志写入频繁,
8 * 落盘会造成同步存储压力,排查场景下也不需要跨启动保留。
9 */
10
11/** 最多保留的日志条数(超出时丢弃最早的) */
12const MAX_LOGS = 400
13
14let logs = []
15let seq = 0
16const listeners = new Set()
17
18const pad = (n) => (n < 10 ? '0' + n : String(n))
19
20const formatTime = () => {
21 const d = new Date()
22 return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
23}
24
25/**
26 * 追加一条日志
27 * @param {string} type log | init | request | response | error | http
28 * @param {string} message 概要文本
29 * @param {string} [detail] 补充信息(错误详情、URL 等)
30 */
31export const addLog = (type, message, detail) => {
32 const item = {
33 id: ++seq,
34 time: formatTime(),
35 type: type || 'log',
36 message: message == null ? '' : String(message),
37 detail: detail == null ? '' : String(detail),
38 }
39 logs.push(item)
40 if (logs.length > MAX_LOGS) logs.splice(0, logs.length - MAX_LOGS)
41 listeners.forEach((fn) => {
42 try {
43 fn(item)
44 } catch (e) {
45 // 单个订阅者异常不影响主流程
46 }
47 })
48 return item
49}
50
51/** 获取全部日志(副本) */
52export const getLogs = () => [...logs]
53
54/** 日志条数 */
55export const getLogCount = () => logs.length
56
57/** 清空日志 */
58export const clearLogs = () => {
59 logs = []
60 listeners.forEach((fn) => {
61 try {
62 fn(null)
63 } catch (e) {
64 // ignore
65 }
66 })
67}
68
69/**
70 * 订阅日志变化
71 * @param {(item: object|null) => void} fn item 为 null 表示日志已被清空
72 * @returns 取消订阅函数
73 */
74export const onLogChange = (fn) => {
75 listeners.add(fn)
76 return () => listeners.delete(fn)
77}
78
79/** 格式化为可复制的纯文本 */
80export const formatLogsForCopy = () =>
81 logs
82 .map((item) => `[${item.time}][${item.type}] ${item.message}${item.detail ? ' | ' + item.detail : ''}`)
83 .join('\n')