Skip to content

Commit 05e5c66

Browse files
authored
Fix caching of time strings in AbsoluteTimeDateFormatter #247
Fix caching of time strings in AbsoluteTimeDateFormatter #247
2 parents 50738b0 + c685dd9 commit 05e5c66

File tree

2 files changed

+41
-30
lines changed

2 files changed

+41
-30
lines changed

src/log4net.Tests/Layout/PatternLayoutTest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,27 @@ public void TestExceptionPattern()
342342

343343
stringAppender.Reset();
344344
}
345+
346+
[Test]
347+
public void ConvertMultipleDatePatternsTest()
348+
{
349+
StringAppender stringAppender = new()
350+
{
351+
Layout = NewPatternLayout("%utcdate{ABSOLUTE} - %utcdate{ISO8601}")
352+
};
353+
354+
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
355+
BasicConfigurator.Configure(rep, stringAppender);
356+
357+
ILog logger = LogManager.GetLogger(rep.Name, nameof(ConvertMultipleDatePatternsTest));
358+
359+
logger.Logger.Log(new(new() { TimeStampUtc = new(2025, 02, 10, 13, 01, 02, 123, DateTimeKind.Utc), Message = "test", Level = Level.Info }));
360+
Assert.That(stringAppender.GetString(), Is.EqualTo("13:01:02,123 - 2025-02-10 13:01:02,123"));
361+
stringAppender.Reset();
362+
logger.Logger.Log(new(new() { TimeStampUtc = new(2025, 02, 10, 13, 01, 03, 123, DateTimeKind.Utc), Message = "test", Level = Level.Info }));
363+
Assert.That(stringAppender.GetString(), Is.EqualTo("13:01:03,123 - 2025-02-10 13:01:03,123"));
364+
}
365+
345366
#if NET8_0_OR_GREATER
346367
[Test]
347368
public void ConvertMicrosecondsPatternTest()

src/log4net/DateFormatter/AbsoluteTimeDateFormatter.cs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ namespace log4net.DateFormatter;
3737
/// <author>Gert Driesen</author>
3838
public class AbsoluteTimeDateFormatter : IDateFormatter
3939
{
40+
private readonly record struct TimeString(long TimeToTheSecond, string AsString);
41+
4042
/// <summary>
4143
/// Renders the date into a string. Format is <c>"HH:mm:ss"</c>.
4244
/// </summary>
@@ -95,24 +97,16 @@ protected virtual void FormatDateWithoutMillis(DateTime dateToFormat, StringBuil
9597
/// </remarks>
9698
public virtual void FormatDate(DateTime dateToFormat, TextWriter writer)
9799
{
98-
string timeString = _sLastTimeStrings.AddOrUpdate(GetType(),
99-
_ => BuildTimeString(),
100-
(_, existing) =>
101-
{
102-
// Calculate the current time precise only to the second
103-
long currentTimeToTheSecond = dateToFormat.Ticks - (dateToFormat.Ticks % TimeSpan.TicksPerSecond);
104-
105-
// Compare this time with the stored last time
106-
// If we are in the same second then append
107-
// the previously calculated time string
108-
if (_sLastTimeToTheSecond == currentTimeToTheSecond)
109-
{
110-
return existing;
111-
}
112-
_sLastTimeToTheSecond = currentTimeToTheSecond;
113-
return BuildTimeString();
114-
});
115-
writer.EnsureNotNull().Write(timeString);
100+
// Calculate the current time precise only to the second
101+
long timeToTheSecond = dateToFormat.Ticks - (dateToFormat.Ticks % TimeSpan.TicksPerSecond);
102+
// Compare this time with the stored last time
103+
// If we are in the same second then append the previously calculated time string
104+
if (!_sLastTimeStrings.TryGetValue(GetType(), out TimeString timeString) || timeString.TimeToTheSecond != timeToTheSecond)
105+
{
106+
timeString = BuildTimeString(dateToFormat, timeToTheSecond);
107+
_sLastTimeStrings[GetType()] = timeString;
108+
}
109+
writer.EnsureNotNull().Write(timeString.AsString);
116110

117111
// Append the current millisecond info
118112
writer.Write(',');
@@ -127,12 +121,13 @@ public virtual void FormatDate(DateTime dateToFormat, TextWriter writer)
127121
}
128122
writer.Write(millis);
129123

130-
string BuildTimeString()
131-
{
132-
var sb = new StringBuilder();
133-
FormatDateWithoutMillis(dateToFormat, sb);
134-
return sb.ToString();
135-
}
124+
}
125+
126+
private TimeString BuildTimeString(DateTime dateToFormat, long timeToTheSecond)
127+
{
128+
StringBuilder sb = new();
129+
FormatDateWithoutMillis(dateToFormat, sb);
130+
return new(timeToTheSecond, sb.ToString());
136131
}
137132

138133
/// <summary>
@@ -150,14 +145,9 @@ string BuildTimeString()
150145
/// </summary>
151146
public const string Iso8601TimeDateFormat = "ISO8601";
152147

153-
/// <summary>
154-
/// Last stored time with precision up to the second.
155-
/// </summary>
156-
private static long _sLastTimeToTheSecond;
157-
158148
/// <summary>
159149
/// Last stored time with precision up to the second, formatted
160150
/// as a string.
161151
/// </summary>
162-
private static readonly ConcurrentDictionary<Type, string> _sLastTimeStrings = new();
152+
private static readonly ConcurrentDictionary<Type, TimeString> _sLastTimeStrings = new();
163153
}

0 commit comments

Comments
 (0)