From 1546973475b810a3ff0395532f6384a5fa1fbb34 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Thu, 7 May 2026 07:29:24 -0500 Subject: [PATCH] Rework BufferLogAppender buffer sizing logic to be more performant in the face of heavy throughput. --- src/buffer-log-appender.ts | 64 +++++++++++++++++++++++++++++++++----- src/util.ts | 11 +++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/buffer-log-appender.ts b/src/buffer-log-appender.ts index 0313db0..4accd07 100644 --- a/src/buffer-log-appender.ts +++ b/src/buffer-log-appender.ts @@ -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 = [] } } diff --git a/src/util.ts b/src/util.ts index e9cf149..eea65c3 100644 --- a/src/util.ts +++ b/src/util.ts @@ -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 +}