Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright © Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Serilog.Expressions;
using Serilog.Parsing;
using Serilog.Templates.Rendering;
using Serilog.Templates.Themes;

namespace Serilog.Templates.Compilation;

class CompiledTimestampToken : CompiledTemplate
{
readonly string? _format;
readonly Alignment? _alignment;
readonly IFormatProvider? _formatProvider;
readonly Style _secondaryText;

public CompiledTimestampToken(string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)
{
_format = format;
_alignment = alignment;
_formatProvider = formatProvider;
_secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText);
}

public override void Evaluate(EvaluationContext ctx, TextWriter output)
{
var invisibleCharacterCount = 0;

if (_alignment == null)
{
EvaluateUnaligned(ctx, output, _formatProvider, ref invisibleCharacterCount);
}
else
{
var writer = new StringWriter();
EvaluateUnaligned(ctx, writer, _formatProvider, ref invisibleCharacterCount);
Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount));
}
}

void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider, ref int invisibleCharacterCount)
{
var value = ctx.LogEvent.Timestamp;

using var style = _secondaryText.Set(output, ref invisibleCharacterCount);

#if FEATURE_SPAN
Span<char> buffer = stackalloc char[36];
if (value.TryFormat(buffer, out int charsWritten, _format, _formatProvider))
{
output.Write(buffer[..charsWritten]);
return;
}
#endif
output.Write(value.ToString(_format, formatProvider));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public static CompiledTemplate Compile(Template template,
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Message },
Format: null
} message => encoder.Wrap(new CompiledMessageToken(formatProvider, message.Alignment, theme)),
FormattedExpression
{
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Timestamp },
} timestamp => encoder.Wrap(new CompiledTimestampToken(timestamp.Format, timestamp.Alignment, formatProvider, theme)),
FormattedExpression expression => encoder.MakeCompiledFormattedExpression(
ExpressionCompiler.Compile(expression.Expression, formatProvider, nameResolver), expression.Format, expression.Alignment, formatProvider, theme),
TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, formatProvider, nameResolver, theme, encoder)).ToArray()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ Culture-specific {42.34} ⇶ Culture-specific 42,34
{rest()} ⇶ {"Name":"nblumhardt"}
{Name} {rest()} ⇶ nblumhardt {}
{rest(true)} ⇶ {}
{@t:yyyy-MM-dd HH:mm:ss.ffff zzz} ⇶ 2000-12-31 23:59:58.1230 +10:00
{@t:yyyy-MM-dd HH:mm:ss.ffff zzz------------------} ⇶ 2000-12-31 23:59:58.1230 +10:00------------------
12 changes: 11 additions & 1 deletion test/Serilog.Expressions.Tests/Support/Some.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ public static LogEvent InformationEvent(string messageTemplate = "Hello, world!"
return LogEvent(LogEventLevel.Information, messageTemplate, propertyValues);
}

public static LogEvent InformationEvent(DateTimeOffset timestamp, string messageTemplate = "Hello, world!", params object?[] propertyValues)
{
return LogEvent(timestamp, LogEventLevel.Information, messageTemplate, propertyValues);
}

public static LogEvent WarningEvent(string messageTemplate = "Hello, world!", params object?[] propertyValues)
{
return LogEvent(LogEventLevel.Warning, messageTemplate, propertyValues);
}

public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = "Hello, world!", params object?[] propertyValues)
{
return LogEvent(DateTimeOffset.Now, level, messageTemplate, propertyValues);
}

public static LogEvent LogEvent(DateTimeOffset timestamp, LogEventLevel level, string messageTemplate = "Hello, world!", params object?[] propertyValues)
{
var log = new LoggerConfiguration().CreateLogger();
#pragma warning disable Serilog004 // Constant MessageTemplate verifier
Expand All @@ -26,7 +36,7 @@ public static LogEvent LogEvent(LogEventLevel level, string messageTemplate = "H
{
throw new XunitException("Template could not be bound.");
}
return new(DateTimeOffset.Now, level, null, template, properties);
return new(timestamp, level, null, template, properties);
}

public static object AnonymousObject()
Expand Down
6 changes: 5 additions & 1 deletion test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using Serilog.Events;
using Serilog.Expressions.Tests.Support;
using Serilog.Templates;
using Xunit;
Expand All @@ -7,14 +8,17 @@ namespace Serilog.Expressions.Tests;

public class TemplateEvaluationTests
{
static readonly DateTimeOffset TestTimestamp = new(
2000, 12, 31, 23, 59, 58, 123, TimeSpan.FromHours(10));

public static IEnumerable<object[]> TemplateEvaluationCases =>
AsvCases.ReadCases("template-evaluation-cases.asv");

[Theory]
[MemberData(nameof(TemplateEvaluationCases))]
public void TemplatesAreCorrectlyEvaluated(string template, string expected)
{
var evt = Some.InformationEvent("Hello, {Name}!", "nblumhardt");
var evt = Some.InformationEvent(TestTimestamp, "Hello, {Name}!", "nblumhardt");
var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag("fr-FR");
var compiled = new ExpressionTemplate(template, formatProvider: frFr);
var output = new StringWriter();
Expand Down