Skip to content

Commit 555c284

Browse files
authored
Merge pull request #121 from epeshk/timestamp-token
Removed allocations for timestamp
2 parents c85771f + df19ac2 commit 555c284

File tree

5 files changed

+92
-2
lines changed

5 files changed

+92
-2
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright © Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Serilog.Expressions;
16+
using Serilog.Parsing;
17+
using Serilog.Templates.Rendering;
18+
using Serilog.Templates.Themes;
19+
20+
namespace Serilog.Templates.Compilation;
21+
22+
class CompiledTimestampToken : CompiledTemplate
23+
{
24+
readonly string? _format;
25+
readonly Alignment? _alignment;
26+
readonly IFormatProvider? _formatProvider;
27+
readonly Style _secondaryText;
28+
29+
public CompiledTimestampToken(string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)
30+
{
31+
_format = format;
32+
_alignment = alignment;
33+
_formatProvider = formatProvider;
34+
_secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText);
35+
}
36+
37+
public override void Evaluate(EvaluationContext ctx, TextWriter output)
38+
{
39+
var invisibleCharacterCount = 0;
40+
41+
if (_alignment == null)
42+
{
43+
EvaluateUnaligned(ctx, output, _formatProvider, ref invisibleCharacterCount);
44+
}
45+
else
46+
{
47+
var writer = new StringWriter();
48+
EvaluateUnaligned(ctx, writer, _formatProvider, ref invisibleCharacterCount);
49+
Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));
50+
}
51+
}
52+
53+
void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider, ref int invisibleCharacterCount)
54+
{
55+
var value = ctx.LogEvent.Timestamp;
56+
57+
using var style = _secondaryText.Set(output, ref invisibleCharacterCount);
58+
59+
#if FEATURE_SPAN
60+
Span<char> buffer = stackalloc char[36];
61+
if (value.TryFormat(buffer, out int charsWritten, _format, _formatProvider))
62+
{
63+
output.Write(buffer[..charsWritten]);
64+
return;
65+
}
66+
#endif
67+
output.Write(value.ToString(_format, formatProvider));
68+
69+
}
70+
}

src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public static CompiledTemplate Compile(Template template,
4444
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Message },
4545
Format: null
4646
} message => encoder.Wrap(new CompiledMessageToken(formatProvider, message.Alignment, theme)),
47+
FormattedExpression
48+
{
49+
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Timestamp },
50+
} timestamp => encoder.Wrap(new CompiledTimestampToken(timestamp.Format, timestamp.Alignment, formatProvider, theme)),
4751
FormattedExpression expression => encoder.MakeCompiledFormattedExpression(
4852
ExpressionCompiler.Compile(expression.Expression, formatProvider, nameResolver), expression.Format, expression.Alignment, formatProvider, theme),
4953
TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, formatProvider, nameResolver, theme, encoder)).ToArray()),

test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ Culture-specific {42.34} ⇶ Culture-specific 42,34
3333
{rest()} ⇶ {"Name":"nblumhardt"}
3434
{Name} {rest()} ⇶ nblumhardt {}
3535
{rest(true)} ⇶ {}
36+
{@t:yyyy-MM-dd HH:mm:ss.ffff zzz} ⇶ 2000-12-31 23:59:58.1230 +10:00
37+
{@t:yyyy-MM-dd HH:mm:ss.ffff zzz------------------} ⇶ 2000-12-31 23:59:58.1230 +10:00------------------

test/Serilog.Expressions.Tests/Support/Some.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,22 @@ public static LogEvent InformationEvent(string messageTemplate = "Hello, world!"
1212
return LogEvent(LogEventLevel.Information, messageTemplate, propertyValues);
1313
}
1414

15+
public static LogEvent InformationEvent(DateTimeOffset timestamp, string messageTemplate = "Hello, world!", params object?[] propertyValues)
16+
{
17+
return LogEvent(timestamp, LogEventLevel.Information, messageTemplate, propertyValues);
18+
}
19+
1520
public static LogEvent WarningEvent(string messageTemplate = "Hello, world!", params object?[] propertyValues)
1621
{
1722
return LogEvent(LogEventLevel.Warning, messageTemplate, propertyValues);
1823
}
1924

2025
public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = "Hello, world!", params object?[] propertyValues)
26+
{
27+
return LogEvent(DateTimeOffset.Now, level, messageTemplate, propertyValues);
28+
}
29+
30+
public static LogEvent LogEvent(DateTimeOffset timestamp, LogEventLevel level, string messageTemplate = "Hello, world!", params object?[] propertyValues)
2131
{
2232
var log = new LoggerConfiguration().CreateLogger();
2333
#pragma warning disable Serilog004 // Constant MessageTemplate verifier
@@ -26,7 +36,7 @@ public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = "H
2636
{
2737
throw new XunitException("Template could not be bound.");
2838
}
29-
return new(DateTimeOffset.Now, level, null, template, properties);
39+
return new(timestamp, level, null, template, properties);
3040
}
3141

3242
public static object AnonymousObject()

test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using Serilog.Events;
23
using Serilog.Expressions.Tests.Support;
34
using Serilog.Templates;
45
using Xunit;
@@ -7,14 +8,17 @@ namespace Serilog.Expressions.Tests;
78

89
public class TemplateEvaluationTests
910
{
11+
static readonly DateTimeOffset TestTimestamp = new(
12+
2000, 12, 31, 23, 59, 58, 123, TimeSpan.FromHours(10));
13+
1014
public static IEnumerable<object[]> TemplateEvaluationCases =>
1115
AsvCases.ReadCases("template-evaluation-cases.asv");
1216

1317
[Theory]
1418
[MemberData(nameof(TemplateEvaluationCases))]
1519
public void TemplatesAreCorrectlyEvaluated(string template, string expected)
1620
{
17-
var evt = Some.InformationEvent("Hello, {Name}!", "nblumhardt");
21+
var evt = Some.InformationEvent(TestTimestamp, "Hello, {Name}!", "nblumhardt");
1822
var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag("fr-FR");
1923
var compiled = new ExpressionTemplate(template, formatProvider: frFr);
2024
var output = new StringWriter();

0 commit comments

Comments
 (0)