Skip to content

Commit 63f621d

Browse files
authored
Merge pull request #106 from serilog/dev
4.0.0 Release
2 parents 1685461 + d8e2daa commit 63f621d

29 files changed

+384
-51
lines changed

example/Sample/Sample.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
9+
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
1010
</ItemGroup>
1111

1212
<ItemGroup>

src/Serilog.Expressions/Expressions/BuiltInProperty.cs

+2
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ static class BuiltInProperty
2525
public const string Properties = "p";
2626
public const string Renderings = "r";
2727
public const string EventId = "i";
28+
public const string TraceId = "tr";
29+
public const string SpanId = "sp";
2830
}

src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ public ExpressionConstantMapper(IDictionary<object, Expression> mapping)
2828

2929
protected override Expression VisitConstant(ConstantExpression node)
3030
{
31-
if (node.Value != null &&
32-
node.Value is ScalarValue sv &&
33-
_mapping.TryGetValue(sv.Value, out var substitute))
31+
if (node.Value is ScalarValue { Value: {} sv } &&
32+
_mapping.TryGetValue(sv, out var substitute))
3433
return substitute;
3534

3635
return base.VisitConstant(node);
3736
}
38-
}
37+
}

src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ static class Intrinsics
5555
public static LogEventPropertyValue ConstructSequenceValue(List<LogEventPropertyValue?> elements)
5656
{
5757
if (elements.Any(el => el == null))
58-
return new SequenceValue(elements.Where(el => el != null));
58+
return new SequenceValue(elements.Where(el => el != null)!);
5959

60-
return new SequenceValue(elements);
60+
return new SequenceValue(elements!);
6161
}
6262

6363
public static List<LogEventProperty> CollectStructureProperties(string[] names, LogEventPropertyValue?[] values)

src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,8 @@ static ExpressionBody CompileLogical(Func<ExpressionBody, ExpressionBody, Expres
186186
protected override ExpressionBody Transform(AccessorExpression spx)
187187
{
188188
var receiver = Transform(spx.Receiver);
189-
return LX.Call(TryGetStructurePropertyValueMethod, LX.Constant(StringComparison.OrdinalIgnoreCase), receiver, LX.Constant(spx.MemberName, typeof(string)));
189+
return LX.Call(TryGetStructurePropertyValueMethod, LX.Constant(StringComparison.Ordinal), receiver, LX.Constant(spx.MemberName, typeof(string)));
190190
}
191-
192191
protected override ExpressionBody Transform(ConstantExpression cx)
193192
{
194193
return LX.Constant(cx.Constant);
@@ -207,6 +206,10 @@ protected override ExpressionBody Transform(AmbientNameExpression px)
207206
BuiltInProperty.Message => Splice(context => new ScalarValue(Intrinsics.RenderMessage(formatter, context))),
208207
BuiltInProperty.Exception => Splice(context =>
209208
context.LogEvent.Exception == null ? null : new ScalarValue(context.LogEvent.Exception)),
209+
BuiltInProperty.TraceId => Splice(context =>
210+
context.LogEvent.TraceId == null ? null : new ScalarValue(context.LogEvent.TraceId.Value)),
211+
BuiltInProperty.SpanId => Splice(context =>
212+
context.LogEvent.SpanId == null ? null : new ScalarValue(context.LogEvent.SpanId.Value)),
210213
BuiltInProperty.Timestamp => Splice(context => new ScalarValue(context.LogEvent.Timestamp)),
211214
BuiltInProperty.MessageTemplate => Splice(context => new ScalarValue(context.LogEvent.MessageTemplate.Text)),
212215
BuiltInProperty.Properties => Splice(context =>

src/Serilog.Expressions/Expressions/Runtime/Coerce.cs

+13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System.Diagnostics;
1516
using System.Diagnostics.CodeAnalysis;
1617
using Serilog.Events;
1718

@@ -77,6 +78,18 @@ public static bool String(LogEventPropertyValue? value, [MaybeNullWhen(false)] o
7778
str = sv.Value.ToString()!;
7879
return true;
7980
}
81+
82+
if (sv.Value is ActivityTraceId traceId)
83+
{
84+
str = traceId.ToHexString();
85+
return true;
86+
}
87+
88+
if (sv.Value is ActivitySpanId spanId)
89+
{
90+
str = spanId.ToHexString();
91+
return true;
92+
}
8093
}
8194

8295
str = default;

src/Serilog.Expressions/Serilog.Expressions.csproj

+7-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
<PropertyGroup>
44
<Description>An embeddable mini-language for filtering, enriching, and formatting Serilog
55
events, ideal for use with JSON or XML configuration.</Description>
6-
<VersionPrefix>3.5.0</VersionPrefix>
6+
<VersionPrefix>4.0.0</VersionPrefix>
77
<Authors>Serilog Contributors</Authors>
8-
<TargetFrameworks>netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
8+
<TargetFrameworks>netstandard2.1;netstandard2.0;net5.0;net6.0;net7.0</TargetFrameworks>
99
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1010
<RootNamespace>Serilog</RootNamespace>
1111
<PackageTags>serilog</PackageTags>
@@ -14,15 +14,17 @@
1414
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
1515
<RepositoryUrl>https://github.com./serilog/serilog-expressions</RepositoryUrl>
1616
<RepositoryType>git</RepositoryType>
17+
<PackageReadmeFile>README.md</PackageReadmeFile>
1718
</PropertyGroup>
1819

1920
<ItemGroup>
20-
<PackageReference Include="Serilog" Version="2.12.0" />
21-
<PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="All" />
21+
<PackageReference Include="Serilog" Version="3.1.0" />
22+
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="All" />
2223
</ItemGroup>
2324

2425
<ItemGroup>
2526
<None Include="..\..\assets\icon.png" Pack="true" Visible="false" PackagePath="" />
27+
<None Include="..\..\README.md" Pack="true" Visible="false" PackagePath="" />
2628
</ItemGroup>
27-
29+
2830
</Project>

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

+15-13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Serilog.Expressions.Ast;
1717
using Serilog.Expressions.Compilation;
1818
using Serilog.Templates.Ast;
19+
using Serilog.Templates.Encoding;
1920
using Serilog.Templates.Themes;
2021

2122
namespace Serilog.Templates.Compilation;
@@ -24,39 +25,40 @@ static class TemplateCompiler
2425
{
2526
public static CompiledTemplate Compile(Template template,
2627
IFormatProvider? formatProvider, NameResolver nameResolver,
27-
TemplateTheme theme)
28+
TemplateTheme theme,
29+
EncodedTemplateFactory encoder)
2830
{
2931
return template switch
3032
{
3133
LiteralText text => new CompiledLiteralText(text.Text, theme),
32-
FormattedExpression { Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Level} } level => new CompiledLevelToken(
33-
level.Format, level.Alignment, theme),
34+
FormattedExpression { Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Level} } level =>
35+
encoder.Wrap(new CompiledLevelToken(level.Format, level.Alignment, theme)),
3436
FormattedExpression
3537
{
3638
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Exception },
3739
Alignment: null,
3840
Format: null
39-
} => new CompiledExceptionToken(theme),
41+
} => encoder.Wrap(new CompiledExceptionToken(theme)),
4042
FormattedExpression
4143
{
4244
Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Message },
4345
Format: null
44-
} message => new CompiledMessageToken(formatProvider, message.Alignment, theme),
45-
FormattedExpression expression => new CompiledFormattedExpression(
46+
} message => encoder.Wrap(new CompiledMessageToken(formatProvider, message.Alignment, theme)),
47+
FormattedExpression expression => encoder.MakeCompiledFormattedExpression(
4648
ExpressionCompiler.Compile(expression.Expression, formatProvider, nameResolver), expression.Format, expression.Alignment, formatProvider, theme),
47-
TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, formatProvider, nameResolver, theme)).ToArray()),
49+
TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, formatProvider, nameResolver, theme, encoder)).ToArray()),
4850
Conditional conditional => new CompiledConditional(
4951
ExpressionCompiler.Compile(conditional.Condition, formatProvider, nameResolver),
50-
Compile(conditional.Consequent, formatProvider, nameResolver, theme),
51-
conditional.Alternative == null ? null : Compile(conditional.Alternative, formatProvider, nameResolver, theme)),
52+
Compile(conditional.Consequent, formatProvider, nameResolver, theme, encoder),
53+
conditional.Alternative == null ? null : Compile(conditional.Alternative, formatProvider, nameResolver, theme, encoder)),
5254
Repetition repetition => new CompiledRepetition(
5355
ExpressionCompiler.Compile(repetition.Enumerable, formatProvider, nameResolver),
5456
repetition.BindingNames.Length > 0 ? repetition.BindingNames[0] : null,
5557
repetition.BindingNames.Length > 1 ? repetition.BindingNames[1] : null,
56-
Compile(repetition.Body, formatProvider, nameResolver, theme),
57-
repetition.Delimiter == null ? null : Compile(repetition.Delimiter, formatProvider, nameResolver, theme),
58-
repetition.Alternative == null ? null : Compile(repetition.Alternative, formatProvider, nameResolver, theme)),
58+
Compile(repetition.Body, formatProvider, nameResolver, theme, encoder),
59+
repetition.Delimiter == null ? null : Compile(repetition.Delimiter, formatProvider, nameResolver, theme, encoder),
60+
repetition.Alternative == null ? null : Compile(repetition.Alternative, formatProvider, nameResolver, theme, encoder)),
5961
_ => throw new NotSupportedException()
6062
};
6163
}
62-
}
64+
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Serilog.Expressions.Runtime;
1818
using Serilog.Templates.Ast;
1919
using Serilog.Templates.Compilation.UnreferencedProperties;
20+
using Serilog.Templates.Compilation.Unsafe;
2021

2122
namespace Serilog.Templates.Compilation;
2223

@@ -27,7 +28,8 @@ public static NameResolver Build(NameResolver? additionalNameResolver, Template
2728
var resolvers = new List<NameResolver>
2829
{
2930
new StaticMemberNameResolver(typeof(RuntimeOperators)),
30-
new UnreferencedPropertiesFunction(template)
31+
new UnreferencedPropertiesFunction(template),
32+
new UnsafeOutputFunction()
3133
};
3234

3335
if (additionalNameResolver != null)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 System.Diagnostics.CodeAnalysis;
16+
using System.Reflection;
17+
using Serilog.Events;
18+
using Serilog.Expressions;
19+
using Serilog.Templates.Encoding;
20+
21+
namespace Serilog.Templates.Compilation.Unsafe;
22+
23+
/// <summary>
24+
/// Marks an expression in a template as bypassing the output encoding mechanism.
25+
/// </summary>
26+
class UnsafeOutputFunction : NameResolver
27+
{
28+
const string FunctionName = "unsafe";
29+
30+
public override bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation)
31+
{
32+
if (name.Equals(FunctionName, StringComparison.OrdinalIgnoreCase))
33+
{
34+
implementation = typeof(UnsafeOutputFunction).GetMethod(nameof(Implementation),
35+
BindingFlags.Static | BindingFlags.Public)!;
36+
return true;
37+
}
38+
39+
implementation = null;
40+
return false;
41+
}
42+
43+
// By convention, built-in functions accept and return nullable values.
44+
// ReSharper disable once ReturnTypeCanBeNotNullable
45+
public static LogEventPropertyValue? Implementation(LogEventPropertyValue? inner)
46+
{
47+
return new ScalarValue(new PreEncodedValue(inner));
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Serilog.Expressions;
2+
using Serilog.Templates.Compilation;
3+
4+
namespace Serilog.Templates.Encoding
5+
{
6+
class EncodedCompiledTemplate : CompiledTemplate
7+
{
8+
readonly CompiledTemplate _inner;
9+
readonly TemplateOutputEncoder _encoder;
10+
11+
public EncodedCompiledTemplate(CompiledTemplate inner, TemplateOutputEncoder encoder)
12+
{
13+
_inner = inner;
14+
_encoder = encoder;
15+
}
16+
17+
public override void Evaluate(EvaluationContext ctx, TextWriter output)
18+
{
19+
var buffer = new StringWriter(output.FormatProvider);
20+
_inner.Evaluate(ctx, buffer);
21+
var encoded = _encoder.Encode(buffer.ToString());
22+
output.Write(encoded);
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Serilog.Expressions;
2+
using Serilog.Parsing;
3+
using Serilog.Templates.Compilation;
4+
using Serilog.Templates.Themes;
5+
6+
namespace Serilog.Templates.Encoding
7+
{
8+
class EncodedTemplateFactory
9+
{
10+
readonly TemplateOutputEncoder? _encoder;
11+
12+
public EncodedTemplateFactory(TemplateOutputEncoder? encoder)
13+
{
14+
_encoder = encoder;
15+
}
16+
17+
public CompiledTemplate Wrap(CompiledTemplate inner)
18+
{
19+
if (_encoder == null)
20+
return inner;
21+
22+
return new EncodedCompiledTemplate(inner, _encoder);
23+
}
24+
25+
public CompiledTemplate MakeCompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme)
26+
{
27+
if (_encoder == null)
28+
return new CompiledFormattedExpression(expression, format, alignment, formatProvider, theme);
29+
30+
return new EscapableEncodedCompiledFormattedExpression(expression, format, alignment, formatProvider, theme, _encoder);
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Serilog.Events;
2+
using Serilog.Expressions;
3+
using Serilog.Expressions.Runtime;
4+
using Serilog.Parsing;
5+
using Serilog.Templates.Compilation;
6+
using Serilog.Templates.Themes;
7+
8+
namespace Serilog.Templates.Encoding
9+
{
10+
class EscapableEncodedCompiledFormattedExpression : CompiledTemplate
11+
{
12+
static int _nextSubstituteLocalNameSuffix;
13+
readonly string _substituteLocalName = $"%sub{Interlocked.Increment(ref _nextSubstituteLocalNameSuffix)}";
14+
readonly Evaluatable _expression;
15+
readonly TemplateOutputEncoder _encoder;
16+
readonly CompiledFormattedExpression _inner;
17+
18+
public EscapableEncodedCompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme, TemplateOutputEncoder encoder)
19+
{
20+
_expression = expression;
21+
_encoder = encoder;
22+
23+
// `expression` can't be passed through, because it may include calls to the `unsafe()` function (nested in arbitrary subexpressions) that
24+
// need to be evaluated first. So, instead, we evaluate `expression` and unwrap the result of `unsafe`, placing the result in a local variable
25+
// that the formatting expression we construct here can read from.
26+
_inner = new CompiledFormattedExpression(GetSubstituteLocalValue, format, alignment, formatProvider, theme);
27+
}
28+
29+
LogEventPropertyValue? GetSubstituteLocalValue(EvaluationContext context)
30+
{
31+
return Locals.TryGetValue(context.Locals, _substituteLocalName, out var computed)
32+
? computed
33+
: null;
34+
}
35+
36+
public override void Evaluate(EvaluationContext ctx, TextWriter output)
37+
{
38+
var value = _expression(ctx);
39+
40+
if (value is ScalarValue { Value: PreEncodedValue pv })
41+
{
42+
var rawContext = pv.Inner == null ?
43+
new EvaluationContext(ctx.LogEvent) :
44+
new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _substituteLocalName, pv.Inner));
45+
_inner.Evaluate(rawContext, output);
46+
return;
47+
}
48+
49+
var buffer = new StringWriter(output.FormatProvider);
50+
51+
var bufferedContext = value == null
52+
? ctx
53+
: new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _substituteLocalName, value));
54+
55+
_inner.Evaluate(bufferedContext, buffer);
56+
var encoded = _encoder.Encode(buffer.ToString());
57+
output.Write(encoded);
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)