11// Centralized logging for Vibora backend
22// Outputs JSON lines to stdout and vibora.log file
33
4- import { appendFileSync } from 'fs'
4+ import { appendFileSync , statSync , renameSync , unlinkSync } from 'fs'
55import { join } from 'path'
6+
7+ // Log rotation settings
8+ const MAX_LOG_SIZE = 10 * 1024 * 1024 // 10MB
9+ const MAX_LOG_BACKUPS = 2 // Keep .1 and .2 backups
610import { type LogEntry , type LogLevel , type Logger , LOG_LEVELS , formatLogEntry } from '../../shared/logger'
711import { getViboraDir , ensureViboraDir } from './settings'
812
@@ -33,6 +37,34 @@ function getLogFile(): string | null {
3337 }
3438}
3539
40+ // Rotate log file if it exceeds MAX_LOG_SIZE
41+ function rotateLogIfNeeded ( logFile : string ) : void {
42+ try {
43+ const stats = statSync ( logFile )
44+ if ( stats . size < MAX_LOG_SIZE ) return
45+
46+ // Rotate: delete oldest, shift others, rename current
47+ for ( let i = MAX_LOG_BACKUPS ; i >= 1 ; i -- ) {
48+ const older = `${ logFile } .${ i } `
49+ const newer = i === 1 ? logFile : `${ logFile } .${ i - 1 } `
50+ try {
51+ if ( i === MAX_LOG_BACKUPS ) {
52+ unlinkSync ( older )
53+ }
54+ } catch {
55+ // File doesn't exist, that's fine
56+ }
57+ try {
58+ renameSync ( newer , older )
59+ } catch {
60+ // Source doesn't exist, that's fine
61+ }
62+ }
63+ } catch {
64+ // File doesn't exist yet or other error, skip rotation
65+ }
66+ }
67+
3668/**
3769 * Core logging function - writes a log entry to stdout and vibora.log
3870 * Used by both the Logger class and /api/logs endpoint
@@ -43,10 +75,11 @@ export function writeEntry(entry: LogEntry): void {
4375 // Write to stdout (captured by daemon/nohup)
4476 console . log ( line )
4577
46- // Write to log file
78+ // Write to log file (with rotation)
4779 const logFile = getLogFile ( )
4880 if ( logFile ) {
4981 try {
82+ rotateLogIfNeeded ( logFile )
5083 appendFileSync ( logFile , line + '\n' )
5184 } catch {
5285 // Ignore file write errors
0 commit comments