Skip to content

Commit 22724dc

Browse files
authored
feat(agent-tars): log file rotation (close: #360) (#359)
1 parent d23d005 commit 22724dc

File tree

1 file changed

+93
-5
lines changed

1 file changed

+93
-5
lines changed

apps/agent-tars/src/main/utils/logger.ts

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ import { shell } from 'electron';
55
import { ConsoleLogger, LogLevel } from '@agent-infra/logger';
66
import { getOmegaDir } from '../mcp/client';
77

8+
/**
9+
* Maximum size of a log file (10MB)
10+
*/
11+
export const MAX_LOG_FILE_SIZE = 10 * 1024 * 1024;
12+
13+
/**
14+
* Maximum number of log files to keep
15+
*/
16+
export const MAX_LOG_FILES = 5;
17+
818
// Ensure log directory exists
919
const ensureLogDir = async (): Promise<string> => {
1020
const omegaDir = await getOmegaDir();
@@ -24,6 +34,7 @@ const createLogFilePath = async (): Promise<string> => {
2434
class FileLogger extends ConsoleLogger {
2535
private logFilePath: string | null = null;
2636
private initPromise: Promise<void> | null = null;
37+
private currentLogFileSize = 0;
2738

2839
constructor(prefix = '', level: LogLevel = LogLevel.INFO) {
2940
super(prefix, level);
@@ -47,12 +58,19 @@ class FileLogger extends ConsoleLogger {
4758
// Ensure log file exists
4859
await fs.ensureFile(this.logFilePath);
4960

61+
// Get current file size if it exists
62+
try {
63+
const stats = await fs.stat(this.logFilePath);
64+
this.currentLogFileSize = stats.size;
65+
} catch (error) {
66+
this.currentLogFileSize = 0;
67+
}
68+
5069
// Add startup marker
5170
const timestamp = new Date().toISOString();
52-
await fs.appendFile(
53-
this.logFilePath,
54-
`\n\n--- Agent TARS started at ${timestamp} ---\n\n`,
55-
);
71+
const startupMessage = `\n\n--- Agent TARS started at ${timestamp} ---\n\n`;
72+
await fs.appendFile(this.logFilePath, startupMessage);
73+
this.currentLogFileSize += Buffer.byteLength(startupMessage);
5674
} catch (error) {
5775
console.error('Failed to initialize log file:', error);
5876
// Reset Promise to allow retry on next attempt
@@ -63,14 +81,84 @@ class FileLogger extends ConsoleLogger {
6381
return this.initPromise;
6482
}
6583

84+
private async checkLogFileSize(): Promise<void> {
85+
if (this.currentLogFileSize > MAX_LOG_FILE_SIZE && this.logFilePath) {
86+
// Create a new log file name with timestamp
87+
const dir = path.dirname(this.logFilePath);
88+
const baseFileName = path.basename(this.logFilePath);
89+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
90+
const rotatedFileName = `${baseFileName}.${timestamp}`;
91+
const rotatedFilePath = path.join(dir, rotatedFileName);
92+
93+
try {
94+
// Rename current log file to include timestamp
95+
await fs.rename(this.logFilePath, rotatedFilePath);
96+
97+
// Clean up old log files if there are too many
98+
await this.cleanupOldLogFiles(dir);
99+
100+
// Reset current log file
101+
this.logFilePath = null;
102+
this.currentLogFileSize = 0;
103+
await this.initLogFile();
104+
} catch (error) {
105+
console.error('Failed to rotate log file:', error);
106+
}
107+
}
108+
}
109+
110+
private async cleanupOldLogFiles(logDir: string): Promise<void> {
111+
try {
112+
// Get all log files
113+
const files = await fs.readdir(logDir);
114+
const logFiles = files.filter(
115+
(file) => file.startsWith('agent-tars-') && file.endsWith('.log'),
116+
);
117+
118+
// If we have more than MAX_LOG_FILES, delete the oldest ones
119+
if (logFiles.length > MAX_LOG_FILES) {
120+
// Sort files by creation time (oldest first)
121+
const fileStats = await Promise.all(
122+
logFiles.map(async (file) => {
123+
const filePath = path.join(logDir, file);
124+
const stats = await fs.stat(filePath);
125+
return { file, filePath, ctime: stats.ctime };
126+
}),
127+
);
128+
129+
fileStats.sort((a, b) => a.ctime.getTime() - b.ctime.getTime());
130+
131+
// Delete oldest files
132+
const filesToDelete = fileStats.slice(
133+
0,
134+
fileStats.length - MAX_LOG_FILES,
135+
);
136+
for (const fileInfo of filesToDelete) {
137+
await fs.unlink(fileInfo.filePath);
138+
}
139+
}
140+
} catch (error) {
141+
console.error('Failed to clean up old log files:', error);
142+
}
143+
}
144+
66145
private async writeToFile(message: string) {
67146
// Ensure log file is initialized
68147
await this.initLogFile();
69148

70149
if (this.logFilePath) {
71150
try {
72151
const timestamp = new Date().toISOString();
73-
await fs.appendFile(this.logFilePath, `[${timestamp}] ${message}\n`);
152+
const logEntry = `[${timestamp}] ${message}\n`;
153+
const logSize = Buffer.byteLength(logEntry);
154+
155+
// Check if adding this log would exceed the size limit
156+
if (this.currentLogFileSize + logSize > MAX_LOG_FILE_SIZE) {
157+
await this.checkLogFileSize();
158+
}
159+
160+
await fs.appendFile(this.logFilePath, logEntry);
161+
this.currentLogFileSize += logSize;
74162
} catch (error) {
75163
console.error('Failed to write to log file:', error);
76164
}

0 commit comments

Comments
 (0)