Skip to content

Commit 3f41c64

Browse files
authored
quick log rotation (#2432)
1 parent 8b46463 commit 3f41c64

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

emain/log.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,107 @@
11
// Copyright 2025, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import fs from "fs";
45
import path from "path";
56
import { format } from "util";
67
import winston from "winston";
78
import { getWaveDataDir, isDev } from "./platform";
89

910
const oldConsoleLog = console.log;
1011

12+
function findHighestLogNumber(logsDir: string): number {
13+
if (!fs.existsSync(logsDir)) {
14+
return 0;
15+
}
16+
const files = fs.readdirSync(logsDir);
17+
let maxNum = 0;
18+
for (const file of files) {
19+
const match = file.match(/^waveapp\.(\d+)\.log$/);
20+
if (match) {
21+
const num = parseInt(match[1], 10);
22+
if (num > maxNum) {
23+
maxNum = num;
24+
}
25+
}
26+
}
27+
return maxNum;
28+
}
29+
30+
function pruneOldLogs(logsDir: string): { pruned: string[]; error: any } {
31+
if (!fs.existsSync(logsDir)) {
32+
return { pruned: [], error: null };
33+
}
34+
35+
const files = fs.readdirSync(logsDir);
36+
const logFiles: { name: string; num: number }[] = [];
37+
38+
for (const file of files) {
39+
const match = file.match(/^waveapp\.(\d+)\.log$/);
40+
if (match) {
41+
logFiles.push({ name: file, num: parseInt(match[1], 10) });
42+
}
43+
}
44+
45+
if (logFiles.length <= 5) {
46+
return { pruned: [], error: null };
47+
}
48+
49+
logFiles.sort((a, b) => b.num - a.num);
50+
const toDelete = logFiles.slice(5);
51+
const pruned: string[] = [];
52+
let firstError: any = null;
53+
54+
for (const logFile of toDelete) {
55+
try {
56+
fs.unlinkSync(path.join(logsDir, logFile.name));
57+
pruned.push(logFile.name);
58+
} catch (e) {
59+
if (firstError == null) {
60+
firstError = e;
61+
}
62+
}
63+
}
64+
65+
return { pruned, error: firstError };
66+
}
67+
68+
function rotateLogIfNeeded(): string | null {
69+
const waveDataDir = getWaveDataDir();
70+
const logFile = path.join(waveDataDir, "waveapp.log");
71+
const logsDir = path.join(waveDataDir, "logs");
72+
73+
if (!fs.existsSync(logsDir)) {
74+
fs.mkdirSync(logsDir, { recursive: true });
75+
}
76+
77+
if (!fs.existsSync(logFile)) {
78+
return null;
79+
}
80+
81+
const stats = fs.statSync(logFile);
82+
if (stats.size > 10 * 1024 * 1024) {
83+
const nextNum = findHighestLogNumber(logsDir) + 1;
84+
const rotatedPath = path.join(logsDir, `waveapp.${nextNum}.log`);
85+
fs.renameSync(logFile, rotatedPath);
86+
return rotatedPath;
87+
}
88+
return null;
89+
}
90+
91+
let logRotateError: any = null;
92+
let rotatedPath: string | null = null;
93+
let prunedFiles: string[] = [];
94+
let pruneError: any = null;
95+
try {
96+
rotatedPath = rotateLogIfNeeded();
97+
const logsDir = path.join(getWaveDataDir(), "logs");
98+
const pruneResult = pruneOldLogs(logsDir);
99+
prunedFiles = pruneResult.pruned;
100+
pruneError = pruneResult.error;
101+
} catch (e) {
102+
logRotateError = e;
103+
}
104+
11105
const loggerTransports: winston.transport[] = [
12106
new winston.transports.File({ filename: path.join(getWaveDataDir(), "waveapp.log"), level: "info" }),
13107
];
@@ -23,6 +117,7 @@ const loggerConfig = {
23117
transports: loggerTransports,
24118
};
25119
const logger = winston.createLogger(loggerConfig);
120+
26121
function log(...msg: any[]) {
27122
try {
28123
logger.info(format(...msg));
@@ -31,4 +126,17 @@ function log(...msg: any[]) {
31126
}
32127
}
33128

129+
if (logRotateError != null) {
130+
log("error rotating/pruning logs (non-fatal):", logRotateError);
131+
}
132+
if (rotatedPath != null) {
133+
log("rotated old log file to:", rotatedPath);
134+
}
135+
if (prunedFiles.length > 0) {
136+
log("pruned old log files:", prunedFiles.join(", "));
137+
}
138+
if (pruneError != null) {
139+
log("error pruning some log files (non-fatal):", pruneError);
140+
}
141+
34142
export { log };

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)