diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index 30763dd..cee49df 100644 --- a/EonaCat.Logger/EonaCat.Logger.csproj +++ b/EonaCat.Logger/EonaCat.Logger.csproj @@ -13,8 +13,8 @@ EonaCat (Jeroen Saey) EonaCat;Logger;EonaCatLogger;Log;Writer;Jeroen;Saey - 1.7.5 - 1.7.5 + 1.7.6 + 1.7.6 README.md True LICENSE @@ -25,7 +25,7 @@ - 1.7.5+{chash:10}.{c:ymd} + 1.7.6+{chash:10}.{c:ymd} true true v[0-9]* diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index 5455694..1dd4d46 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -42,6 +42,9 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable private int _position; private long _size; + [ThreadStatic] + private static StringBuilder? _cachedStringBuilder; + public string LogFile => _filePath; private volatile bool _running = true; private readonly int _maxRolloverFiles; @@ -131,6 +134,150 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable FlushFinal(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteMessage(LogMessage msg) + { + var sb = AcquireStringBuilder(); + + if (IncludeCorrelationId) + { + var ctx = _context.GetAll(); + var tags = msg.Tags; + + if (ctx.Count > 0 || (tags?.Length ?? 0) > 0) + { + sb.Append(" ["); + + foreach (var kv in ctx) + { + sb.Append(kv.Key).Append('=').Append(kv.Value).Append(' '); + } + + if (tags != null) + { + for (int i = 0; i < tags.Length; i++) + { + sb.Append("tag=").Append(tags[i]).Append(' '); + } + } + + // Trim trailing space + if (sb[sb.Length -1] == ' ') + { + sb.Length--; + } + + sb.Append(']'); + } + + // Ensure correlation id exists + var correlationId = _context.Get("CorrelationId"); + if (correlationId == null) + { + correlationId = Guid.NewGuid().ToString(); + _context.Set("CorrelationId", correlationId); + } + } + + sb.Append(' ') + .Append(msg.Message) + .AppendLine(); + + int charCount = sb.Length; + int maxBytes = Utf8.GetMaxByteCount(charCount); + byte[] rented = ArrayPool.Shared.Rent(maxBytes); + + int byteCount = Utf8.GetBytes(sb.ToString(), 0, charCount, rented, 0); + + byte[] data = rented; + int length = byteCount; + + if (_encryptionEnabled) + { + data = _encryptor.TransformFinalBlock(rented, 0, byteCount); + length = data.Length; + ArrayPool.Shared.Return(rented, true); + rented = null; + } + + WriteToBuffer(data, length); + + if (_maxFileSize > 0 && _size >= _maxFileSize) + { + RollFile(); + } + + if (rented != null) + { + ArrayPool.Shared.Return(rented, true); + } + + ReleaseStringBuilder(sb); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteToBuffer(byte[] data, int length) + { + if (length > BufferSize) + { + if (_position > 0) + { + FlushInternal(); + } + + WriteDirect(data, length); + return; + } + + if (_position + length > BufferSize) + { + FlushInternal(); + } + + Buffer.BlockCopy(data, 0, _buffer, _position, length); + _position += length; + _size += length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static StringBuilder AcquireStringBuilder() + { + var sb = _cachedStringBuilder; + if (sb == null) + { + sb = new StringBuilder(256); + _cachedStringBuilder = sb; + } + else + { + sb.Clear(); + } + + return sb; + } + + private const int MaxBuilderCapacity = 8 * 1024; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ReleaseStringBuilder(StringBuilder sb) + { + if (sb.Capacity > MaxBuilderCapacity) + { + _cachedStringBuilder = new StringBuilder(256); + } + } + + private void WriteDirect(byte[] data, int length) + { + _stream.Write(data, 0, length); + _size += length; + + if (_maxFileSize > 0 && _size >= _maxFileSize) + { + RollFile(); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FlushIfNeeded() { @@ -157,90 +304,6 @@ public sealed class FileLoggerProvider : BatchingLoggerProvider, IDisposable _stream.Flush(true); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMessage(LogMessage msg) - { - StringBuilder sb = new StringBuilder(); - - if (IncludeCorrelationId) - { - var ctx = _context.GetAll(); - if (ctx.Count > 0 || (msg.Tags?.Length ?? 0) > 0) - { - sb.Append(" ["); - - foreach (var kv in ctx) - { - sb.Append(kv.Key); - sb.Append('='); - sb.Append(kv.Value); - sb.Append(' '); - } - - if (msg.Tags != null) - { - for (int i = 0; i < msg.Tags.Length; i++) - { - sb.Append("tag="); - sb.Append(msg.Tags[i]); - sb.Append(' '); - } - } - - if (sb[sb.Length - 1] == ' ') - { - sb.Length--; - } - - sb.Append(']'); - } - } - - string correlationId = null; - if (IncludeCorrelationId) - { - correlationId = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString(); - _context.Set("CorrelationId", correlationId); - } - - string text = sb.ToString() + ' ' + msg.Message + Environment.NewLine; - - int max = Utf8.GetMaxByteCount(text.Length); - byte[] temp = ArrayPool.Shared.Rent(max); - - int bytes = Utf8.GetBytes(text, 0, text.Length, temp, 0); - - byte[] final = temp; - int length = bytes; - - if (_encryptionEnabled) - { - final = _encryptor.TransformFinalBlock(temp, 0, bytes); - length = final.Length; - ArrayPool.Shared.Return(temp, true); - temp = null; - } - - if (_position + length > BufferSize) - { - FlushInternal(); - } - - Buffer.BlockCopy(final, 0, _buffer, _position, length); - _position += length; - _size += length; - - if (_maxFileSize > 0 && _size >= _maxFileSize) - { - RollFile(); - } - - if (temp != null) - { - ArrayPool.Shared.Return(temp, true); - } - } - private readonly object _rollLock = new(); private void RollFile()