Rework BufferLogAppender buffer sizing logic to be more performant in the face of heavy throughput.

This commit is contained in:
2026-05-07 07:29:24 -05:00
parent 5e167b7fb4
commit 1546973475
2 changed files with 67 additions and 8 deletions
+56 -8
View File
@@ -1,13 +1,44 @@
import type { LogAppender } from './log-appender'
import { LogLevel, type LogMessage } from './log-message'
import { clamp } from './util'
export interface BufferSizeBounds {
target: number
max: number
}
export class BufferLogAppender implements LogAppender {
public threshold: LogLevel
public buffer: LogMessage[]
public bufferMax?: number
constructor(buffer?: LogMessage[], threshold?: LogLevel) {
this.buffer = buffer ?? []
private _size?: BufferSizeBounds
public get size() {
return this._size === undefined ? undefined : { ...this._size }
}
public set size(s: {target: number, max?: number} | undefined) {
if (s === undefined) {
this._size = undefined
} else {
const target = clamp(s.target, { min: 1 })
this._size = {
target,
max: s.max === undefined
? clamp(target * 1.2, { min: target + 1 })
: clamp(s.max, { min: target + 1 })
}
}
}
constructor(threshold?: LogLevel, size?: {target: number, max?: number}) {
if (size !== undefined && typeof size === 'object') {
this.size = size
} else {
this._size = { target: 1000, max: 1200 }
}
this.buffer = []
this.threshold = threshold ?? LogLevel.ALL
}
@@ -16,15 +47,32 @@ export class BufferLogAppender implements LogAppender {
this.buffer.push(msg)
if (this.bufferMax !== undefined) {
const max = Math.max(0, this.bufferMax)
while (this.buffer.length > max) {
this.buffer.shift()
if (this._size !== undefined) {
if (this._size.max <= this._size.target) {
// This should be impossible, so if it happens, I want to know.
const oldSize = this.size
this.size = { target: this._size.target }
this.buffer.push({
scope: '@jdbernard/js-logging/buffer-log-appender.ts',
level: LogLevel.ERROR,
msg: {
msg: 'BufferLogAppender misconfigured: max size was not greater ' +
'than target size. Reconfiguring.',
oldSize,
newSize: this.size
},
ts: new Date()
})
}
if (this.buffer.length > this._size.max) {
this.buffer = this.buffer.slice(-this._size.target)
}
}
}
public clearBuffer(): void {
this.buffer.length = 0
this.buffer = []
}
}
+11
View File
@@ -10,3 +10,14 @@ export function omit(
}
return result
}
export function clamp(
val: number,
bounds?: { min?: number, max?: number},
allowFloats?: boolean ): number {
let clamped = val
if (!allowFloats) clamped = Math.floor(clamped)
if (bounds?.min !== undefined) clamped = Math.max(bounds?.min, clamped)
if (bounds?.max !== undefined) clamped = Math.min(bounds?.max, clamped)
return clamped
}