Files
QZMusic-Web/src/stores/log.ts

125 lines
3.7 KiB
TypeScript

import { defineStore } from 'pinia';
import { ref, watch } from 'vue';
export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
export interface LogEntry {
id: number;
time: string;
level: LogLevel;
module: string;
message: string;
detail?: any;
}
const MAX_LOGS = 500;
const STORAGE_KEY = 'qz-app-logs';
const LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };
function loadFromStorage(): LogEntry[] {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) {
const parsed = JSON.parse(raw);
if (Array.isArray(parsed)) return parsed as LogEntry[];
}
} catch {
// ignore
}
return [];
}
export const useLogStore = defineStore('log', () => {
const logs = ref<LogEntry[]>(loadFromStorage());
let nextId = logs.value.length > 0 ? Math.max(...logs.value.map((l) => l.id)) + 1 : 1;
watch(
logs,
(newLogs) => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(newLogs));
} catch {
// ignore quota issues
}
},
{ deep: true }
);
const add = (level: LogLevel, module: string, message: string, detail?: any) => {
const now = new Date();
const hh = now.getHours().toString().padStart(2, '0');
const mm = now.getMinutes().toString().padStart(2, '0');
const ss = now.getSeconds().toString().padStart(2, '0');
const ms = now.getMilliseconds().toString().padStart(3, '0');
const entry: LogEntry = {
id: nextId++,
time: `${hh}:${mm}:${ss}.${ms}`,
level,
module,
message,
detail,
};
logs.value.push(entry);
if (logs.value.length > MAX_LOGS) {
logs.value = logs.value.slice(-MAX_LOGS);
}
// 同步到浏览器控制台,方便调试
const consoleArgs = [`[${entry.time}] [${level.toUpperCase()}] [${module}]`, message];
if (detail !== undefined) consoleArgs.push(detail);
try {
if (level === 'error') console.error(...consoleArgs);
else if (level === 'warn') console.warn(...consoleArgs);
else if (level === 'debug') console.debug(...consoleArgs);
else console.log(...consoleArgs);
} catch {
// ignore
}
};
const info = (module: string, message: string, detail?: any) => add('info', module, message, detail);
const warn = (module: string, message: string, detail?: any) => add('warn', module, message, detail);
const error = (module: string, message: string, detail?: any) => add('error', module, message, detail);
const debug = (module: string, message: string, detail?: any) => add('debug', module, message, detail);
const clear = () => {
logs.value = [];
nextId = 1;
};
const filter = (module?: string, level?: LogLevel) => {
return logs.value.filter((l) => {
if (module && l.module !== module) return false;
if (level && LEVEL_ORDER[l.level] < LEVEL_ORDER[level]) return false;
return true;
});
};
const moduleList = () => {
const set = new Set<string>();
for (const l of logs.value) set.add(l.module);
return Array.from(set).sort();
};
const countByLevel = () => {
const c = { info: 0, warn: 0, error: 0, debug: 0 };
for (const l of logs.value) c[l.level]++;
return c;
};
return {
logs,
info,
warn,
error,
debug,
add,
clear,
filter,
moduleList,
countByLevel,
};
});