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()