Skip to content

Commit

Permalink
Handle output buffer overflows gracefully
Browse files Browse the repository at this point in the history
  • Loading branch information
ltrzesniewski committed Dec 5, 2018
1 parent 79978ee commit 70c454a
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 15 deletions.
17 changes: 15 additions & 2 deletions src/ZeroLog.Tests/LogManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NFluent;
using NUnit.Framework;
using ZeroLog.Appenders;
using ZeroLog.Config;
using ZeroLog.ConfigResolvers;

namespace ZeroLog.Tests
{
Expand Down Expand Up @@ -257,5 +255,20 @@ public void should_register_all_assembly_enums()
LogManager.RegisterAllEnumsFrom(typeof(ConsoleColor).Assembly);
Check.That(EnumCache.IsRegistered(typeof(ConsoleColor))).IsTrue();
}

[Test]
public void should_truncate_long_lines()
{
var log = LogManager.GetLogger(typeof(LogManagerTests));

var signal = _testAppender.SetMessageCountTarget(1);

var longMessage = new string('.', LogManager.OutputBufferSize + 1);
log.Info().Append(longMessage).Log();

signal.Wait(TimeSpan.FromMilliseconds(100));
var message = _testAppender.LoggedMessages.Single();
Check.That(message).IsEqualTo(new string('.', LogManager.OutputBufferSize - LogManager.Config.TruncatedMessageSuffix.Length) + LogManager.Config.TruncatedMessageSuffix);
}
}
}
63 changes: 50 additions & 13 deletions src/ZeroLog/LogManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Formatting;
using System.Threading;
Expand All @@ -14,8 +15,11 @@ namespace ZeroLog
{
public class LogManager : IInternalLogManager
{
internal const int OutputBufferSize = 16 * 1024;

private static readonly IInternalLogManager _noOpLogManager = new NoopLogManager();
private static IInternalLogManager _logManager = _noOpLogManager;
private static readonly Encoding _encoding = Encoding.UTF8;

private readonly ConcurrentDictionary<string, Log> _loggers = new ConcurrentDictionary<string, Log>();
private readonly ConcurrentQueue<IInternalLogEvent> _queue;
Expand All @@ -26,7 +30,6 @@ public class LogManager : IInternalLogManager
private readonly Thread _writeThread;

private bool _isRunning;
private readonly Encoding _encoding = Encoding.UTF8;
private IAppender[] _appenders = ArrayUtil.Empty<IAppender>();

public static ZeroLogConfig Config { get; } = new ZeroLogConfig();
Expand Down Expand Up @@ -204,7 +207,7 @@ private void WriteThread()

Shutdown();
}
catch
catch
{
// Don't kill the process
}
Expand All @@ -214,8 +217,8 @@ private void WriteThread()
private void WriteToAppenders()
{
var spinWait = new SpinWait();
var stringBuffer = new StringBuffer(16 * 1024);
var destination = new byte[16 * 1024];
var stringBuffer = new StringBuffer(OutputBufferSize);
var destination = new byte[OutputBufferSize];
var flush = false;

while (_isRunning || !_queue.IsEmpty)
Expand Down Expand Up @@ -254,17 +257,18 @@ private bool TryToProcessQueue(StringBuffer stringBuffer, byte[] destination)
if ((logEvent.Appenders?.Length ?? 0) <= 0)
return true;

int bytesWritten;

try
{
FormatLogMessage(stringBuffer, logEvent);
bytesWritten = CopyStringBufferToByteArray(stringBuffer, destination);
}
catch (Exception)
catch
{
FormatErrorMessage(stringBuffer, logEvent);
HandleFormattingError(stringBuffer, logEvent, destination, out bytesWritten);
}

var bytesWritten = CopyStringBufferToByteArray(stringBuffer, destination);

WriteMessageLogToAppenders(destination, logEvent, bytesWritten);
}
finally
Expand All @@ -276,12 +280,24 @@ private bool TryToProcessQueue(StringBuffer stringBuffer, byte[] destination)
return true;
}

private static void FormatErrorMessage(StringBuffer stringBuffer, IInternalLogEvent logEvent)
[MethodImpl(MethodImplOptions.NoInlining)]
private static void HandleFormattingError(StringBuffer stringBuffer, IInternalLogEvent logEvent, byte[] destination, out int bytesWritten)
{
stringBuffer.Clear();
stringBuffer.Append("An error occured during formatting: ");
try
{
stringBuffer.Clear();
stringBuffer.Append("An error occured during formatting: ");

logEvent.WriteToStringBufferUnformatted(stringBuffer);
logEvent.WriteToStringBufferUnformatted(stringBuffer);
bytesWritten = CopyStringBufferToByteArray(stringBuffer, destination);
}
catch (Exception ex)
{
stringBuffer.Clear();
stringBuffer.Append("An error occured during formatting: ");
stringBuffer.Append(ex.Message);
bytesWritten = CopyStringBufferToByteArray(stringBuffer, destination);
}
}

private static void WriteMessageLogToAppenders(byte[] destination, IInternalLogEvent logEvent, int bytesWritten)
Expand All @@ -299,14 +315,35 @@ private static void FormatLogMessage(StringBuffer stringBuffer, IInternalLogEven
logEvent.WriteToStringBuffer(stringBuffer);
}

private unsafe int CopyStringBufferToByteArray(StringBuffer stringBuffer, byte[] destination)
private static unsafe int CopyStringBufferToByteArray(StringBuffer stringBuffer, byte[] destination)
{
fixed (byte* dest = &destination[0])
{
// This works only for ASCII strings, but doing the real check would be expensive, and it's an edge case...
if (stringBuffer.Count > destination.Length)
TruncateMessage(stringBuffer, destination.Length);

return stringBuffer.CopyTo(dest, destination.Length, 0, stringBuffer.Count, _encoding);
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void TruncateMessage(StringBuffer stringBuffer, int maxLength)
{
var suffix = Config.TruncatedMessageSuffix;
var maxMessageLength = maxLength - suffix.Length;

if (maxMessageLength > 0)
{
stringBuffer.Count = maxMessageLength;
stringBuffer.Append(suffix);
}
else
{
stringBuffer.Count = maxLength;
}
}

private void UpdateAppenders()
{
var appenders = _configResolver.GetAllAppenders().ToArray();
Expand Down

0 comments on commit 70c454a

Please sign in to comment.