125 lines
3.7 KiB
TypeScript
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,
|
|
};
|
|
});
|