enh: live log size and rotation controls

This commit is contained in:
Ben Allfree 2024-11-28 00:42:57 -08:00
parent fec6d4fa59
commit 993faed077

View File

@ -1,4 +1,6 @@
import { appendFile } from 'fs/promises' import Bottleneck from 'bottleneck'
import { existsSync } from 'fs'
import { appendFile, cp, stat, truncate } from 'fs/promises'
import { Tail } from 'tail' import { Tail } from 'tail'
import { import {
ensureInstanceDirectoryStructure, ensureInstanceDirectoryStructure,
@ -25,6 +27,29 @@ export type LogEntry = {
time: string time: string
} }
const MultiChannelLimiter = () => {
const channels = new Map<string, Bottleneck>()
setInterval(() => {
for (const [channel, limiter] of channels.entries()) {
if (limiter.empty()) {
console.log(`Deleting empty limiter for ${channel}`)
channels.delete(channel)
}
}
}, 1000)
return {
schedule(channel: string, fn: () => Promise<void>) {
if (!channels.has(channel)) {
channels.set(channel, new Bottleneck({ maxConcurrent: 1 }))
}
return channels.get(channel)!.schedule(fn)!
},
}
}
const limiter = MultiChannelLimiter()
export function InstanceLogWriter( export function InstanceLogWriter(
instanceId: string, instanceId: string,
volume: string, volume: string,
@ -42,8 +67,24 @@ export function InstanceLogWriter(
`${target}.log`, `${target}.log`,
) )
const appendLogEntry = (msg: string, stream: 'stdout' | 'stderr') => { const appendLogEntry = async (msg: string, stream: 'stdout' | 'stderr') =>
appendFile( limiter.schedule(logFile, async () => {
try {
if (existsSync(logFile)) {
// Check file size
const stats = await stat(logFile)
const MAX_SIZE = 1024 // 10MB in bytes
if (stats.size > MAX_SIZE) {
await cp(logFile, `${logFile}.1`, {
force: true,
})
await truncate(logFile, 0)
}
}
dbg(msg)
await appendFile(
logFile, logFile,
stringify({ stringify({
message: msg, message: msg,
@ -51,16 +92,17 @@ export function InstanceLogWriter(
time: new Date().toISOString(), time: new Date().toISOString(),
}) + '\n', }) + '\n',
) )
} catch (e) {
error(`Failed to write log entry: ${e}`)
} }
})
const api = { const api = {
info: (msg: string) => { info: (msg: string) => {
dbg(msg) return appendLogEntry(msg, 'stdout')
appendLogEntry(msg, 'stdout')
}, },
error: (msg: string) => { error: (msg: string) => {
dbg(msg) return appendLogEntry(msg, 'stderr')
appendLogEntry(msg, 'stderr')
}, },
} }
@ -110,7 +152,10 @@ export function InstanceLogReader(
unsub = () => tail.close() unsub = () => tail.close()
} catch (e) { } catch (e) {
if ((e as any).code === 'ENOENT') {
} else {
warn(e) warn(e)
}
tid = setTimeout(check, 1000) tid = setTimeout(check, 1000)
} }
} }