From 01c3e9770f5357c928afac9d3b9020db12b7d7c6 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 11:07:24 -0700 Subject: [PATCH 1/6] simple rotation logic --- emain/log.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/emain/log.ts b/emain/log.ts index 6e84ef0ab4..639802f267 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -1,6 +1,7 @@ // Copyright 2025, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 +import fs from "fs"; import path from "path"; import { format } from "util"; import winston from "winston"; @@ -8,6 +9,78 @@ import { getWaveDataDir, isDev } from "./platform"; const oldConsoleLog = console.log; +function findHighestLogNumber(logsDir: string): number { + if (!fs.existsSync(logsDir)) { + return 0; + } + const files = fs.readdirSync(logsDir); + let maxNum = 0; + for (const file of files) { + const match = file.match(/^waveapp\.(\d+)\.log$/); + if (match) { + const num = parseInt(match[1], 10); + if (num > maxNum) { + maxNum = num; + } + } + } + return maxNum; +} + +function pruneOldLogs(logsDir: string) { + if (!fs.existsSync(logsDir)) { + return; + } + + const files = fs.readdirSync(logsDir); + const logFiles: { name: string; num: number }[] = []; + + for (const file of files) { + const match = file.match(/^waveapp\.(\d+)\.log$/); + if (match) { + logFiles.push({ name: file, num: parseInt(match[1], 10) }); + } + } + + if (logFiles.length <= 5) { + return; + } + + logFiles.sort((a, b) => b.num - a.num); + const toDelete = logFiles.slice(5); + + for (const logFile of toDelete) { + fs.unlinkSync(path.join(logsDir, logFile.name)); + } +} + +function rotateLogIfNeeded() { + const waveDataDir = getWaveDataDir(); + const logFile = path.join(waveDataDir, "waveapp.log"); + const logsDir = path.join(waveDataDir, "logs"); + + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + + if (!fs.existsSync(logFile)) { + return; + } + + const stats = fs.statSync(logFile); + const fileSizeMB = stats.size / (1024 * 1024); + + if (fileSizeMB > 10) { + const nextNum = findHighestLogNumber(logsDir) + 1; + const rotatedPath = path.join(logsDir, `waveapp.${nextNum}.log`); + fs.renameSync(logFile, rotatedPath); + } +} + +rotateLogIfNeeded(); +const logsDir = path.join(getWaveDataDir(), "logs"); +pruneOldLogs(logsDir); + const loggerTransports: winston.transport[] = [ new winston.transports.File({ filename: path.join(getWaveDataDir(), "waveapp.log"), level: "info" }), ]; From 979684f5d76f6c97958ba7d009dd136cbf6fa805 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 11:12:09 -0700 Subject: [PATCH 2/6] cleaner size logic --- emain/log.ts | 4 +--- package-lock.json | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/emain/log.ts b/emain/log.ts index 639802f267..14e54a58e5 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -68,9 +68,7 @@ function rotateLogIfNeeded() { } const stats = fs.statSync(logFile); - const fileSizeMB = stats.size / (1024 * 1024); - - if (fileSizeMB > 10) { + if (stats.size > 10 * 1024 * 1024) { const nextNum = findHighestLogNumber(logsDir) + 1; const rotatedPath = path.join(logsDir, `waveapp.${nextNum}.log`); fs.renameSync(logFile, rotatedPath); diff --git a/package-lock.json b/package-lock.json index dc23c81a4d..b175b6dd22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "waveterm", - "version": "0.12.0-beta.1", + "version": "0.12.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "waveterm", - "version": "0.12.0-beta.1", + "version": "0.12.0-beta.2", "hasInstallScript": true, "license": "Apache-2.0", "workspaces": [ From a55847ac225f02e22f071ab4b0e0cef9b600cb91 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 11:52:14 -0700 Subject: [PATCH 3/6] try/catch and log an error once the logger is set up. --- emain/log.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/emain/log.ts b/emain/log.ts index 14e54a58e5..15f9d456a6 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -75,9 +75,14 @@ function rotateLogIfNeeded() { } } -rotateLogIfNeeded(); -const logsDir = path.join(getWaveDataDir(), "logs"); -pruneOldLogs(logsDir); +let logRotateError: any = null; +try { + rotateLogIfNeeded(); + const logsDir = path.join(getWaveDataDir(), "logs"); + pruneOldLogs(logsDir); +} catch (e) { + logRotateError = e; +} const loggerTransports: winston.transport[] = [ new winston.transports.File({ filename: path.join(getWaveDataDir(), "waveapp.log"), level: "info" }), @@ -94,6 +99,9 @@ const loggerConfig = { transports: loggerTransports, }; const logger = winston.createLogger(loggerConfig); +if (logRotateError != null) { + logger.error("error rotating/pruning logs (non-fatal):", logRotateError); +} function log(...msg: any[]) { try { logger.info(format(...msg)); From 37ccfd7b37476a8921fa1f7ba0a69dc86f988cf4 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 11:55:29 -0700 Subject: [PATCH 4/6] add some logging about log rotation and pruning... --- emain/log.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/emain/log.ts b/emain/log.ts index 15f9d456a6..0202157f5c 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -27,9 +27,9 @@ function findHighestLogNumber(logsDir: string): number { return maxNum; } -function pruneOldLogs(logsDir: string) { +function pruneOldLogs(logsDir: string): string[] { if (!fs.existsSync(logsDir)) { - return; + return []; } const files = fs.readdirSync(logsDir); @@ -43,7 +43,7 @@ function pruneOldLogs(logsDir: string) { } if (logFiles.length <= 5) { - return; + return []; } logFiles.sort((a, b) => b.num - a.num); @@ -52,9 +52,11 @@ function pruneOldLogs(logsDir: string) { for (const logFile of toDelete) { fs.unlinkSync(path.join(logsDir, logFile.name)); } + + return toDelete.map((f) => f.name); } -function rotateLogIfNeeded() { +function rotateLogIfNeeded(): string | null { const waveDataDir = getWaveDataDir(); const logFile = path.join(waveDataDir, "waveapp.log"); const logsDir = path.join(waveDataDir, "logs"); @@ -64,7 +66,7 @@ function rotateLogIfNeeded() { } if (!fs.existsSync(logFile)) { - return; + return null; } const stats = fs.statSync(logFile); @@ -72,14 +74,18 @@ function rotateLogIfNeeded() { const nextNum = findHighestLogNumber(logsDir) + 1; const rotatedPath = path.join(logsDir, `waveapp.${nextNum}.log`); fs.renameSync(logFile, rotatedPath); + return rotatedPath; } + return null; } let logRotateError: any = null; +let rotatedPath: string | null = null; +let prunedFiles: string[] = []; try { - rotateLogIfNeeded(); + rotatedPath = rotateLogIfNeeded(); const logsDir = path.join(getWaveDataDir(), "logs"); - pruneOldLogs(logsDir); + prunedFiles = pruneOldLogs(logsDir); } catch (e) { logRotateError = e; } @@ -102,6 +108,12 @@ const logger = winston.createLogger(loggerConfig); if (logRotateError != null) { logger.error("error rotating/pruning logs (non-fatal):", logRotateError); } +if (rotatedPath != null) { + logger.info("rotated old log file to:", rotatedPath); +} +if (prunedFiles.length > 0) { + logger.info("pruned old log files:", prunedFiles.join(", ")); +} function log(...msg: any[]) { try { logger.info(format(...msg)); From 9270abd71b82b489ae1576e0aa1188011022e401 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 18:23:52 -0700 Subject: [PATCH 5/6] fix logging --- emain/log.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/emain/log.ts b/emain/log.ts index 0202157f5c..cf0da0aa46 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -105,15 +105,7 @@ const loggerConfig = { transports: loggerTransports, }; const logger = winston.createLogger(loggerConfig); -if (logRotateError != null) { - logger.error("error rotating/pruning logs (non-fatal):", logRotateError); -} -if (rotatedPath != null) { - logger.info("rotated old log file to:", rotatedPath); -} -if (prunedFiles.length > 0) { - logger.info("pruned old log files:", prunedFiles.join(", ")); -} + function log(...msg: any[]) { try { logger.info(format(...msg)); @@ -122,4 +114,14 @@ function log(...msg: any[]) { } } +if (logRotateError != null) { + log("error rotating/pruning logs (non-fatal):", logRotateError); +} +if (rotatedPath != null) { + log("rotated old log file to:", rotatedPath); +} +if (prunedFiles.length > 0) { + log("pruned old log files:", prunedFiles.join(", ")); +} + export { log }; From c13110208f1496964dd1e22d7021c02e8aa89ed5 Mon Sep 17 00:00:00 2001 From: sawka Date: Tue, 14 Oct 2025 18:27:01 -0700 Subject: [PATCH 6/6] try/catch pruning, and the log out first error --- emain/log.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/emain/log.ts b/emain/log.ts index cf0da0aa46..34fada4b36 100644 --- a/emain/log.ts +++ b/emain/log.ts @@ -27,9 +27,9 @@ function findHighestLogNumber(logsDir: string): number { return maxNum; } -function pruneOldLogs(logsDir: string): string[] { +function pruneOldLogs(logsDir: string): { pruned: string[]; error: any } { if (!fs.existsSync(logsDir)) { - return []; + return { pruned: [], error: null }; } const files = fs.readdirSync(logsDir); @@ -43,17 +43,26 @@ function pruneOldLogs(logsDir: string): string[] { } if (logFiles.length <= 5) { - return []; + return { pruned: [], error: null }; } logFiles.sort((a, b) => b.num - a.num); const toDelete = logFiles.slice(5); + const pruned: string[] = []; + let firstError: any = null; for (const logFile of toDelete) { - fs.unlinkSync(path.join(logsDir, logFile.name)); + try { + fs.unlinkSync(path.join(logsDir, logFile.name)); + pruned.push(logFile.name); + } catch (e) { + if (firstError == null) { + firstError = e; + } + } } - return toDelete.map((f) => f.name); + return { pruned, error: firstError }; } function rotateLogIfNeeded(): string | null { @@ -82,10 +91,13 @@ function rotateLogIfNeeded(): string | null { let logRotateError: any = null; let rotatedPath: string | null = null; let prunedFiles: string[] = []; +let pruneError: any = null; try { rotatedPath = rotateLogIfNeeded(); const logsDir = path.join(getWaveDataDir(), "logs"); - prunedFiles = pruneOldLogs(logsDir); + const pruneResult = pruneOldLogs(logsDir); + prunedFiles = pruneResult.pruned; + pruneError = pruneResult.error; } catch (e) { logRotateError = e; } @@ -123,5 +135,8 @@ if (rotatedPath != null) { if (prunedFiles.length > 0) { log("pruned old log files:", prunedFiles.join(", ")); } +if (pruneError != null) { + log("error pruning some log files (non-fatal):", pruneError); +} export { log };