Skip to content

Commit 01dfbb5

Browse files
authored
MicrosoftConsoleJsonLayout - Skip string-allocation for timestamp-output (#628)
1 parent 7de383b commit 01dfbb5

File tree

1 file changed

+85
-3
lines changed

1 file changed

+85
-3
lines changed

src/NLog.Extensions.Logging/Layouts/MicrosoftConsoleJsonLayout.cs

+85-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text;
45
using NLog.Config;
56
using NLog.Layouts;
67

@@ -20,7 +21,7 @@ public class MicrosoftConsoleJsonLayout : JsonLayout
2021
/// </summary>
2122
public MicrosoftConsoleJsonLayout()
2223
{
23-
Attributes.Add(new JsonAttribute("Timestamp", "${date:format=O:universalTime=true}"));
24+
Attributes.Add(new JsonAttribute("Timestamp", new TimestampLayout()));
2425
Attributes.Add(new JsonAttribute("EventId", Layout.FromMethod(evt => LookupEventId(evt), LayoutRenderOptions.ThreadAgnostic)) { Encode = false });
2526
Attributes.Add(new JsonAttribute("LogLevel", Layout.FromMethod(evt => ConvertLogLevel(evt.Level), LayoutRenderOptions.ThreadAgnostic)));
2627
Attributes.Add(new JsonAttribute("Category", "${logger}"));
@@ -75,7 +76,7 @@ public string TimestampFormat
7576
get
7677
{
7778
var index = LookupNamedAttributeIndex("Timestamp");
78-
return index >= 0 ? ((Attributes[index].Layout as SimpleLayout)?.LayoutRenderers?.FirstOrDefault() as NLog.LayoutRenderers.DateLayoutRenderer)?.Format : null;
79+
return index >= 0 ? (Attributes[index].Layout as TimestampLayout)?.TimestampFormat : null;
7980
}
8081
set
8182
{
@@ -87,7 +88,7 @@ public string TimestampFormat
8788

8889
if (!string.IsNullOrEmpty(value))
8990
{
90-
Attributes.Insert(0, new JsonAttribute("Timestamp", $"${{date:format={value}:universalTime=true}}"));
91+
Attributes.Insert(0, new JsonAttribute("Timestamp", new TimestampLayout() { TimestampFormat = value }));
9192
}
9293
}
9394
}
@@ -150,5 +151,86 @@ private static string ConvertLogLevel(LogLevel logLevel)
150151
else
151152
return nameof(Microsoft.Extensions.Logging.LogLevel.Critical);
152153
}
154+
155+
[ThreadAgnostic]
156+
private sealed class TimestampLayout : Layout
157+
{
158+
public string TimestampFormat
159+
{
160+
get => _timestampFormat;
161+
set
162+
{
163+
if ("o".Equals(value))
164+
value = "O";
165+
_timestampFormat = value;
166+
_timestampFormatString = string.IsNullOrEmpty(value) ? null : $"{{0:{value}}}";
167+
}
168+
}
169+
private string _timestampFormat;
170+
private string _timestampFormatString;
171+
172+
public TimestampLayout()
173+
{
174+
TimestampFormat = "O"; // Round-trip - ISO8601 - yyyy-MM-ddTHH:mm:ss.fffffffZ
175+
}
176+
177+
protected override string GetFormattedMessage(LogEventInfo logEvent)
178+
{
179+
return _timestampFormatString != null ? logEvent.TimeStamp.ToUniversalTime().ToString(TimestampFormat, System.Globalization.CultureInfo.InvariantCulture) : String.Empty;
180+
}
181+
182+
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
183+
{
184+
var utcTimestamp = logEvent.TimeStamp.ToUniversalTime();
185+
if ("O".Equals(TimestampFormat))
186+
{
187+
// yyyy-MM-ddTHH:mm:ss.fffffffZ
188+
Append4DigitsZeroPadded(target, utcTimestamp.Year);
189+
target.Append('-');
190+
Append2DigitsZeroPadded(target, utcTimestamp.Month);
191+
target.Append('-');
192+
Append2DigitsZeroPadded(target, utcTimestamp.Day);
193+
target.Append('T');
194+
Append2DigitsZeroPadded(target, utcTimestamp.Hour);
195+
target.Append(':');
196+
Append2DigitsZeroPadded(target, utcTimestamp.Minute);
197+
target.Append(':');
198+
Append2DigitsZeroPadded(target, utcTimestamp.Second);
199+
target.Append('.');
200+
var ticks = (int)(utcTimestamp.Ticks % 10000000);
201+
Append7DigitsZeroPadded(target, ticks);
202+
target.Append('Z');
203+
}
204+
else if (_timestampFormatString != null)
205+
{
206+
target.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, _timestampFormatString, utcTimestamp);
207+
}
208+
}
209+
210+
internal static void Append2DigitsZeroPadded(StringBuilder builder, int number)
211+
{
212+
builder.Append((char)((number / 10) + '0'));
213+
builder.Append((char)((number % 10) + '0'));
214+
}
215+
216+
internal static void Append4DigitsZeroPadded( StringBuilder builder, int number)
217+
{
218+
builder.Append((char)(((number / 1000) % 10) + '0'));
219+
builder.Append((char)(((number / 100) % 10) + '0'));
220+
builder.Append((char)(((number / 10) % 10) + '0'));
221+
builder.Append((char)((number % 10) + '0'));
222+
}
223+
224+
internal static void Append7DigitsZeroPadded(StringBuilder builder, int number)
225+
{
226+
builder.Append((char)(((number / 1000000) % 10) + '0'));
227+
builder.Append((char)(((number / 100000) % 10) + '0'));
228+
builder.Append((char)(((number / 10000) % 10) + '0'));
229+
builder.Append((char)(((number / 1000) % 10) + '0'));
230+
builder.Append((char)(((number / 100) % 10) + '0'));
231+
builder.Append((char)(((number / 10) % 10) + '0'));
232+
builder.Append((char)((number % 10) + '0'));
233+
}
234+
}
153235
}
154236
}

0 commit comments

Comments
 (0)