Files
svn/tech/client/交接文档/谭健交接文档/Script/LogServer/handler/LogSave/LogSave.ts
2025-08-04 10:46:00 +08:00

121 lines
4.0 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import { Handler } from '../../Server';
import { Error, HandlerConfig, Log } from '../../type';
const enum FileType {
Log = 'log', Error = 'error'
}
const FILE_CLOSE_COUNTDOWN = 60 * 10;
const FILE_CLOSE_CHECK_DELAY = 1;
const LOG_LEVEL_TITLE = [
null, 'LOG', 'WARN', 'ERROR'
]
export default class LogSave implements Handler {
private readonly fileDir: string = null;
private timer: NodeJS.Timer = null;
private fileCache: { [id: string]: { countdown: number, stream: fs.WriteStream } } = null;
constructor(config: HandlerConfig) {
this.fileCache = {};
if (config.path)
this.fileDir = config.path as string;
else
this.fileDir = path.join(__dirname, '..', 'log');
}
private mkdir(dir: string): void {
if (!fs.existsSync(dir)) {
let parentDir = path.dirname(dir);
this.mkdir(parentDir);
fs.mkdirSync(dir);
}
}
public onServerStart(): void {
this.timer = setInterval(() => this.checkFileCache(), FILE_CLOSE_CHECK_DELAY * 1000);
}
public onServerStop(): void {
this.timer && clearInterval(this.timer);
this.timer = null;
this.checkFileCache(true);
}
public onClientConnected(id: string): void {
}
public onClientLogMessage(id: string, logs: Log[]): void {
const fileStream = this.openFile(id, FileType.Log);
logs.forEach(log => {
const date = new Date(log.time);
const timeString = `${date.getHours()}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
fileStream.write(`[${timeString}][${LOG_LEVEL_TITLE[log.level]}] ${log.text}\n`);
});
}
public onClientErrorMessage(id: string, error: Error): void {
const fileStream = this.openFile(id, FileType.Error);
const date = new Date(error.time);
const timeString = `${date.getHours()}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
let errorString = `time: ${timeString}\n`;
for (const key in error) {
key !== 'time' && (errorString += `${key}: ${error[key]}\n`);
}
errorString += '\n'
fileStream.write(errorString);
}
public onClientDisconnect(id: string): void {
this.closeFile(id, FileType.Log);
this.closeFile(id, FileType.Error);
}
private getFilePath(id: string, fileType: FileType): string {
const date = new Date();
const dateString = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)}`;
const dirPath = path.join(this.fileDir, dateString, fileType);
this.mkdir(dirPath);
return path.join(dirPath, `${id}.txt`);
}
private openFile(id: string, fileType: FileType): fs.WriteStream {
const fileName = this.getFilePath(id, fileType);
let fileData = this.fileCache[fileName];
if (!fileData) {
const stream = fs.createWriteStream(fileName, { flags: 'a' });
fileData = this.fileCache[fileName] = { countdown: FILE_CLOSE_COUNTDOWN, stream };
}
fileData.countdown = FILE_CLOSE_COUNTDOWN;
return fileData.stream;
}
private closeFile(id: string, fileType: FileType): void {
const fileName = this.getFilePath(id, fileType);
const fileData = this.fileCache[fileName];
if (!fileData) return;
fileData.stream.close();
this.fileCache[fileName] = null;
delete this.fileCache[fileName];
}
private checkFileCache(focusClose: boolean = false): void {
for (const fileName in this.fileCache) {
const fileData = this.fileCache[fileName];
if (!fileData) continue;
if (!focusClose && fileData.countdown-- > 0) continue;
fileData.stream.close();
this.fileCache[fileName] = null;
delete this.fileCache[fileName];
}
}
}