mirror of
https://github.com/pockethost/pockethost.git
synced 2025-11-24 06:25:48 +00:00
enh: live log size and rotation controls
This commit is contained in:
parent
fec6d4fa59
commit
993faed077
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user