Skip to content

Commit 3b7ec69

Browse files
committed
Fix segment handling when formatter retries
1 parent 9ce8e14 commit 3b7ec69

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
2+
// Licensed under the BSD-Clause 2 license.
3+
// See license.txt file in the project root for full license information.
4+
5+
using XenoAtom.Logging.Helpers;
6+
7+
namespace XenoAtom.Logging.Tests;
8+
9+
[TestClass]
10+
public class LogFormatterBufferTests
11+
{
12+
[TestInitialize]
13+
public void Initialize()
14+
{
15+
LogManager.Shutdown();
16+
}
17+
18+
[TestCleanup]
19+
public void Cleanup()
20+
{
21+
LogManager.Shutdown();
22+
}
23+
24+
[TestMethod]
25+
public void LogFormatterBuffer_ResetsSegmentsBetweenRetries()
26+
{
27+
var formatter = new FailOnceSegmentingFormatter();
28+
var writer = new BufferCaptureWriter(formatter);
29+
30+
var config = new LogManagerConfig
31+
{
32+
RootLogger =
33+
{
34+
MinimumLevel = LogLevel.Trace,
35+
Writers =
36+
{
37+
writer
38+
}
39+
}
40+
};
41+
42+
LogManager.Initialize(config);
43+
var logger = LogManager.GetLogger("Tests.FormatterBuffer.Retry");
44+
logger.Info("hello");
45+
LogManager.Shutdown();
46+
47+
Assert.AreEqual("ok", writer.Text);
48+
Assert.AreEqual(1, writer.Segments.Length);
49+
Assert.AreEqual(LogMessageFormatSegmentKind.Text, writer.Segments[0].Kind);
50+
}
51+
52+
private sealed class BufferCaptureWriter : LogWriter
53+
{
54+
private readonly LogFormatter _formatter;
55+
56+
public BufferCaptureWriter(LogFormatter formatter)
57+
{
58+
_formatter = formatter;
59+
}
60+
61+
public string? Text { get; private set; }
62+
63+
public LogMessageFormatSegment[] Segments { get; private set; } = [];
64+
65+
protected override void Log(LogMessage logMessage)
66+
{
67+
using var formatterBuffer = new LogFormatterBuffer();
68+
var segments = new LogMessageFormatSegments(enabled: true);
69+
try
70+
{
71+
var text = formatterBuffer.Format(logMessage, _formatter, ref segments);
72+
Text = text.ToString();
73+
Segments = segments.AsSpan().ToArray();
74+
}
75+
finally
76+
{
77+
segments.Dispose();
78+
}
79+
}
80+
}
81+
82+
private sealed record FailOnceSegmentingFormatter : LogFormatter
83+
{
84+
private int _tryCount;
85+
86+
public override bool TryFormat(LogMessage logMessage, Span<char> destination, out int charsWritten, ref LogMessageFormatSegments segments)
87+
{
88+
segments.Add(0, 0, LogMessageFormatSegmentKind.Text);
89+
90+
if (_tryCount++ == 0)
91+
{
92+
charsWritten = 0;
93+
return false;
94+
}
95+
96+
if (destination.Length < 2)
97+
{
98+
charsWritten = 0;
99+
return false;
100+
}
101+
102+
destination[0] = 'o';
103+
destination[1] = 'k';
104+
charsWritten = 2;
105+
return true;
106+
}
107+
}
108+
}
109+

src/XenoAtom.Logging/Helpers/LogFormatterBuffer.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,14 @@ public ReadOnlySpan<char> Format(LogMessage logMessage, LogFormatter formatter,
2929
var buffer = _charBuffer;
3030
Span<byte> span = buffer;
3131
int charsWritten;
32-
while (!formatter.TryFormat(logMessage, MemoryMarshal.Cast<byte, char>(span), out charsWritten, ref segments))
32+
while (true)
3333
{
34+
segments.Reset();
35+
if (formatter.TryFormat(logMessage, MemoryMarshal.Cast<byte, char>(span), out charsWritten, ref segments))
36+
{
37+
break;
38+
}
39+
3440
ArrayPool<byte>.Shared.Return(buffer);
3541
buffer = ArrayPool<byte>.Shared.Rent(span.Length * 2);
3642
span = buffer;

0 commit comments

Comments
 (0)