Files
TrueGrowth/vendor/aigcpanel/electron/mapi/log/index.ts
Jammy 52636c91ae
Some checks failed
CI / main (push) Has been cancelled
CI / release-e2e (push) Has been cancelled
Track bundled vendor runtime sources
2026-07-07 10:05:50 +08:00

320 lines
8.9 KiB
TypeScript

import electron from "electron";
import date from "date-and-time";
import path from "node:path";
import { AppEnv } from "../env";
import fs from "node:fs";
import dayjs from "dayjs";
import FileIndex from "../file";
let fileName = null;
let fileStream = null;
let appFileNames = {};
let appFileStreams = {};
const stringDatetime = () => {
return date.format(new Date(), "YYYYMMDD");
};
const jsonStringifyLogData = (data: any) => {
return JSON.stringify(data, (key, value) => {
if (typeof value === "string" && value.length > 200) {
if (
value.startsWith("data:") ||
value.substring(0, 190).match(/^[a-zA-Z0-9+/=]+\s*$/)
) {
return (
value.substring(0, 100) + "...(length=" + value.length + ")"
);
}
}
return value;
});
};
const logsDir = () => {
return path.join(AppEnv.userData, "logs");
};
const appLogsDir = () => {
return path.join(AppEnv.dataRoot, "logs");
};
const root = () => {
return logsDir();
};
const file = () => {
return path.join(logsDir(), "log_" + stringDatetime() + ".log");
};
const appFile = (name: string) => {
return path.join(appLogsDir(), name + "_" + stringDatetime() + ".log");
};
const cleanOldLogs = (keepDays: number) => {
const logDirs = [
// 系统日志
logsDir(),
// 应用日志
appLogsDir(),
];
for (const logDir of logDirs) {
if (!fs.existsSync(logDir)) {
return;
}
const files = fs.readdirSync(logDir);
const now = new Date();
// console.log('cleanOldLogs', logDir, files)
for (let file of files) {
const filePath = path.join(logDir, file);
let date = null;
for (let s of file.split(/[_\\.]/)) {
// 匹配 YYYYMMDD
if (s.match(/^\d{8}$/)) {
date = s;
break;
}
}
if (!date) {
continue;
}
const fileDate = new Date(
parseInt(date.substring(0, 4)),
parseInt(date.substring(4, 6)) - 1,
parseInt(date.substring(6, 8)),
);
const diff = Math.abs(now.getTime() - fileDate.getTime());
const diffDays = Math.ceil(diff / (1000 * 3600 * 24));
// console.log('fileDate', file, fileDate, diffDays)
if (diffDays > keepDays) {
fs.unlinkSync(filePath);
}
}
}
};
const log = (level: "INFO" | "ERROR", label: string, data: any = null) => {
if (fileName !== file()) {
fileName = file();
const logDir = logsDir();
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
if (fileStream) {
fileStream.end();
}
fileStream = fs.createWriteStream(fileName, { flags: "a" });
cleanOldLogs(14);
}
let line = [];
line.push(date.format(new Date(), "YYYY-MM-DD HH:mm:ss"));
line.push(level);
line.push(label);
if (data) {
if (!["number", "string"].includes(typeof data)) {
data = jsonStringifyLogData(data);
}
line.push(data);
}
console.log(line.join(" - "));
fileStream.write(line.join(" - ") + "\n");
};
const info = (label: string, data: any = null) => {
return log("INFO", label, data);
};
const error = (label: string, data: any = null) => {
return log("ERROR", label, data);
};
const appLog = (
name: string,
level: "INFO" | "ERROR",
label: string,
data: any = null,
) => {
let fileChanged = false;
if (appFileNames[name] !== appFile(name)) {
appFileNames[name] = appFile(name);
fileChanged = true;
}
if (fileChanged || !appFileStreams[name]) {
if (appFileStreams[name]) {
appFileStreams[name].end();
}
const logDir = appLogsDir();
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
appFileStreams[name] = fs.createWriteStream(appFileNames[name], {
flags: "a",
});
}
let line = [];
line.push(date.format(new Date(), "YYYY-MM-DD HH:mm:ss"));
line.push(level);
line.push(label);
if (data) {
if (!["number", "string"].includes(typeof data)) {
data = JSON.stringify(data);
}
line.push(data);
}
console.log(`[APP:${name}] - ` + line.join(" - "));
appFileStreams[name].write(line.join(" - ") + "\n");
};
const appPath = (name: string) => {
if (!appFileNames[name]) {
appFileNames[name] = appFile(name);
}
return appFileNames[name];
};
const appInfo = (name: string, label: string, data: any = null) => {
return appLog(name, "INFO", label, data);
};
const appError = (name: string, label: string, data: any = null) => {
return appLog(name, "ERROR", label, data);
};
const infoRenderOrMain = (label: string, data: any = null) => {
if (electron.ipcRenderer) {
console.log("Log.info", label, data);
return electron.ipcRenderer.invoke("log:info", label, data);
} else {
return info(label, data);
}
};
const errorRenderOrMain = (label: string, data: any = null) => {
if (electron.ipcRenderer) {
console.error("Log.error", label, data);
return electron.ipcRenderer.invoke("log:error", label, data);
} else {
return error(label, data);
}
};
const appInfoRenderOrMain = (name: string, label: string, data: any = null) => {
if (electron.ipcRenderer) {
console.log("Log.appInfo", name, label, data);
return electron.ipcRenderer.invoke("log:appInfo", name, label, data);
} else {
return appInfo(name, label, data);
}
};
const appErrorRenderOrMain = (
name: string,
label: string,
data: any = null,
) => {
if (electron.ipcRenderer) {
console.error("Log.appError", name, label, data);
return electron.ipcRenderer.invoke("log:appError", name, label, data);
} else {
return appError(name, label, data);
}
};
const collectRenderOrMain = async (option?: {
startTime?: string;
endTime?: string;
limit?: number;
}) => {
option = Object.assign(
{
startTime: dayjs().subtract(1, "day").format("YYYY-MM-DD HH:mm:ss"),
endTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
limit: 10 * 10000,
},
option,
);
let startMs = dayjs(option.startTime).valueOf();
let endMs = dayjs(option.endTime).valueOf();
let startDayMs = dayjs(option.startTime).startOf("day").valueOf();
let endDayMs = dayjs(option.endTime).endOf("day").valueOf();
let resultLines = [];
let logFiles = [];
logFiles = logFiles.concat(
await FileIndex.list(logsDir(), { isDataPath: false }),
);
logFiles = logFiles.concat(
await FileIndex.list(appLogsDir(), { isDataPath: false }),
);
// console.log('logFiles', logFiles)
logFiles = logFiles.filter((logFile) => {
if (logFile.isDirectory) {
return false;
}
let date = null;
for (let s of logFile.name.split(/[_\\.]/)) {
// 匹配 YYYYMMDD
if (s.match(/^\d{8}$/)) {
date = s;
break;
}
}
if (!date) {
return false;
}
const fileDate = new Date(
parseInt(date.substring(0, 4)),
parseInt(date.substring(4, 6)) - 1,
parseInt(date.substring(6, 8)),
);
if (fileDate.getTime() < startDayMs || fileDate.getTime() > endDayMs) {
return false;
}
return true;
});
// console.log('collectRenderOrMain', {
// ...option,
// logFiles, startMs, endMs, startDayMs, endDayMs
// })
for (const logFile of logFiles) {
await FileIndex.readLine(
logFile.pathname,
(line) => {
const lineParts = line.split(" - ");
const lineTime = dayjs(lineParts[0]);
// console.log('lineTime', lineParts[0], lineTime.isBefore(startMs) || lineTime.isAfter(endMs))
if (lineTime.isBefore(startMs) || lineTime.isAfter(endMs)) {
return;
}
resultLines.push(line);
},
{ isDataPath: false },
);
}
return {
startTime: option.startTime,
endTime: option.endTime,
logs: resultLines.join("\n"),
};
};
export default {
root,
info,
error,
infoRenderOrMain,
errorRenderOrMain,
appPath,
appInfo,
appError,
appInfoRenderOrMain,
appErrorRenderOrMain,
collectRenderOrMain,
jsonStringifyLogData,
};
export const Log = {
jsonStringifyLogData,
info: infoRenderOrMain,
error: errorRenderOrMain,
appPath,
appInfo: appInfoRenderOrMain,
appError: appErrorRenderOrMain,
};