mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 17:08:27 +00:00
fix(logging): cap file logs with configurable maxFileBytes
Co-authored-by: Xinhua Gu <562450+xinhuagu@users.noreply.github.com>
This commit is contained in:
@@ -16,12 +16,14 @@ export const DEFAULT_LOG_FILE = path.join(DEFAULT_LOG_DIR, "openclaw.log"); // l
|
||||
const LOG_PREFIX = "openclaw";
|
||||
const LOG_SUFFIX = ".log";
|
||||
const MAX_LOG_AGE_MS = 24 * 60 * 60 * 1000; // 24h
|
||||
const DEFAULT_MAX_LOG_FILE_BYTES = 500 * 1024 * 1024; // 500 MB
|
||||
|
||||
const requireConfig = resolveNodeRequireFromMeta(import.meta.url);
|
||||
|
||||
export type LoggerSettings = {
|
||||
level?: LogLevel;
|
||||
file?: string;
|
||||
maxFileBytes?: number;
|
||||
consoleLevel?: LogLevel;
|
||||
consoleStyle?: ConsoleStyle;
|
||||
};
|
||||
@@ -31,6 +33,7 @@ type LogObj = { date?: Date } & Record<string, unknown>;
|
||||
type ResolvedSettings = {
|
||||
level: LogLevel;
|
||||
file: string;
|
||||
maxFileBytes: number;
|
||||
};
|
||||
export type LoggerResolvedSettings = ResolvedSettings;
|
||||
export type LogTransportRecord = Record<string, unknown>;
|
||||
@@ -72,14 +75,15 @@ function resolveSettings(): ResolvedSettings {
|
||||
const envLevel = resolveEnvLogLevelOverride();
|
||||
const level = envLevel ?? fromConfig;
|
||||
const file = cfg?.file ?? defaultRollingPathForToday();
|
||||
return { level, file };
|
||||
const maxFileBytes = resolveMaxLogFileBytes(cfg?.maxFileBytes);
|
||||
return { level, file, maxFileBytes };
|
||||
}
|
||||
|
||||
function settingsChanged(a: ResolvedSettings | null, b: ResolvedSettings) {
|
||||
if (!a) {
|
||||
return true;
|
||||
}
|
||||
return a.level !== b.level || a.file !== b.file;
|
||||
return a.level !== b.level || a.file !== b.file || a.maxFileBytes !== b.maxFileBytes;
|
||||
}
|
||||
|
||||
export function isFileLogLevelEnabled(level: LogLevel): boolean {
|
||||
@@ -99,6 +103,8 @@ function buildLogger(settings: ResolvedSettings): TsLogger<LogObj> {
|
||||
if (isRollingPath(settings.file)) {
|
||||
pruneOldRollingLogs(path.dirname(settings.file));
|
||||
}
|
||||
let currentFileBytes = getCurrentLogFileBytes(settings.file);
|
||||
let warnedAboutSizeCap = false;
|
||||
const logger = new TsLogger<LogObj>({
|
||||
name: "openclaw",
|
||||
minLevel: levelToMinLevel(settings.level),
|
||||
@@ -109,7 +115,28 @@ function buildLogger(settings: ResolvedSettings): TsLogger<LogObj> {
|
||||
try {
|
||||
const time = logObj.date?.toISOString?.() ?? new Date().toISOString();
|
||||
const line = JSON.stringify({ ...logObj, time });
|
||||
fs.appendFileSync(settings.file, `${line}\n`, { encoding: "utf8" });
|
||||
const payload = `${line}\n`;
|
||||
const payloadBytes = Buffer.byteLength(payload, "utf8");
|
||||
const nextBytes = currentFileBytes + payloadBytes;
|
||||
if (nextBytes > settings.maxFileBytes) {
|
||||
if (!warnedAboutSizeCap) {
|
||||
warnedAboutSizeCap = true;
|
||||
const warningLine = JSON.stringify({
|
||||
time: new Date().toISOString(),
|
||||
level: "warn",
|
||||
subsystem: "logging",
|
||||
message: `log file size cap reached; suppressing writes file=${settings.file} maxFileBytes=${settings.maxFileBytes}`,
|
||||
});
|
||||
appendLogLine(settings.file, `${warningLine}\n`);
|
||||
process.stderr.write(
|
||||
`[openclaw] log file size cap reached; suppressing writes file=${settings.file} maxFileBytes=${settings.maxFileBytes}\n`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (appendLogLine(settings.file, payload)) {
|
||||
currentFileBytes = nextBytes;
|
||||
}
|
||||
} catch {
|
||||
// never block on logging failures
|
||||
}
|
||||
@@ -121,6 +148,30 @@ function buildLogger(settings: ResolvedSettings): TsLogger<LogObj> {
|
||||
return logger;
|
||||
}
|
||||
|
||||
function resolveMaxLogFileBytes(raw: unknown): number {
|
||||
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
|
||||
return Math.floor(raw);
|
||||
}
|
||||
return DEFAULT_MAX_LOG_FILE_BYTES;
|
||||
}
|
||||
|
||||
function getCurrentLogFileBytes(file: string): number {
|
||||
try {
|
||||
return fs.statSync(file).size;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function appendLogLine(file: string, line: string): boolean {
|
||||
try {
|
||||
fs.appendFileSync(file, line, { encoding: "utf8" });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLogger(): TsLogger<LogObj> {
|
||||
const settings = resolveSettings();
|
||||
const cachedLogger = loggingState.cachedLogger as TsLogger<LogObj> | null;
|
||||
|
||||
Reference in New Issue
Block a user