This commit is contained in:
2026-02-12 22:10:13 +01:00
parent 4d54574cef
commit a13bafdb1b

View File

@@ -256,13 +256,13 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable
const int maxBatch = 5000; const int maxBatch = 5000;
int batchCount = 0; int batchCount = 0;
// Rent a buffer // Start with a reasonably sized buffer from the pool
byte[] combined = ArrayPool<byte>.Shared.Rent(BufferSize); int estimatedSize = 1024 * 64; // 64KB starting buffer
byte[] combined = ArrayPool<byte>.Shared.Rent(estimatedSize);
int pos = 0; int pos = 0;
while (queue.TryDequeue(out var msg) && batchCount < maxBatch) while (queue.TryDequeue(out var msg) && batchCount < maxBatch)
{ {
// Rotate file if date changed
var msgDate = msg.Timestamp.UtcDateTime.Date; var msgDate = msg.Timestamp.UtcDateTime.Date;
if (state.Date != msgDate) if (state.Date != msgDate)
{ {
@@ -270,66 +270,68 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable
RotateByDate(state, msgDate, string.Empty); RotateByDate(state, msgDate, string.Empty);
} }
var msgText = BuildMessage(msg); var messageString = BuildMessage(msg);
int byteCount = Utf8.GetByteCount(msgText); int requiredLength = Utf8.GetByteCount(messageString);
// Flush buffer if message won't fit // Grow buffer if needed
if (pos + byteCount > combined.Length) if (pos + requiredLength > combined.Length)
{ {
await FlushBufferAsync(state).ConfigureAwait(false); int newSize = Math.Max(combined.Length * 2, pos + requiredLength);
pos = 0; byte[] newBuffer = ArrayPool<byte>.Shared.Rent(newSize);
Array.Copy(combined, 0, newBuffer, 0, pos);
ArrayPool<byte>.Shared.Return(combined);
combined = newBuffer;
} }
Utf8.GetBytes(msgText, 0, msgText.Length, combined, pos); // Write directly into combined buffer
pos += byteCount; pos += Utf8.GetBytes(messageString, 0, messageString.Length, combined, pos);
batchCount++; batchCount++;
} }
if (pos == 0) if (pos == 0)
{ {
ArrayPool<byte>.Shared.Return(combined); ArrayPool<byte>.Shared.Return(combined);
return; return; // nothing to write
} }
// If encryption is enabled, encrypt into a new array
byte[] dataToWrite; byte[] dataToWrite;
if (_isEncryptionEnabled) if (_isEncryptionEnabled)
{ {
// Encrypt creates a new array, old buffer cleared immediately
dataToWrite = Encrypt(combined.AsSpan(0, pos).ToArray()); dataToWrite = Encrypt(combined.AsSpan(0, pos).ToArray());
Array.Clear(combined, 0, pos);
} }
else else
{ {
// No encryption: just use the pooled buffer slice directly
dataToWrite = combined; dataToWrite = combined;
} }
// Flush buffer if needed // Flush buffer if needed
if (state.BufferPosition + dataToWrite.Length > BufferSize) if (state.BufferPosition + pos > BufferSize)
{ {
await FlushBufferAsync(state).ConfigureAwait(false); await FlushBufferAsync(state).ConfigureAwait(false);
} }
// Rollover if file exceeds max size // Rollover if file exceeds max size
if (_maxFileSize > 0 && state.Size + dataToWrite.Length > _maxFileSize) if (_maxFileSize > 0 && state.Size + pos > _maxFileSize)
{ {
await FlushBufferAsync(state).ConfigureAwait(false); await FlushBufferAsync(state).ConfigureAwait(false);
RollOverAndCompressOldest(state, string.Empty); RollOverAndCompressOldest(state, string.Empty);
} }
// Copy into buffer // Copy directly into the file buffer
Array.Copy(dataToWrite, 0, state.Buffer, state.BufferPosition, dataToWrite.Length); Array.Copy(dataToWrite, 0, state.Buffer, state.BufferPosition, pos);
state.BufferPosition += dataToWrite.Length; state.BufferPosition += pos;
state.Size += dataToWrite.Length; state.Size += pos;
// Clear sensitive data
Array.Clear(dataToWrite, 0, pos);
// Return buffer if unencrypted
if (!_isEncryptionEnabled) if (!_isEncryptionEnabled)
{ {
// return pooled buffer if not encrypted
ArrayPool<byte>.Shared.Return(dataToWrite); ArrayPool<byte>.Shared.Return(dataToWrite);
} }
// Clear encrypted array immediately
Array.Clear(dataToWrite, 0, dataToWrite.Length);
} }
private async Task FlushBufferAsync(FileState state, CancellationToken token = default) private async Task FlushBufferAsync(FileState state, CancellationToken token = default)