diff --git a/example/Sample/Program.cs b/example/Sample/Program.cs index 1169c88..8a8443a 100644 --- a/example/Sample/Program.cs +++ b/example/Sample/Program.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using Serilog; using Serilog.Debugging; using Serilog.Templates; +using Serilog.Templates.Themes; namespace Sample { @@ -24,7 +26,8 @@ static void TextFormattingExample1() .WriteTo.Console(new ExpressionTemplate( "[{@t:HH:mm:ss} {@l:u3}" + "{#if SourceContext is not null} ({Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)}){#end}] " + - "{@m} (first item is {coalesce(Items[0], '')})\n{@x}")) + "{@m} (first item is {coalesce(Items[0], '')})\n{@x}", + theme: TemplateTheme.Code)) .CreateLogger(); log.Information("Running {Example}", nameof(TextFormattingExample1)); @@ -41,7 +44,7 @@ static void JsonFormattingExample() using var log = new LoggerConfiguration() .Enrich.WithProperty("Application", "Example") .WriteTo.Console(new ExpressionTemplate( - "{ {@t, @mt, @l: if @l = 'Information' then undefined() else @l, @x, ..@p} }\n")) + "{ {@t: UtcDateTime(@t), @mt, @l: if @l = 'Information' then undefined() else @l, @x, ..@p} }\n")) .CreateLogger(); log.Information("Running {Example}", nameof(JsonFormattingExample)); @@ -75,6 +78,16 @@ static void PipelineComponentExample() static void TextFormattingExample2() { + // Emulates `Microsoft.Extensions.Logging`'s `ConsoleLogger`. + + var melon = new TemplateTheme(TemplateTheme.Literate, new Dictionary + { + // `Information` is dark green in MEL. + [TemplateThemeStyle.LevelInformation] = "\x1b[38;5;34m", + [TemplateThemeStyle.String] = "\x1b[38;5;159m", + [TemplateThemeStyle.Number] = "\x1b[38;5;159m" + }); + using var log = new LoggerConfiguration() .WriteTo.Console(new ExpressionTemplate( "{@l:w4}: {SourceContext}\n" + @@ -82,16 +95,18 @@ static void TextFormattingExample2() " {#each s in Scope}=> {s}{#delimit} {#end}\n" + "{#end}" + " {@m}\n" + - "{@x}")) + "{@x}", + theme: melon)) .CreateLogger(); var program = log.ForContext(); - program.Information("Starting up"); + program.Information("Host listening at {ListenUri}", "https://hello-world.local"); - // Emulate data produced by the Serilog.AspNetCore integration - var scoped = program.ForContext("Scope", new[] {"Main", "TextFormattingExample2()"}); + program + .ForContext("Scope", new[] {"Main", "TextFormattingExample2()"}) + .Information("HTTP {Method} {Path} responded {StatusCode} in {Elapsed:0.000} ms", "GET", "/api/hello", 200, 1.23); - scoped.Information("Hello, world!"); + program.Warning("We've reached the end of the line"); } } } diff --git a/example/Sample/Sample.csproj b/example/Sample/Sample.csproj index c4a34a0..78981c6 100644 --- a/example/Sample/Sample.csproj +++ b/example/Sample/Sample.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 Exe diff --git a/serilog-expressions.sln.DotSettings b/serilog-expressions.sln.DotSettings index 21575ef..b1832e8 100644 --- a/serilog-expressions.sln.DotSettings +++ b/serilog-expressions.sln.DotSettings @@ -1,6 +1,7 @@  True True + True True True True diff --git a/src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs b/src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs index 3031c56..5995308 100644 --- a/src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/AccessorExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; namespace Serilog.Expressions.Ast { diff --git a/src/Serilog.Expressions/Expressions/Ast/AmbientNameExpression.cs b/src/Serilog.Expressions/Expressions/Ast/AmbientNameExpression.cs index dce2562..1b2ea8a 100644 --- a/src/Serilog.Expressions/Expressions/Ast/AmbientNameExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/AmbientNameExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; namespace Serilog.Expressions.Ast { @@ -6,11 +20,11 @@ class AmbientNameExpression : Expression { readonly bool _requiresEscape; - public AmbientNameExpression(string Name, bool isBuiltIn) + public AmbientNameExpression(string name, bool isBuiltIn) { - PropertyName = Name ?? throw new ArgumentNullException(nameof(Name)); + PropertyName = name ?? throw new ArgumentNullException(nameof(name)); IsBuiltIn = isBuiltIn; - _requiresEscape = !SerilogExpression.IsValidIdentifier(Name); + _requiresEscape = !SerilogExpression.IsValidIdentifier(name); } public string PropertyName { get; } diff --git a/src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs b/src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs index 18b98c1..7b49779 100644 --- a/src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/ArrayExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Linq; namespace Serilog.Expressions.Ast diff --git a/src/Serilog.Expressions/Expressions/Ast/CallExpression.cs b/src/Serilog.Expressions/Expressions/Ast/CallExpression.cs index f25eb4e..b1bd730 100644 --- a/src/Serilog.Expressions/Expressions/Ast/CallExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/CallExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Linq; namespace Serilog.Expressions.Ast diff --git a/src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs b/src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs index 5dca402..38ad809 100644 --- a/src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/ConstantExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Globalization; using Serilog.Events; diff --git a/src/Serilog.Expressions/Expressions/Ast/Element.cs b/src/Serilog.Expressions/Expressions/Ast/Element.cs index 7a810f0..7a08406 100644 --- a/src/Serilog.Expressions/Expressions/Ast/Element.cs +++ b/src/Serilog.Expressions/Expressions/Ast/Element.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { abstract class Element { diff --git a/src/Serilog.Expressions/Expressions/Ast/Expression.cs b/src/Serilog.Expressions/Expressions/Ast/Expression.cs index 045d1dd..716ea7e 100644 --- a/src/Serilog.Expressions/Expressions/Ast/Expression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/Expression.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { abstract class Expression { diff --git a/src/Serilog.Expressions/Expressions/Ast/IndexOfMatchExpression.cs b/src/Serilog.Expressions/Expressions/Ast/IndexOfMatchExpression.cs index 574ae0a..24ecb41 100644 --- a/src/Serilog.Expressions/Expressions/Ast/IndexOfMatchExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/IndexOfMatchExpression.cs @@ -1,3 +1,17 @@ +// 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 System; using System.Text.RegularExpressions; diff --git a/src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs b/src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs index 2029c70..282c1ea 100644 --- a/src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/IndexerExpression.cs @@ -1,3 +1,17 @@ +// 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. + namespace Serilog.Expressions.Ast { class IndexerExpression : Expression diff --git a/src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs b/src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs index 0474676..aec0be0 100644 --- a/src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs +++ b/src/Serilog.Expressions/Expressions/Ast/IndexerWildcard.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { enum IndexerWildcard { Undefined, Any, All } } diff --git a/src/Serilog.Expressions/Expressions/Ast/IndexerWildcardExpression.cs b/src/Serilog.Expressions/Expressions/Ast/IndexerWildcardExpression.cs index 280ca83..df431ce 100644 --- a/src/Serilog.Expressions/Expressions/Ast/IndexerWildcardExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/IndexerWildcardExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; namespace Serilog.Expressions.Ast { diff --git a/src/Serilog.Expressions/Expressions/Ast/ItemElement.cs b/src/Serilog.Expressions/Expressions/Ast/ItemElement.cs index 7e9c366..f28dbd8 100644 --- a/src/Serilog.Expressions/Expressions/Ast/ItemElement.cs +++ b/src/Serilog.Expressions/Expressions/Ast/ItemElement.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { class ItemElement : Element { diff --git a/src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs b/src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs index 171a9b5..f26059b 100644 --- a/src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/LambdaExpression.cs @@ -1,3 +1,17 @@ +// 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 System.Linq; namespace Serilog.Expressions.Ast diff --git a/src/Serilog.Expressions/Expressions/Ast/LocalNameExpression.cs b/src/Serilog.Expressions/Expressions/Ast/LocalNameExpression.cs index d786d68..b276784 100644 --- a/src/Serilog.Expressions/Expressions/Ast/LocalNameExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/LocalNameExpression.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; namespace Serilog.Expressions.Ast { diff --git a/src/Serilog.Expressions/Expressions/Ast/Member.cs b/src/Serilog.Expressions/Expressions/Ast/Member.cs index 9230f45..4769c36 100644 --- a/src/Serilog.Expressions/Expressions/Ast/Member.cs +++ b/src/Serilog.Expressions/Expressions/Ast/Member.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { abstract class Member { diff --git a/src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs b/src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs index cab763e..e747e67 100644 --- a/src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/ObjectExpression.cs @@ -1,3 +1,17 @@ +// 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 System.Linq; namespace Serilog.Expressions.Ast diff --git a/src/Serilog.Expressions/Expressions/Ast/ParameterExpression.cs b/src/Serilog.Expressions/Expressions/Ast/ParameterExpression.cs index 23b158d..83870c1 100644 --- a/src/Serilog.Expressions/Expressions/Ast/ParameterExpression.cs +++ b/src/Serilog.Expressions/Expressions/Ast/ParameterExpression.cs @@ -1,3 +1,17 @@ +// 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. + namespace Serilog.Expressions.Ast { class ParameterExpression : Expression diff --git a/src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs b/src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs index 862c1ff..d8e39dc 100644 --- a/src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs +++ b/src/Serilog.Expressions/Expressions/Ast/PropertyMember.cs @@ -1,4 +1,18 @@ -using Serilog.Events; +// 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.Events; namespace Serilog.Expressions.Ast { diff --git a/src/Serilog.Expressions/Expressions/Ast/SpreadElement.cs b/src/Serilog.Expressions/Expressions/Ast/SpreadElement.cs index d6fa538..4da9b40 100644 --- a/src/Serilog.Expressions/Expressions/Ast/SpreadElement.cs +++ b/src/Serilog.Expressions/Expressions/Ast/SpreadElement.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { class SpreadElement : Element { diff --git a/src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs b/src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs index 7d13152..f039cba 100644 --- a/src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs +++ b/src/Serilog.Expressions/Expressions/Ast/SpreadMember.cs @@ -1,4 +1,18 @@ -namespace Serilog.Expressions.Ast +// 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. + +namespace Serilog.Expressions.Ast { class SpreadMember : Member { diff --git a/src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs b/src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs index 3ffc6ec..c25b6b1 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Arrays/ConstantArrayEvaluator.cs @@ -1,4 +1,18 @@ -using System.Linq; +// 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 System.Linq; using Serilog.Events; using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Transformations; diff --git a/src/Serilog.Expressions/Expressions/Compilation/DefaultFunctionNameResolver.cs b/src/Serilog.Expressions/Expressions/Compilation/DefaultFunctionNameResolver.cs index ba79f4f..ed149ea 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/DefaultFunctionNameResolver.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/DefaultFunctionNameResolver.cs @@ -1,4 +1,18 @@ -using Serilog.Expressions.Runtime; +// 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.Runtime; namespace Serilog.Expressions.Compilation { diff --git a/src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs b/src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs index f504045..31d1fdd 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/ExpressionCompiler.cs @@ -1,4 +1,19 @@ -using Serilog.Expressions.Ast; +// 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 System; +using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Arrays; using Serilog.Expressions.Compilation.Linq; using Serilog.Expressions.Compilation.Properties; @@ -22,10 +37,11 @@ public static Expression Translate(Expression expression) return actual; } - public static Evaluatable Compile(Expression expression, NameResolver nameResolver) + public static Evaluatable Compile(Expression expression, IFormatProvider? formatProvider, + NameResolver nameResolver) { var actual = Translate(expression); - return LinqExpressionCompiler.Compile(actual, nameResolver); + return LinqExpressionCompiler.Compile(actual, formatProvider, nameResolver); } } } diff --git a/src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs b/src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs index 085b7d4..b0eba87 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Linq/ExpressionConstantMapper.cs @@ -1,4 +1,18 @@ -using System.Collections.Generic; +// 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 System.Collections.Generic; using System.Linq.Expressions; using Serilog.Events; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs b/src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs index 74564b0..17ad067 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Linq/Intrinsics.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; @@ -8,6 +22,7 @@ using Serilog.Expressions.Runtime; using Serilog.Formatting.Display; using Serilog.Parsing; +using Serilog.Templates.Compilation; // ReSharper disable ParameterTypeCanBeEnumerable.Global @@ -18,9 +33,6 @@ static class Intrinsics static readonly LogEventPropertyValue NegativeOne = new ScalarValue(-1); static readonly LogEventPropertyValue Tombstone = new ScalarValue("😬 (if you see this you have found a bug.)"); - // TODO #19: formatting is culture-specific. - static readonly MessageTemplateTextFormatter MessageFormatter = new MessageTemplateTextFormatter("{Message:lj}"); - public static List CollectSequenceElements(LogEventPropertyValue?[] elements) { return elements.ToList(); @@ -164,27 +176,27 @@ public static bool CoerceToScalarBoolean(LogEventPropertyValue value) return null; } - public static string RenderMessage(LogEvent logEvent) + // Use of `CompiledMessageToken` is a layering violation here, but we want to ensure the formatting implementations + // line up exactly. Some refactoring here might be worthwhile, though with an eye on indirection costs. + public static string RenderMessage(CompiledMessageToken formatter, EvaluationContext ctx) { - // Use the same `:lj`-style formatting default as Serilog.Sinks.Console. var sw = new StringWriter(); - MessageFormatter.Format(logEvent, sw); + formatter.Evaluate(ctx, sw); return sw.ToString(); } - public static LogEventPropertyValue? GetRenderings(LogEvent logEvent) + public static LogEventPropertyValue? GetRenderings(LogEvent logEvent, IFormatProvider? formatProvider) { List? elements = null; foreach (var token in logEvent.MessageTemplate.Tokens) { - if (token is PropertyToken pt && pt.Format != null) + if (token is PropertyToken {Format: { }} pt) { elements ??= new List(); var space = new StringWriter(); - // TODO #19: formatting is culture-specific. - pt.Render(logEvent.Properties, space); + pt.Render(logEvent.Properties, space, formatProvider); elements.Add(new ScalarValue(space.ToString())); } } diff --git a/src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs b/src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs index 1d5f4a8..d7bd435 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Linq/LinqExpressionCompiler.cs @@ -20,6 +20,8 @@ using Serilog.Events; using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Transformations; +using Serilog.Templates.Compilation; +using Serilog.Templates.Themes; using ConstantExpression = Serilog.Expressions.Ast.ConstantExpression; using Expression = Serilog.Expressions.Ast.Expression; using ParameterExpression = System.Linq.Expressions.ParameterExpression; @@ -31,6 +33,7 @@ namespace Serilog.Expressions.Compilation.Linq class LinqExpressionCompiler : SerilogExpressionTransformer { readonly NameResolver _nameResolver; + readonly IFormatProvider? _formatProvider; static readonly MethodInfo CollectSequenceElementsMethod = typeof(Intrinsics) .GetMethod(nameof(Intrinsics.CollectSequenceElements), BindingFlags.Static | BindingFlags.Public)!; @@ -70,15 +73,17 @@ class LinqExpressionCompiler : SerilogExpressionTransformer ParameterExpression Context { get; } = LX.Variable(typeof(EvaluationContext), "ctx"); - LinqExpressionCompiler(NameResolver nameResolver) + LinqExpressionCompiler(IFormatProvider? formatProvider, NameResolver nameResolver) { _nameResolver = nameResolver; + _formatProvider = formatProvider; } - public static Evaluatable Compile(Expression expression, NameResolver nameResolver) + public static Evaluatable Compile(Expression expression, IFormatProvider? formatProvider, + NameResolver nameResolver) { if (expression == null) throw new ArgumentNullException(nameof(expression)); - var compiler = new LinqExpressionCompiler(nameResolver); + var compiler = new LinqExpressionCompiler(formatProvider, nameResolver); var body = compiler.Transform(expression); return LX.Lambda(body, compiler.Context).Compile(); } @@ -93,7 +98,9 @@ protected override ExpressionBody Transform(CallExpression lx) if (!_nameResolver.TryResolveFunctionName(lx.OperatorName, out var m)) throw new ArgumentException($"The function name `{lx.OperatorName}` was not recognized."); - var parameterCount = m.GetParameters().Count(pi => pi.ParameterType == typeof(LogEventPropertyValue)); + var methodParameters = m.GetParameters(); + + var parameterCount = methodParameters.Count(pi => pi.ParameterType == typeof(LogEventPropertyValue)); if (parameterCount != lx.Operands.Length) throw new ArgumentException($"The function `{lx.OperatorName}` requires {parameterCount} arguments."); @@ -106,9 +113,15 @@ protected override ExpressionBody Transform(CallExpression lx) if (Operators.SameOperator(lx.OperatorName, Operators.RuntimeOpOr)) return CompileLogical(LX.OrElse, operands[0], operands[1]); - if (m.GetParameters().Any(pi => pi.ParameterType == typeof(StringComparison))) - operands.Insert(0, LX.Constant(lx.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); - + for (var i = 0; i < methodParameters.Length; ++i) + { + var pi = methodParameters[i]; + if (pi.ParameterType == typeof(StringComparison)) + operands.Insert(i, LX.Constant(lx.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); + else if (pi.ParameterType == typeof(IFormatProvider)) + operands.Insert(i, LX.Constant(_formatProvider, typeof(IFormatProvider))); + } + return LX.Call(m, operands); } @@ -138,10 +151,13 @@ protected override ExpressionBody Transform(AmbientNameExpression px) { if (px.IsBuiltIn) { + var formatter = new CompiledMessageToken(_formatProvider, null, TemplateTheme.None); + var formatProvider = _formatProvider; + return px.PropertyName switch { BuiltInProperty.Level => Splice(context => new ScalarValue(context.LogEvent.Level)), - BuiltInProperty.Message => Splice(context => new ScalarValue(Intrinsics.RenderMessage(context.LogEvent))), + BuiltInProperty.Message => Splice(context => new ScalarValue(Intrinsics.RenderMessage(formatter, context))), BuiltInProperty.Exception => Splice(context => context.LogEvent.Exception == null ? null : new ScalarValue(context.LogEvent.Exception)), BuiltInProperty.Timestamp => Splice(context => new ScalarValue(context.LogEvent.Timestamp)), @@ -149,7 +165,7 @@ protected override ExpressionBody Transform(AmbientNameExpression px) BuiltInProperty.Properties => Splice(context => new StructureValue(context.LogEvent.Properties.Select(kvp => new LogEventProperty(kvp.Key, kvp.Value)), null)), - BuiltInProperty.Renderings => Splice(context => Intrinsics.GetRenderings(context.LogEvent)), + BuiltInProperty.Renderings => Splice(context => Intrinsics.GetRenderings(context.LogEvent, formatProvider)), BuiltInProperty.EventId => Splice(context => new ScalarValue(EventIdHash.Compute(context.LogEvent.MessageTemplate.Text))), _ => LX.Constant(null, typeof(LogEventPropertyValue)) diff --git a/src/Serilog.Expressions/Expressions/Compilation/Linq/ParameterReplacementVisitor.cs b/src/Serilog.Expressions/Expressions/Compilation/Linq/ParameterReplacementVisitor.cs index 2438846..964e56d 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Linq/ParameterReplacementVisitor.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Linq/ParameterReplacementVisitor.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Linq; using System.Linq.Expressions; diff --git a/src/Serilog.Expressions/Expressions/Compilation/OrderedNameResolver.cs b/src/Serilog.Expressions/Expressions/Compilation/OrderedNameResolver.cs index 5983a3e..62b2b2b 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/OrderedNameResolver.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/OrderedNameResolver.cs @@ -1,4 +1,18 @@ -using System.Collections.Generic; +// 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 System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Pattern.cs b/src/Serilog.Expressions/Expressions/Compilation/Pattern.cs index d0ff8fa..ca250b9 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Pattern.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Pattern.cs @@ -1,3 +1,17 @@ +// 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 System.Diagnostics.CodeAnalysis; using Serilog.Events; using Serilog.Expressions.Ast; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Properties/PropertiesObjectAccessorTransformer.cs b/src/Serilog.Expressions/Expressions/Compilation/Properties/PropertiesObjectAccessorTransformer.cs index c20c2f7..c1180df 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Properties/PropertiesObjectAccessorTransformer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Properties/PropertiesObjectAccessorTransformer.cs @@ -1,4 +1,18 @@ -using Serilog.Expressions.Ast; +// 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.Ast; using Serilog.Expressions.Compilation.Transformations; namespace Serilog.Expressions.Compilation.Properties diff --git a/src/Serilog.Expressions/Expressions/Compilation/Text/LikeSyntaxTransformer.cs b/src/Serilog.Expressions/Expressions/Compilation/Text/LikeSyntaxTransformer.cs index e4a4463..2846138 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Text/LikeSyntaxTransformer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Text/LikeSyntaxTransformer.cs @@ -1,3 +1,17 @@ +// 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 System; using System.Text.RegularExpressions; using Serilog.Debugging; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Text/TextMatchingTransformer.cs b/src/Serilog.Expressions/Expressions/Compilation/Text/TextMatchingTransformer.cs index 9b3ec97..281506a 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Text/TextMatchingTransformer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Text/TextMatchingTransformer.cs @@ -1,3 +1,17 @@ +// 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 System; using System.Text.RegularExpressions; using Serilog.Debugging; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs b/src/Serilog.Expressions/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs index 1c03a4c..5bfcbf7 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Transformations/FilterExpressionTransformer`1.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.Expressions.Ast; namespace Serilog.Expressions.Compilation.Transformations diff --git a/src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs b/src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs index 82eb856..02e49c8 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Transformations/IdentityTransformer.cs @@ -1,4 +1,18 @@ -using System.Collections.Generic; +// 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 System.Collections.Generic; using Serilog.Expressions.Ast; namespace Serilog.Expressions.Compilation.Transformations diff --git a/src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs b/src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs index ba570aa..b68a2ee 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Transformations/NodeReplacer.cs @@ -1,4 +1,18 @@ -using Serilog.Expressions.Ast; +// 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.Ast; namespace Serilog.Expressions.Compilation.Transformations { diff --git a/src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs b/src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs index b199608..be7d224 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Variadics/VariadicCallRewriter.cs @@ -1,3 +1,17 @@ +// 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 System.Linq; using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Transformations; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs b/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs index 51a4ecd..0e79c25 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardComprehensionTransformer.cs @@ -1,4 +1,18 @@ -using System.Linq; +// 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 System.Linq; using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Transformations; diff --git a/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardSearch.cs b/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardSearch.cs index 555e025..ade7c6c 100644 --- a/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardSearch.cs +++ b/src/Serilog.Expressions/Expressions/Compilation/Wildcards/WildcardSearch.cs @@ -1,4 +1,18 @@ -using System.Linq; +// 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 System.Linq; using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation.Transformations; diff --git a/src/Serilog.Expressions/Expressions/Evaluatable.cs b/src/Serilog.Expressions/Expressions/Evaluatable.cs index 6c1e297..a133973 100644 --- a/src/Serilog.Expressions/Expressions/Evaluatable.cs +++ b/src/Serilog.Expressions/Expressions/Evaluatable.cs @@ -1,4 +1,18 @@ -using Serilog.Events; +// 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.Events; namespace Serilog.Expressions { diff --git a/src/Serilog.Expressions/Expressions/EvaluationContext.cs b/src/Serilog.Expressions/Expressions/EvaluationContext.cs index 2043d4f..01313c2 100644 --- a/src/Serilog.Expressions/Expressions/EvaluationContext.cs +++ b/src/Serilog.Expressions/Expressions/EvaluationContext.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.Events; using Serilog.Expressions.Runtime; diff --git a/src/Serilog.Expressions/Expressions/NameResolver.cs b/src/Serilog.Expressions/Expressions/NameResolver.cs index 2c4cf5c..c874be0 100644 --- a/src/Serilog.Expressions/Expressions/NameResolver.cs +++ b/src/Serilog.Expressions/Expressions/NameResolver.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Diagnostics.CodeAnalysis; using System.Reflection; using Serilog.Events; @@ -18,7 +32,8 @@ public abstract class NameResolver /// True if the name could be resolved; otherwise, false. /// The method implementing a function should be static, return , /// and accept parameters of type . If the ci modifier is supported, - /// a should be in the first argument position. + /// a should be included in the argument list. If the function is culture-specific, + /// an should be included in the argument list. public abstract bool TryResolveFunctionName(string name, [MaybeNullWhen(false)] out MethodInfo implementation); } } \ No newline at end of file diff --git a/src/Serilog.Expressions/Expressions/Operators.cs b/src/Serilog.Expressions/Expressions/Operators.cs index e90b495..6a4b72d 100644 --- a/src/Serilog.Expressions/Expressions/Operators.cs +++ b/src/Serilog.Expressions/Expressions/Operators.cs @@ -1,8 +1,22 @@ -using System; +// 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 System; using System.Collections.Generic; using Serilog.Expressions.Ast; -// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable InconsistentNaming, MemberCanBePrivate.Global namespace Serilog.Expressions { diff --git a/src/Serilog.Expressions/Expressions/Parsing/Combinators.cs b/src/Serilog.Expressions/Expressions/Parsing/Combinators.cs index e1291e8..da04606 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/Combinators.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/Combinators.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.ParserConstruction; using Serilog.ParserConstruction.Model; diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionKeyword.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionKeyword.cs index 372c5d3..e02bdd3 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionKeyword.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionKeyword.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; namespace Serilog.Expressions.Parsing { diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs index ede4508..df0398a 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionParser.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Diagnostics.CodeAnalysis; using Serilog.Expressions.Ast; diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTextParsers.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTextParsers.cs index 431eba7..98d8b7d 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTextParsers.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTextParsers.cs @@ -1,4 +1,18 @@ -using Serilog.ParserConstruction; +// 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.ParserConstruction; using Serilog.ParserConstruction.Model; using Serilog.ParserConstruction.Parsers; diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs index 0830352..93a7c34 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionToken.cs @@ -1,4 +1,18 @@ -using Serilog.ParserConstruction.Display; +// 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.ParserConstruction.Display; namespace Serilog.Expressions.Parsing { diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs index 3fbda4a..049d0bc 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenParsers.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Globalization; using System.Linq; using Serilog.Events; diff --git a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs index faca88d..f78db88 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ExpressionTokenizer.cs @@ -1,4 +1,18 @@ -using System.Collections.Generic; +// 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 System.Collections.Generic; using System.Linq; using Serilog.ParserConstruction; using Serilog.ParserConstruction.Model; diff --git a/src/Serilog.Expressions/Expressions/Parsing/ParserExtensions.cs b/src/Serilog.Expressions/Expressions/Parsing/ParserExtensions.cs index 8777811..1f9f2c1 100644 --- a/src/Serilog.Expressions/Expressions/Parsing/ParserExtensions.cs +++ b/src/Serilog.Expressions/Expressions/Parsing/ParserExtensions.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.ParserConstruction; using Serilog.ParserConstruction.Model; diff --git a/src/Serilog.Expressions/Expressions/Runtime/Coerce.cs b/src/Serilog.Expressions/Expressions/Runtime/Coerce.cs index 2fee8fe..0c0f181 100644 --- a/src/Serilog.Expressions/Expressions/Runtime/Coerce.cs +++ b/src/Serilog.Expressions/Expressions/Runtime/Coerce.cs @@ -1,3 +1,17 @@ +// 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 System; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/src/Serilog.Expressions/Expressions/Runtime/Locals.cs b/src/Serilog.Expressions/Expressions/Runtime/Locals.cs index f2f3b30..3143208 100644 --- a/src/Serilog.Expressions/Expressions/Runtime/Locals.cs +++ b/src/Serilog.Expressions/Expressions/Runtime/Locals.cs @@ -1,4 +1,18 @@ -using System.Diagnostics.CodeAnalysis; +// 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 System.Diagnostics.CodeAnalysis; using Serilog.Events; namespace Serilog.Expressions.Runtime diff --git a/src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs b/src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs index b2e2f95..18e10df 100644 --- a/src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs +++ b/src/Serilog.Expressions/Expressions/Runtime/RuntimeOperators.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Collections.Generic; using System.Linq; using Serilog.Events; @@ -409,18 +423,18 @@ public static LogEventPropertyValue TypeOf(LogEventPropertyValue? value) public static LogEventPropertyValue _Internal_IsNull(LogEventPropertyValue? value) { - return ScalarBoolean(value is null || value is ScalarValue sv && sv.Value == null); + return ScalarBoolean(value is null or ScalarValue {Value: null}); } public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? value) { - return ScalarBoolean(!(value is null || value is ScalarValue sv && sv.Value == null)); + return ScalarBoolean(value is not (null or ScalarValue {Value: null})); } // Ideally this will be compiled as a short-circuiting intrinsic public static LogEventPropertyValue? Coalesce(LogEventPropertyValue? v1, LogEventPropertyValue? v2) { - if (v1 is null || v1 is ScalarValue sv && sv.Value == null) + if (v1 is null or ScalarValue {Value: null}) return v2; return v1; @@ -468,7 +482,7 @@ public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? v return Coerce.IsTrue(condition) ? consequent : alternative; } - public static LogEventPropertyValue? ToString(LogEventPropertyValue? value, LogEventPropertyValue? format) + public static LogEventPropertyValue? ToString(IFormatProvider? formatProvider, LogEventPropertyValue? value, LogEventPropertyValue? format) { if (value is not ScalarValue sv || sv.Value == null || @@ -480,8 +494,7 @@ public static LogEventPropertyValue _Internal_IsNotNull(LogEventPropertyValue? v string? toString; if (sv.Value is IFormattable formattable) { - // TODO #19: formatting is culture-specific. - toString = formattable.ToString(fmt, null); + toString = formattable.ToString(fmt, formatProvider); } else { diff --git a/src/Serilog.Expressions/Expressions/SerilogExpression.cs b/src/Serilog.Expressions/Expressions/SerilogExpression.cs index 8de20b8..37b1de2 100644 --- a/src/Serilog.Expressions/Expressions/SerilogExpression.cs +++ b/src/Serilog.Expressions/Expressions/SerilogExpression.cs @@ -14,6 +14,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using Serilog.Expressions.Compilation; using Serilog.Expressions.Parsing; @@ -31,15 +32,17 @@ public static class SerilogExpression /// Create an evaluation function based on the provided expression. /// /// An expression. + /// Optionally, a format provider that will be used for culture-specific formatting; + /// by default, is used. /// Optionally, a - /// with which to resolve function names that appear in the template. + /// with which to resolve function names that appear in the template. /// A function that evaluates the expression in the context of a log event. - public static CompiledExpression Compile( - string expression, + public static CompiledExpression Compile(string expression, + IFormatProvider? formatProvider = null, NameResolver? nameResolver = null) { if (expression == null) throw new ArgumentNullException(nameof(expression)); - if (!TryCompileImpl(expression, nameResolver, out var filter, out var error)) + if (!TryCompileImpl(expression, formatProvider, nameResolver, out var filter, out var error)) throw new ArgumentException(error); return filter; @@ -60,33 +63,35 @@ public static bool TryCompile( [MaybeNullWhen(true)] out string error) { if (expression == null) throw new ArgumentNullException(nameof(expression)); - return TryCompileImpl(expression, null, out result, out error); + return TryCompileImpl(expression, null, null, out result, out error); } /// /// Create an evaluation function based on the provided expression. /// /// An expression. + /// Optionally, a format provider that will be used for culture-specific formatting; + /// by default, is used. + /// A + /// with which to resolve function names that appear in the template. /// A function that evaluates the expression in the context of a log event. /// The reported error, if compilation was unsuccessful. - /// A - /// with which to resolve function names that appear in the template. /// True if the function could be created; otherwise, false. /// Regular expression syntax errors currently generate exceptions instead of producing friendly /// errors. - public static bool TryCompile( - string expression, + public static bool TryCompile(string expression, + IFormatProvider? formatProvider, NameResolver nameResolver, [MaybeNullWhen(false)] out CompiledExpression result, [MaybeNullWhen(true)] out string error) { if (expression == null) throw new ArgumentNullException(nameof(expression)); if (nameResolver == null) throw new ArgumentNullException(nameof(nameResolver)); - return TryCompileImpl(expression, nameResolver, out result, out error); + return TryCompileImpl(expression, formatProvider, nameResolver, out result, out error); } - static bool TryCompileImpl( - string expression, + static bool TryCompileImpl(string expression, + IFormatProvider? formatProvider, NameResolver? nameResolver, [MaybeNullWhen(false)] out CompiledExpression result, [MaybeNullWhen(true)] out string error) @@ -98,7 +103,7 @@ static bool TryCompileImpl( return false; } - var evaluate = ExpressionCompiler.Compile(root, DefaultFunctionNameResolver.Build(nameResolver)); + var evaluate = ExpressionCompiler.Compile(root, formatProvider, DefaultFunctionNameResolver.Build(nameResolver)); result = evt => evaluate(new EvaluationContext(evt)); error = null; return true; diff --git a/src/Serilog.Expressions/Expressions/StaticMemberNameResolver.cs b/src/Serilog.Expressions/Expressions/StaticMemberNameResolver.cs index d28c01c..0806ac9 100644 --- a/src/Serilog.Expressions/Expressions/StaticMemberNameResolver.cs +++ b/src/Serilog.Expressions/Expressions/StaticMemberNameResolver.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/src/Serilog.Expressions/LoggerFilterConfigurationExtensions.cs b/src/Serilog.Expressions/LoggerFilterConfigurationExtensions.cs index 3356132..87c0ae0 100644 --- a/src/Serilog.Expressions/LoggerFilterConfigurationExtensions.cs +++ b/src/Serilog.Expressions/LoggerFilterConfigurationExtensions.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.Configuration; using Serilog.Expressions; using Serilog.Expressions.Runtime; diff --git a/src/Serilog.Expressions/Pipeline/ComputedPropertyEnricher.cs b/src/Serilog.Expressions/Pipeline/ComputedPropertyEnricher.cs index c85d676..810bff3 100644 --- a/src/Serilog.Expressions/Pipeline/ComputedPropertyEnricher.cs +++ b/src/Serilog.Expressions/Pipeline/ComputedPropertyEnricher.cs @@ -1,3 +1,17 @@ +// 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 System; using Serilog.Core; using Serilog.Events; diff --git a/src/Serilog.Expressions/Templates/Ast/Conditional.cs b/src/Serilog.Expressions/Templates/Ast/Conditional.cs index 3bf74e4..a861922 100644 --- a/src/Serilog.Expressions/Templates/Ast/Conditional.cs +++ b/src/Serilog.Expressions/Templates/Ast/Conditional.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.Expressions.Ast; namespace Serilog.Templates.Ast diff --git a/src/Serilog.Expressions/Templates/Ast/FormattedExpression.cs b/src/Serilog.Expressions/Templates/Ast/FormattedExpression.cs index ba43b80..caad317 100644 --- a/src/Serilog.Expressions/Templates/Ast/FormattedExpression.cs +++ b/src/Serilog.Expressions/Templates/Ast/FormattedExpression.cs @@ -1,3 +1,17 @@ +// 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 System; using Serilog.Expressions.Ast; using Serilog.Parsing; diff --git a/src/Serilog.Expressions/Templates/Ast/LiteralText.cs b/src/Serilog.Expressions/Templates/Ast/LiteralText.cs index 97ad716..17e41f5 100644 --- a/src/Serilog.Expressions/Templates/Ast/LiteralText.cs +++ b/src/Serilog.Expressions/Templates/Ast/LiteralText.cs @@ -1,3 +1,17 @@ +// 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 System; namespace Serilog.Templates.Ast diff --git a/src/Serilog.Expressions/Templates/Ast/Repetition.cs b/src/Serilog.Expressions/Templates/Ast/Repetition.cs index 2c29cd7..e5ee594 100644 --- a/src/Serilog.Expressions/Templates/Ast/Repetition.cs +++ b/src/Serilog.Expressions/Templates/Ast/Repetition.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using Serilog.Expressions.Ast; namespace Serilog.Templates.Ast diff --git a/src/Serilog.Expressions/Templates/Ast/Template.cs b/src/Serilog.Expressions/Templates/Ast/Template.cs index c81f37d..8fc76e6 100644 --- a/src/Serilog.Expressions/Templates/Ast/Template.cs +++ b/src/Serilog.Expressions/Templates/Ast/Template.cs @@ -1,3 +1,17 @@ +// 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. + namespace Serilog.Templates.Ast { abstract class Template diff --git a/src/Serilog.Expressions/Templates/Ast/TemplateBlock.cs b/src/Serilog.Expressions/Templates/Ast/TemplateBlock.cs index 867ae7b..d90e597 100644 --- a/src/Serilog.Expressions/Templates/Ast/TemplateBlock.cs +++ b/src/Serilog.Expressions/Templates/Ast/TemplateBlock.cs @@ -1,3 +1,17 @@ +// 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 System; namespace Serilog.Templates.Ast diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledConditional.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledConditional.cs index 6703408..795ef29 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledConditional.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledConditional.cs @@ -1,6 +1,19 @@ -using System; +// 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 System; using System.IO; -using Serilog.Events; using Serilog.Expressions; namespace Serilog.Templates.Compilation @@ -18,12 +31,12 @@ public CompiledConditional(Evaluatable condition, CompiledTemplate consequent, C _alternative = alternative; } - public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + public override void Evaluate(EvaluationContext ctx, TextWriter output) { if (ExpressionResult.IsTrue(_condition.Invoke(ctx))) - _consequent.Evaluate(ctx, output, formatProvider); + _consequent.Evaluate(ctx, output); else - _alternative?.Evaluate(ctx, output, formatProvider); + _alternative?.Evaluate(ctx, output); } } } diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledExceptionToken.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledExceptionToken.cs new file mode 100644 index 0000000..8e932d3 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledExceptionToken.cs @@ -0,0 +1,51 @@ +// 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 System.IO; +using Serilog.Expressions; +using Serilog.Templates.Themes; + +namespace Serilog.Templates.Compilation +{ + class CompiledExceptionToken : CompiledTemplate + { + const string StackFrameLinePrefix = " "; + + readonly Style _text, _secondaryText; + + public CompiledExceptionToken(TemplateTheme theme) + { + _text = theme.GetStyle(TemplateThemeStyle.Text); + _secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText); + } + + public override void Evaluate(EvaluationContext ctx, TextWriter output) + { + // Padding and alignment are not applied by this renderer. + + if (ctx.LogEvent.Exception is null) + return; + + var lines = new StringReader(ctx.LogEvent.Exception.ToString()); + string? nextLine; + while ((nextLine = lines.ReadLine()) != null) + { + var style = nextLine.StartsWith(StackFrameLinePrefix) ? _secondaryText : _text; + var _ = 0; + using (style.Set(output, ref _)) + output.WriteLine(nextLine); + } + } + } +} diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledFormattedExpression.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledFormattedExpression.cs index 53c9c57..02dcfc2 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledFormattedExpression.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledFormattedExpression.cs @@ -1,43 +1,63 @@ +// 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 System; using System.IO; using Serilog.Events; using Serilog.Expressions; -using Serilog.Formatting.Json; using Serilog.Parsing; using Serilog.Templates.Rendering; +using Serilog.Templates.Themes; namespace Serilog.Templates.Compilation { class CompiledFormattedExpression : CompiledTemplate { - static readonly JsonValueFormatter JsonFormatter = new JsonValueFormatter("$type"); - + readonly ThemedJsonValueFormatter _jsonFormatter; readonly Evaluatable _expression; readonly string? _format; readonly Alignment? _alignment; + readonly IFormatProvider? _formatProvider; + readonly Style _secondaryText; - public CompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment) + public CompiledFormattedExpression(Evaluatable expression, string? format, Alignment? alignment, IFormatProvider? formatProvider, TemplateTheme theme) { _expression = expression ?? throw new ArgumentNullException(nameof(expression)); _format = format; _alignment = alignment; + _formatProvider = formatProvider; + _secondaryText = theme.GetStyle(TemplateThemeStyle.SecondaryText); + _jsonFormatter = new ThemedJsonValueFormatter(theme); } - public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + public override void Evaluate(EvaluationContext ctx, TextWriter output) { + var invisibleCharacterCount = 0; + if (_alignment == null) { - EvaluateUnaligned(ctx, output, formatProvider); + EvaluateUnaligned(ctx, output, _formatProvider, ref invisibleCharacterCount); } else { var writer = new StringWriter(); - EvaluateUnaligned(ctx, writer, formatProvider); - Padding.Apply(output, writer.ToString(), _alignment.Value); + EvaluateUnaligned(ctx, writer, _formatProvider, ref invisibleCharacterCount); + Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount)); } } - void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider, ref int invisibleCharacterCount) { var value = _expression(ctx); if (value == null) @@ -48,17 +68,16 @@ void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, IFormatProvider if (scalar.Value is null) return; // Null is empty - if (scalar.Value is LogEventLevel level) - // This would be better implemented using CompiledLevelToken : CompiledTemplate. - output.Write(LevelRenderer.GetLevelMoniker(level, _format)); - else if (scalar.Value is IFormattable fmt) + using var style = _secondaryText.Set(output, ref invisibleCharacterCount); + + if (scalar.Value is IFormattable fmt) output.Write(fmt.ToString(_format, formatProvider)); else output.Write(scalar.Value.ToString()); } else { - JsonFormatter.Format(value, output); + invisibleCharacterCount += _jsonFormatter.Format(value, output); } } } diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledLevelToken.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledLevelToken.cs new file mode 100644 index 0000000..0fb1059 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledLevelToken.cs @@ -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 System.IO; +using Serilog.Expressions; +using Serilog.Parsing; +using Serilog.Templates.Rendering; +using Serilog.Templates.Themes; + +namespace Serilog.Templates.Compilation +{ + class CompiledLevelToken : CompiledTemplate + { + readonly string? _format; + readonly Alignment? _alignment; + readonly Style[] _levelStyles; + + public CompiledLevelToken(string? format, Alignment? alignment, TemplateTheme theme) + { + _format = format; + _alignment = alignment; + _levelStyles = new[] + { + theme.GetStyle(TemplateThemeStyle.LevelVerbose), + theme.GetStyle(TemplateThemeStyle.LevelDebug), + theme.GetStyle(TemplateThemeStyle.LevelInformation), + theme.GetStyle(TemplateThemeStyle.LevelWarning), + theme.GetStyle(TemplateThemeStyle.LevelError), + theme.GetStyle(TemplateThemeStyle.LevelFatal), + }; + } + + public override void Evaluate(EvaluationContext ctx, TextWriter output) + { + var invisibleCharacterCount = 0; + + if (_alignment == null) + { + EvaluateUnaligned(ctx, output, ref invisibleCharacterCount); + } + else + { + var writer = new StringWriter(); + EvaluateUnaligned(ctx, writer, ref invisibleCharacterCount); + Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount)); + } + } + + void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, ref int invisibleCharacterCount) + { + var levelIndex = (int) ctx.LogEvent.Level; + if (levelIndex < 0 || levelIndex >= _levelStyles.Length) + return; + + using var _ = _levelStyles[levelIndex].Set(output, ref invisibleCharacterCount); + output.Write(LevelRenderer.GetLevelMoniker(ctx.LogEvent.Level, _format)); + } + } +} diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledLiteralText.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledLiteralText.cs index 0bcb0ba..582b715 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledLiteralText.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledLiteralText.cs @@ -1,22 +1,40 @@ +// 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 System; using System.IO; -using Serilog.Events; using Serilog.Expressions; +using Serilog.Templates.Themes; namespace Serilog.Templates.Compilation { class CompiledLiteralText : CompiledTemplate { readonly string _text; + readonly Style _style; - public CompiledLiteralText(string text) + public CompiledLiteralText(string text, TemplateTheme theme) { _text = text ?? throw new ArgumentNullException(nameof(text)); + _style = theme.GetStyle(TemplateThemeStyle.TertiaryText); } - public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + public override void Evaluate(EvaluationContext ctx, TextWriter output) { - output.Write(_text); + var _ = 0; + using (_style.Set(output, ref _)) + output.Write(_text); } } -} \ No newline at end of file +} diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledMessageToken.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledMessageToken.cs new file mode 100644 index 0000000..836d7dd --- /dev/null +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledMessageToken.cs @@ -0,0 +1,184 @@ +// 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 System; +using System.Collections.Generic; +using System.IO; +using Serilog.Events; +using Serilog.Expressions; +using Serilog.Parsing; +using Serilog.Templates.Rendering; +using Serilog.Templates.Themes; + +namespace Serilog.Templates.Compilation +{ + class CompiledMessageToken : CompiledTemplate + { + readonly IFormatProvider? _formatProvider; + readonly Alignment? _alignment; + readonly Style _text, _invalid, _null, _bool, _string, _num, _scalar; + readonly ThemedJsonValueFormatter _jsonFormatter; + + public CompiledMessageToken(IFormatProvider? formatProvider, Alignment? alignment, TemplateTheme theme) + { + _formatProvider = formatProvider; + _alignment = alignment; + _text = theme.GetStyle(TemplateThemeStyle.Text); + _null = theme.GetStyle(TemplateThemeStyle.Null); + _bool = theme.GetStyle(TemplateThemeStyle.Boolean); + _num = theme.GetStyle(TemplateThemeStyle.Number); + _string = theme.GetStyle(TemplateThemeStyle.String); + _scalar = theme.GetStyle(TemplateThemeStyle.Scalar); + _invalid = theme.GetStyle(TemplateThemeStyle.Invalid); + _jsonFormatter = new ThemedJsonValueFormatter(theme); + } + + public override void Evaluate(EvaluationContext ctx, TextWriter output) + { + var invisibleCharacterCount = 0; + + if (_alignment == null) + { + EvaluateUnaligned(ctx, output, ref invisibleCharacterCount); + } + else + { + var writer = new StringWriter(); + EvaluateUnaligned(ctx, writer, ref invisibleCharacterCount); + Padding.Apply(output, writer.ToString(), _alignment.Value.Widen(invisibleCharacterCount)); + } + } + + void EvaluateUnaligned(EvaluationContext ctx, TextWriter output, ref int invisibleCharacterCount) + { + foreach (var token in ctx.LogEvent.MessageTemplate.Tokens) + { + switch (token) + { + case TextToken tt: + { + using var _ = _text.Set(output, ref invisibleCharacterCount); + output.Write(tt.Text); + break; + } + case PropertyToken pt: + { + EvaluateProperty(ctx.LogEvent.Properties, pt, output, ref invisibleCharacterCount); + break; + } + default: + { + output.Write(token); + break; + } + } + } + } + + void EvaluateProperty(IReadOnlyDictionary properties, PropertyToken pt, TextWriter output, ref int invisibleCharacterCount) + { + if (!properties.TryGetValue(pt.PropertyName, out var value)) + { + using var _ = _invalid.Set(output, ref invisibleCharacterCount); + output.Write(pt.ToString()); + return; + } + + if (pt.Alignment is null) + { + EvaluatePropertyUnaligned(value, output, pt.Format, ref invisibleCharacterCount); + return; + } + + var buffer = new StringWriter(); + var resultInvisibleCharacters = 0; + + EvaluatePropertyUnaligned(value, buffer, pt.Format, ref resultInvisibleCharacters); + + var result = buffer.ToString(); + invisibleCharacterCount += resultInvisibleCharacters; + + if (result.Length - resultInvisibleCharacters >= pt.Alignment.Value.Width) + output.Write(result); + else + Padding.Apply(output, result, pt.Alignment.Value.Widen(resultInvisibleCharacters)); + } + + void EvaluatePropertyUnaligned(LogEventPropertyValue propertyValue, TextWriter output, string? format, ref int invisibleCharacterCount) + { + if (propertyValue is not ScalarValue scalar) + { + invisibleCharacterCount += _jsonFormatter.Format(propertyValue, output); + return; + } + + var value = scalar.Value; + + if (value == null) + { + using (_null.Set(output, ref invisibleCharacterCount)) + output.Write("null"); + return; + } + + if (value is string str) + { + using (_string.Set(output, ref invisibleCharacterCount)) + output.Write(str); + return; + } + + if (value is ValueType) + { + if (value is int or uint or long or ulong or decimal or byte or sbyte or short or ushort) + { + using (_num.Set(output, ref invisibleCharacterCount)) + output.Write(((IFormattable)value).ToString(format, _formatProvider)); + return; + } + + if (value is double d) + { + using (_num.Set(output, ref invisibleCharacterCount)) + output.Write(d.ToString(format, _formatProvider)); + return; + } + + if (value is float f) + { + using (_num.Set(output, ref invisibleCharacterCount)) + output.Write(f.ToString(format, _formatProvider)); + return; + } + + if (value is bool b) + { + using (_bool.Set(output, ref invisibleCharacterCount)) + output.Write(b); + return; + } + } + + if (value is IFormattable formattable) + { + using (_scalar.Set(output, ref invisibleCharacterCount)) + output.Write(formattable.ToString(format, _formatProvider)); + return; + } + + using (_scalar.Set(output, ref invisibleCharacterCount)) + output.Write(value); + } + } +} diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledRepetition.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledRepetition.cs index b3e4aad..4ca1e40 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledRepetition.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledRepetition.cs @@ -1,4 +1,17 @@ -using System; +// 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 System.IO; using Serilog.Events; using Serilog.Expressions; @@ -31,13 +44,13 @@ public CompiledRepetition( _alternative = alternative; } - public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + public override void Evaluate(EvaluationContext ctx, TextWriter output) { var enumerable = _enumerable(ctx); if (enumerable == null || enumerable is ScalarValue) { - _alternative?.Evaluate(ctx, output, formatProvider); + _alternative?.Evaluate(ctx, output); return; } @@ -45,7 +58,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP { if (sv.Elements.Count == 0) { - _alternative?.Evaluate(ctx, output, formatProvider); + _alternative?.Evaluate(ctx, output); return; } @@ -58,13 +71,13 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP if (first) first = false; else - _delimiter?.Evaluate(ctx, output, formatProvider); + _delimiter?.Evaluate(ctx, output); var local = _keyOrElementName != null ? new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, element)) : ctx; - _body.Evaluate(local, output, formatProvider); + _body.Evaluate(local, output); } return; @@ -74,7 +87,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP { if (structure.Properties.Count == 0) { - _alternative?.Evaluate(ctx, output, formatProvider); + _alternative?.Evaluate(ctx, output); return; } @@ -84,7 +97,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP if (first) first = false; else - _delimiter?.Evaluate(ctx, output, formatProvider); + _delimiter?.Evaluate(ctx, output); var local = _keyOrElementName != null ? new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, new ScalarValue(member.Name))) @@ -94,7 +107,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP ? new EvaluationContext(local.LogEvent, Locals.Set(local.Locals, _valueName, member.Value)) : local; - _body.Evaluate(local, output, formatProvider); + _body.Evaluate(local, output); } } @@ -102,7 +115,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP { if (dict.Elements.Count == 0) { - _alternative?.Evaluate(ctx, output, formatProvider); + _alternative?.Evaluate(ctx, output); return; } @@ -112,7 +125,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP if (first) first = false; else - _delimiter?.Evaluate(ctx, output, formatProvider); + _delimiter?.Evaluate(ctx, output); var local = _keyOrElementName != null ? new EvaluationContext(ctx.LogEvent, Locals.Set(ctx.Locals, _keyOrElementName, element.Key)) @@ -122,7 +135,7 @@ public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatP ? new EvaluationContext(local.LogEvent, Locals.Set(local.Locals, _valueName, element.Value)) : local; - _body.Evaluate(local, output, formatProvider); + _body.Evaluate(local, output); } } diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledTemplate.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledTemplate.cs index 9ff6222..a44889f 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledTemplate.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledTemplate.cs @@ -1,12 +1,24 @@ -using System; +// 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 System.IO; -using Serilog.Events; using Serilog.Expressions; namespace Serilog.Templates.Compilation { abstract class CompiledTemplate { - public abstract void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider); + public abstract void Evaluate(EvaluationContext ctx, TextWriter output); } -} \ No newline at end of file +} diff --git a/src/Serilog.Expressions/Templates/Compilation/CompiledTemplateBlock.cs b/src/Serilog.Expressions/Templates/Compilation/CompiledTemplateBlock.cs index 3b9832b..b335ded 100644 --- a/src/Serilog.Expressions/Templates/Compilation/CompiledTemplateBlock.cs +++ b/src/Serilog.Expressions/Templates/Compilation/CompiledTemplateBlock.cs @@ -1,6 +1,19 @@ +// 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 System; using System.IO; -using Serilog.Events; using Serilog.Expressions; namespace Serilog.Templates.Compilation @@ -14,10 +27,10 @@ public CompiledTemplateBlock(CompiledTemplate[] elements) _elements = elements ?? throw new ArgumentNullException(nameof(elements)); } - public override void Evaluate(EvaluationContext ctx, TextWriter output, IFormatProvider? formatProvider) + public override void Evaluate(EvaluationContext ctx, TextWriter output) { foreach (var element in _elements) - element.Evaluate(ctx, output, formatProvider); + element.Evaluate(ctx, output); } } -} \ No newline at end of file +} diff --git a/src/Serilog.Expressions/Templates/Compilation/NameResolution/ExpressionLocalNameBinder.cs b/src/Serilog.Expressions/Templates/Compilation/NameResolution/ExpressionLocalNameBinder.cs index 3894076..8d407f2 100644 --- a/src/Serilog.Expressions/Templates/Compilation/NameResolution/ExpressionLocalNameBinder.cs +++ b/src/Serilog.Expressions/Templates/Compilation/NameResolution/ExpressionLocalNameBinder.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Collections.Generic; using System.Linq; using Serilog.Expressions.Ast; diff --git a/src/Serilog.Expressions/Templates/Compilation/NameResolution/TemplateLocalNameResolver.cs b/src/Serilog.Expressions/Templates/Compilation/NameResolution/TemplateLocalNameResolver.cs index 62222a9..3b947e8 100644 --- a/src/Serilog.Expressions/Templates/Compilation/NameResolution/TemplateLocalNameResolver.cs +++ b/src/Serilog.Expressions/Templates/Compilation/NameResolution/TemplateLocalNameResolver.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Collections.Generic; using System.Linq; using Serilog.Templates.Ast; diff --git a/src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs b/src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs index 87980a7..70a93e3 100644 --- a/src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs +++ b/src/Serilog.Expressions/Templates/Compilation/TemplateCompiler.cs @@ -1,33 +1,63 @@ +// 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 System; using System.Linq; using Serilog.Expressions; +using Serilog.Expressions.Ast; using Serilog.Expressions.Compilation; using Serilog.Templates.Ast; +using Serilog.Templates.Themes; namespace Serilog.Templates.Compilation { static class TemplateCompiler { - public static CompiledTemplate Compile(Template template, NameResolver nameResolver) + public static CompiledTemplate Compile(Template template, + IFormatProvider? formatProvider, NameResolver nameResolver, + TemplateTheme theme) { - if (template == null) throw new ArgumentNullException(nameof(template)); return template switch { - LiteralText text => new CompiledLiteralText(text.Text), + LiteralText text => new CompiledLiteralText(text.Text, theme), + FormattedExpression { Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Level} } level => new CompiledLevelToken( + level.Format, level.Alignment, theme), + FormattedExpression + { + Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Exception }, + Alignment: null, + Format: null + } => new CompiledExceptionToken(theme), + FormattedExpression + { + Expression: AmbientNameExpression { IsBuiltIn: true, PropertyName: BuiltInProperty.Message }, + Format: null + } message => new CompiledMessageToken(formatProvider, message.Alignment, theme), FormattedExpression expression => new CompiledFormattedExpression( - ExpressionCompiler.Compile(expression.Expression, nameResolver), expression.Format, expression.Alignment), - TemplateBlock block => new CompiledTemplateBlock(block.Elements.Select(e => Compile(e, nameResolver)).ToArray()), + 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)).ToArray()), Conditional conditional => new CompiledConditional( - ExpressionCompiler.Compile(conditional.Condition, nameResolver), - Compile(conditional.Consequent, nameResolver), - conditional.Alternative == null ? null : Compile(conditional.Alternative, nameResolver)), + ExpressionCompiler.Compile(conditional.Condition, formatProvider, nameResolver), + Compile(conditional.Consequent, formatProvider, nameResolver, theme), + conditional.Alternative == null ? null : Compile(conditional.Alternative, formatProvider, nameResolver, theme)), Repetition repetition => new CompiledRepetition( - ExpressionCompiler.Compile(repetition.Enumerable, nameResolver), + ExpressionCompiler.Compile(repetition.Enumerable, formatProvider, nameResolver), repetition.BindingNames.Length > 0 ? repetition.BindingNames[0] : null, repetition.BindingNames.Length > 1 ? repetition.BindingNames[1] : null, - Compile(repetition.Body, nameResolver), - repetition.Delimiter == null ? null : Compile(repetition.Delimiter, nameResolver), - repetition.Alternative == null ? null : Compile(repetition.Alternative, nameResolver)), + Compile(repetition.Body, formatProvider, nameResolver, theme), + repetition.Delimiter == null ? null : Compile(repetition.Delimiter, formatProvider, nameResolver, theme), + repetition.Alternative == null ? null : Compile(repetition.Alternative, formatProvider, nameResolver, theme)), _ => throw new NotSupportedException() }; } diff --git a/src/Serilog.Expressions/Templates/ExpressionTemplate.cs b/src/Serilog.Expressions/Templates/ExpressionTemplate.cs index 60f0305..28afab9 100644 --- a/src/Serilog.Expressions/Templates/ExpressionTemplate.cs +++ b/src/Serilog.Expressions/Templates/ExpressionTemplate.cs @@ -1,3 +1,17 @@ +// 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 System; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -8,6 +22,7 @@ using Serilog.Templates.Compilation; using Serilog.Templates.Compilation.NameResolution; using Serilog.Templates.Parsing; +using Serilog.Templates.Themes; namespace Serilog.Templates { @@ -16,9 +31,8 @@ namespace Serilog.Templates /// public class ExpressionTemplate : ITextFormatter { - readonly IFormatProvider? _formatProvider; readonly CompiledTemplate _compiled; - + /// /// Construct an . /// @@ -32,7 +46,7 @@ public static bool TryParse( [MaybeNullWhen(true)] out string error) { if (template == null) throw new ArgumentNullException(nameof(template)); - return TryParse(template, null, null, out result, out error); + return TryParse(template, null, null, null, out result, out error); } /// @@ -41,6 +55,7 @@ public static bool TryParse( /// The template text. /// Optionally, an to use when formatting /// embedded values. + /// Optionally, an ANSI theme to apply to the template output. /// The parsed template, if successful. /// A description of the error, if unsuccessful. /// Optionally, a @@ -50,6 +65,7 @@ public static bool TryParse( string template, IFormatProvider? formatProvider, NameResolver? nameResolver, + TemplateTheme? theme, [MaybeNullWhen(false)] out ExpressionTemplate result, [MaybeNullWhen(true)] out string error) { @@ -64,14 +80,18 @@ public static bool TryParse( var planned = TemplateLocalNameBinder.BindLocalValueNames(parsed); - result = new ExpressionTemplate(TemplateCompiler.Compile(planned, DefaultFunctionNameResolver.Build(nameResolver)), formatProvider); + result = new ExpressionTemplate( + TemplateCompiler.Compile( + planned, + formatProvider, DefaultFunctionNameResolver.Build(nameResolver), + theme ?? TemplateTheme.None)); + return true; } - ExpressionTemplate(CompiledTemplate compiled, IFormatProvider? formatProvider) + ExpressionTemplate(CompiledTemplate compiled) { _compiled = compiled; - _formatProvider = formatProvider; } /// @@ -82,10 +102,12 @@ public static bool TryParse( /// embedded values. /// Optionally, a /// with which to resolve function names that appear in the template. + /// Optionally, an ANSI theme to apply to the template output. public ExpressionTemplate( string template, IFormatProvider? formatProvider = null, - NameResolver? nameResolver = null) + NameResolver? nameResolver = null, + TemplateTheme? theme = null) { if (template == null) throw new ArgumentNullException(nameof(template)); @@ -95,14 +117,15 @@ public ExpressionTemplate( var planned = TemplateLocalNameBinder.BindLocalValueNames(parsed); - _compiled = TemplateCompiler.Compile(planned, DefaultFunctionNameResolver.Build(nameResolver)); - _formatProvider = formatProvider; + _compiled = TemplateCompiler.Compile( + planned, + formatProvider, DefaultFunctionNameResolver.Build(nameResolver), theme ?? TemplateTheme.None); } /// public void Format(LogEvent logEvent, TextWriter output) { - _compiled.Evaluate(new EvaluationContext(logEvent), output, _formatProvider); + _compiled.Evaluate(new EvaluationContext(logEvent), output); } } } diff --git a/src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs b/src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs index b8c9814..296a410 100644 --- a/src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs +++ b/src/Serilog.Expressions/Templates/Parsing/TemplateParser.cs @@ -1,4 +1,18 @@ -using System; +// 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 System; using System.Diagnostics.CodeAnalysis; using Serilog.Templates.Ast; @@ -6,8 +20,8 @@ namespace Serilog.Templates.Parsing { class TemplateParser { - readonly TemplateTokenizer _tokenizer = new TemplateTokenizer(); - readonly TemplateTokenParsers _templateTokenParsers = new TemplateTokenParsers(); + readonly TemplateTokenizer _tokenizer = new(); + readonly TemplateTokenParsers _templateTokenParsers = new(); public bool TryParse( string template, diff --git a/src/Serilog.Expressions/Templates/Parsing/TemplateTokenParsers.cs b/src/Serilog.Expressions/Templates/Parsing/TemplateTokenParsers.cs index f8dc970..0fd2c58 100644 --- a/src/Serilog.Expressions/Templates/Parsing/TemplateTokenParsers.cs +++ b/src/Serilog.Expressions/Templates/Parsing/TemplateTokenParsers.cs @@ -1,4 +1,18 @@ -using Serilog.Expressions.Ast; +// 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.Ast; using Serilog.Expressions.Parsing; using Serilog.Parsing; using Serilog.ParserConstruction; @@ -33,7 +47,7 @@ from width in Token.EqualTo(Number).Apply(Numerics.NaturalUInt32) var hole = from _ in Token.EqualTo(LBrace) from expr in ExpressionTokenParsers.Expr - from align in alignment.OptionalOrDefault() + from align in alignment.Select(a => (Alignment?)a).OptionalOrDefault() from fmt in format.OptionalOrDefault() from __ in Token.EqualTo(RBrace) select (Template) new FormattedExpression(expr, fmt, align); @@ -79,7 +93,7 @@ from end in Directive(false, End) var eachDirective = Token.EqualTo(LBraceHash) .IgnoreThen(Token.EqualTo(Each)).Try() - .IgnoreThen(Token.EqualTo(ExpressionToken.Identifier) + .IgnoreThen(Token.EqualTo(Identifier) .Select(i => i.ToStringValue()) .AtLeastOnceDelimitedBy(Token.EqualTo(Comma))) .Then(bindings => Token.EqualTo(In).Value(bindings)) diff --git a/src/Serilog.Expressions/Templates/Parsing/TemplateTokenizer.cs b/src/Serilog.Expressions/Templates/Parsing/TemplateTokenizer.cs index 4c639af..283f262 100644 --- a/src/Serilog.Expressions/Templates/Parsing/TemplateTokenizer.cs +++ b/src/Serilog.Expressions/Templates/Parsing/TemplateTokenizer.cs @@ -1,4 +1,18 @@ -using System.Collections.Generic; +// 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 System.Collections.Generic; using Serilog.Expressions.Parsing; using Serilog.ParserConstruction; using Serilog.ParserConstruction.Model; diff --git a/src/Serilog.Expressions/Templates/Rendering/AlignmentExtensions.cs b/src/Serilog.Expressions/Templates/Rendering/AlignmentExtensions.cs new file mode 100644 index 0000000..7a6a5e5 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Rendering/AlignmentExtensions.cs @@ -0,0 +1,26 @@ +// 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.Parsing; + +namespace Serilog.Templates.Rendering +{ + static class AlignmentExtensions + { + public static Alignment Widen(this Alignment alignment, int amount) + { + return new(alignment.Direction, alignment.Width + amount); + } + } +} diff --git a/src/Serilog.Expressions/Templates/Themes/Style.cs b/src/Serilog.Expressions/Templates/Themes/Style.cs new file mode 100644 index 0000000..df66777 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/Style.cs @@ -0,0 +1,47 @@ +// 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 System.IO; + +namespace Serilog.Templates.Themes +{ + readonly struct Style + { + readonly string? _ansiStyle; + + public Style(string ansiStyle) + { + _ansiStyle = ansiStyle; + } + + internal StyleReset Set(TextWriter output, ref int invisibleCharacterCount) + { + if (_ansiStyle != null) + { + output.Write(_ansiStyle); + invisibleCharacterCount += _ansiStyle.Length; + invisibleCharacterCount += StyleReset.ResetCharCount; + + return new StyleReset(output); + } + + return default; + } + + public string? GetAnsiStyle() + { + return _ansiStyle; + } + } +} diff --git a/src/Serilog.Expressions/Templates/Themes/StyleReset.cs b/src/Serilog.Expressions/Templates/Themes/StyleReset.cs new file mode 100644 index 0000000..f6f096a --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/StyleReset.cs @@ -0,0 +1,37 @@ +// 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 System; +using System.IO; + +namespace Serilog.Templates.Themes +{ + readonly struct StyleReset : IDisposable + { + const string AnsiStyleResetSequence = "\x1b[0m"; + public const int ResetCharCount = 4; + + readonly TextWriter? _output; + + public StyleReset(TextWriter output) + { + _output = output; + } + + public void Dispose() + { + _output?.Write(AnsiStyleResetSequence); + } + } +} diff --git a/src/Serilog.Expressions/Templates/Themes/TemplateTheme.cs b/src/Serilog.Expressions/Templates/Themes/TemplateTheme.cs new file mode 100644 index 0000000..3a8fccb --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/TemplateTheme.cs @@ -0,0 +1,79 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; + +namespace Serilog.Templates.Themes +{ + /// + /// A template theme using the ANSI terminal escape sequences. + /// + public class TemplateTheme + { + /// + /// A 256-color theme along the lines of Visual Studio Code. + /// + public static TemplateTheme Code { get; } = TemplateThemes.Code; + + /// + /// A theme using only gray, black and white. + /// + public static TemplateTheme Grayscale { get; } = TemplateThemes.Grayscale; + + /// + /// A theme in the style of the original Serilog.Sinks.Literate. + /// + public static TemplateTheme Literate { get; } = TemplateThemes.Literate; + + internal static TemplateTheme None { get; } = new TemplateTheme(new Dictionary()); + + readonly Dictionary _styles; + + /// + /// Construct a theme given a set of styles. + /// + /// Styles to apply within the theme. The dictionary maps style names to ANSI + /// sequences implementing the styles. + /// When is null + public TemplateTheme(IReadOnlyDictionary ansiStyles) + { + if (ansiStyles is null) throw new ArgumentNullException(nameof(ansiStyles)); + _styles = ansiStyles.ToDictionary(kv => kv.Key, kv => new Style(kv.Value)); + } + + /// + /// Construct a theme given a set of styles. + /// + /// A base template theme, which will supply styles not overridden in . + /// Styles to apply within the theme. The dictionary maps style names to ANSI + /// sequences implementing the styles. + /// When is null + public TemplateTheme(TemplateTheme baseTheme, IReadOnlyDictionary ansiStyles) + { + if (baseTheme == null) throw new ArgumentNullException(nameof(baseTheme)); + if (ansiStyles is null) throw new ArgumentNullException(nameof(ansiStyles)); + _styles = new Dictionary(baseTheme._styles); + foreach (var kv in ansiStyles) + _styles[kv.Key] = new Style(kv.Value); + } + + internal Style GetStyle(TemplateThemeStyle templateThemeStyle) + { + _styles.TryGetValue(templateThemeStyle, out var style); + return style; + } + } +} diff --git a/src/Serilog.Expressions/Templates/Themes/TemplateThemeStyle.cs b/src/Serilog.Expressions/Templates/Themes/TemplateThemeStyle.cs new file mode 100644 index 0000000..25912e5 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/TemplateThemeStyle.cs @@ -0,0 +1,104 @@ +// 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. + +namespace Serilog.Templates.Themes +{ + /// + /// Elements styled by a template theme. + /// + public enum TemplateThemeStyle + { + /// + /// Prominent text, generally content within an event's message. + /// + Text, + + /// + /// Boilerplate text, for example items specified in an output template. + /// + SecondaryText, + + /// + /// De-emphasized text, for example literal text in output templates and + /// punctuation used when writing structured data. + /// + TertiaryText, + + /// + /// Output demonstrating some kind of configuration issue, e.g. an invalid + /// message template token. + /// + Invalid, + + /// + /// The built-in value. + /// + Null, + + /// + /// Property and type names. + /// + Name, + + /// + /// Strings. + /// + String, + + /// + /// Numbers. + /// + Number, + + /// + /// values. + /// + Boolean, + + /// + /// All other scalar values, e.g. instances. + /// + Scalar, + + /// + /// Level indicator. + /// + LevelVerbose, + + /// + /// Level indicator. + /// + LevelDebug, + + /// + /// Level indicator. + /// + LevelInformation, + + /// + /// Level indicator. + /// + LevelWarning, + + /// + /// Level indicator. + /// + LevelError, + + /// + /// Level indicator. + /// + LevelFatal, + } +} diff --git a/src/Serilog.Expressions/Templates/Themes/TemplateThemes.cs b/src/Serilog.Expressions/Templates/Themes/TemplateThemes.cs new file mode 100644 index 0000000..7b17bef --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/TemplateThemes.cs @@ -0,0 +1,84 @@ +// 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 System.Collections.Generic; + +namespace Serilog.Templates.Themes +{ + static class TemplateThemes + { + public static TemplateTheme Literate { get; } = new( + new Dictionary + { + [TemplateThemeStyle.Text] = "\x1b[38;5;0015m", + [TemplateThemeStyle.SecondaryText] = "\x1b[38;5;0007m", + [TemplateThemeStyle.TertiaryText] = "\x1b[38;5;0008m", + [TemplateThemeStyle.Invalid] = "\x1b[38;5;0011m", + [TemplateThemeStyle.Null] = "\x1b[38;5;0027m", + [TemplateThemeStyle.Name] = "\x1b[38;5;0007m", + [TemplateThemeStyle.String] = "\x1b[38;5;0045m", + [TemplateThemeStyle.Number] = "\x1b[38;5;0200m", + [TemplateThemeStyle.Boolean] = "\x1b[38;5;0027m", + [TemplateThemeStyle.Scalar] = "\x1b[38;5;0085m", + [TemplateThemeStyle.LevelVerbose] = "\x1b[38;5;0007m", + [TemplateThemeStyle.LevelDebug] = "\x1b[38;5;0007m", + [TemplateThemeStyle.LevelInformation] = "\x1b[38;5;0015m", + [TemplateThemeStyle.LevelWarning] = "\x1b[38;5;0011m", + [TemplateThemeStyle.LevelError] = "\x1b[38;5;0015m\x1b[48;5;0196m", + [TemplateThemeStyle.LevelFatal] = "\x1b[38;5;0015m\x1b[48;5;0196m", + }); + + public static TemplateTheme Grayscale { get; } = new( + new Dictionary + { + [TemplateThemeStyle.Text] = "\x1b[37;1m", + [TemplateThemeStyle.SecondaryText] = "\x1b[37m", + [TemplateThemeStyle.TertiaryText] = "\x1b[30;1m", + [TemplateThemeStyle.Invalid] = "\x1b[37;1m\x1b[47m", + [TemplateThemeStyle.Null] = "\x1b[1m\x1b[37;1m", + [TemplateThemeStyle.Name] = "\x1b[37m", + [TemplateThemeStyle.String] = "\x1b[1m\x1b[37;1m", + [TemplateThemeStyle.Number] = "\x1b[1m\x1b[37;1m", + [TemplateThemeStyle.Boolean] = "\x1b[1m\x1b[37;1m", + [TemplateThemeStyle.Scalar] = "\x1b[1m\x1b[37;1m", + [TemplateThemeStyle.LevelVerbose] = "\x1b[30;1m", + [TemplateThemeStyle.LevelDebug] = "\x1b[30;1m", + [TemplateThemeStyle.LevelInformation] = "\x1b[37;1m", + [TemplateThemeStyle.LevelWarning] = "\x1b[37;1m\x1b[47m", + [TemplateThemeStyle.LevelError] = "\x1b[30m\x1b[47m", + [TemplateThemeStyle.LevelFatal] = "\x1b[30m\x1b[47m", + }); + + public static TemplateTheme Code { get; } = new( + new Dictionary + { + [TemplateThemeStyle.Text] = "\x1b[38;5;0253m", + [TemplateThemeStyle.SecondaryText] = "\x1b[38;5;0246m", + [TemplateThemeStyle.TertiaryText] = "\x1b[38;5;0242m", + [TemplateThemeStyle.Invalid] = "\x1b[33;1m", + [TemplateThemeStyle.Null] = "\x1b[38;5;0038m", + [TemplateThemeStyle.Name] = "\x1b[38;5;0081m", + [TemplateThemeStyle.String] = "\x1b[38;5;0216m", + [TemplateThemeStyle.Number] = "\x1b[38;5;151m", + [TemplateThemeStyle.Boolean] = "\x1b[38;5;0038m", + [TemplateThemeStyle.Scalar] = "\x1b[38;5;0079m", + [TemplateThemeStyle.LevelVerbose] = "\x1b[37m", + [TemplateThemeStyle.LevelDebug] = "\x1b[37m", + [TemplateThemeStyle.LevelInformation] = "\x1b[37;1m", + [TemplateThemeStyle.LevelWarning] = "\x1b[38;5;0229m", + [TemplateThemeStyle.LevelError] = "\x1b[38;5;0197m\x1b[48;5;0238m", + [TemplateThemeStyle.LevelFatal] = "\x1b[38;5;0197m\x1b[48;5;0238m", + }); + } +} \ No newline at end of file diff --git a/src/Serilog.Expressions/Templates/Themes/ThemedJsonValueFormatter.cs b/src/Serilog.Expressions/Templates/Themes/ThemedJsonValueFormatter.cs new file mode 100644 index 0000000..ba0fd44 --- /dev/null +++ b/src/Serilog.Expressions/Templates/Themes/ThemedJsonValueFormatter.cs @@ -0,0 +1,254 @@ +// 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 System; +using System.Globalization; +using System.IO; +using Serilog.Data; +using Serilog.Events; +using Serilog.Formatting.Json; + +// ReSharper disable ForCanBeConvertedToForeach + +namespace Serilog.Templates.Themes +{ + class ThemedJsonValueFormatter : LogEventPropertyValueVisitor + { + const string TypeTagPropertyName = "$type"; + + readonly Style _null, _bool, _num, _string, _scalar, _tertiary, _name; + + public ThemedJsonValueFormatter(TemplateTheme theme) + { + _null = theme.GetStyle(TemplateThemeStyle.Null); + _bool = theme.GetStyle(TemplateThemeStyle.Boolean); + _num = theme.GetStyle(TemplateThemeStyle.Number); + _string = theme.GetStyle(TemplateThemeStyle.String); + _scalar = theme.GetStyle(TemplateThemeStyle.Scalar); + _tertiary = theme.GetStyle(TemplateThemeStyle.TertiaryText); + _name = theme.GetStyle(TemplateThemeStyle.Name); + } + + public int Format(LogEventPropertyValue value, TextWriter output) + { + return Visit(output, value); + } + + protected override int VisitScalarValue(TextWriter state, ScalarValue scalar) + { + return FormatLiteralValue(scalar, state); + } + + protected override int VisitSequenceValue(TextWriter state, SequenceValue sequence) + { + var count = 0; + + using (_tertiary.Set(state, ref count)) + state.Write('['); + + var delim = string.Empty; + for (var index = 0; index < sequence.Elements.Count; ++index) + { + if (delim.Length != 0) + { + using (_tertiary.Set(state, ref count)) + state.Write(delim); + } + + delim = ","; + count += Visit(state, sequence.Elements[index]); + } + + using (_tertiary.Set(state, ref count)) + state.Write(']'); + + return count; + + } + + protected override int VisitStructureValue(TextWriter state, StructureValue structure) + { + var count = 0; + + using (_tertiary.Set(state, ref count)) + state.Write('{'); + + var delim = string.Empty; + for (var index = 0; index < structure.Properties.Count; ++index) + { + if (delim.Length != 0) + { + using (_tertiary.Set(state, ref count)) + state.Write(delim); + } + + delim = ","; + + var property = structure.Properties[index]; + + using (_name.Set(state, ref count)) + JsonValueFormatter.WriteQuotedJsonString(property.Name, state); + + using (_tertiary.Set(state, ref count)) + state.Write(":"); + + count += Visit(state, property.Value); + } + + if (structure.TypeTag != null) + { + using (_tertiary.Set(state, ref count)) + state.Write(delim); + + using (_name.Set(state, ref count)) + JsonValueFormatter.WriteQuotedJsonString(TypeTagPropertyName, state); + + using (_tertiary.Set(state, ref count)) + state.Write(":"); + + using (_string.Set(state, ref count)) + JsonValueFormatter.WriteQuotedJsonString(structure.TypeTag, state); + } + + using (_tertiary.Set(state, ref count)) + state.Write('}'); + + return count; + } + + protected override int VisitDictionaryValue(TextWriter state, DictionaryValue dictionary) + { + var count = 0; + + using (_tertiary.Set(state, ref count)) + state.Write('{'); + + var delim = string.Empty; + foreach (var element in dictionary.Elements) + { + if (delim.Length != 0) + { + using (_tertiary.Set(state, ref count)) + state.Write(delim); + } + + delim = ","; + + var style = element.Key.Value == null + ? _null + : element.Key.Value is string + ? _string + : _scalar; + + using (style.Set(state, ref count)) + JsonValueFormatter.WriteQuotedJsonString((element.Key.Value ?? "null").ToString(), state); + + using (_tertiary.Set(state, ref count)) + state.Write(":"); + + count += Visit(state, element.Value); + } + + using (_tertiary.Set(state, ref count)) + state.Write('}'); + + return count; + } + + int FormatLiteralValue(ScalarValue scalar, TextWriter output) + { + var value = scalar.Value; + var count = 0; + + if (value == null) + { + using (_null.Set(output, ref count)) + output.Write("null"); + return count; + } + + if (value is string str) + { + using (_string.Set(output, ref count)) + JsonValueFormatter.WriteQuotedJsonString(str, output); + return count; + } + + if (value is ValueType) + { + if (value is int or uint or long or ulong or decimal or byte or sbyte or short or ushort) + { + using (_num.Set(output, ref count)) + output.Write(((IFormattable)value).ToString(null, CultureInfo.InvariantCulture)); + return count; + } + + if (value is double d) + { + using (_num.Set(output, ref count)) + { + if (double.IsNaN(d) || double.IsInfinity(d)) + JsonValueFormatter.WriteQuotedJsonString(d.ToString(CultureInfo.InvariantCulture), output); + else + output.Write(d.ToString("R", CultureInfo.InvariantCulture)); + } + return count; + } + + if (value is float f) + { + using (_num.Set(output, ref count)) + { + if (double.IsNaN(f) || double.IsInfinity(f)) + JsonValueFormatter.WriteQuotedJsonString(f.ToString(CultureInfo.InvariantCulture), output); + else + output.Write(f.ToString("R", CultureInfo.InvariantCulture)); + } + return count; + } + + if (value is bool b) + { + using (_bool.Set(output, ref count)) + output.Write(b ? "true" : "false"); + + return count; + } + + if (value is char ch) + { + using (_scalar.Set(output, ref count)) + JsonValueFormatter.WriteQuotedJsonString(ch.ToString(), output); + return count; + } + + if (value is DateTime or DateTimeOffset) + { + using (_scalar.Set(output, ref count)) + { + output.Write('"'); + output.Write(((IFormattable)value).ToString("O", CultureInfo.InvariantCulture)); + output.Write('"'); + } + return count; + } + } + + using (_scalar.Set(output, ref count)) + JsonValueFormatter.WriteQuotedJsonString(value.ToString(), output); + + return count; + } + } +} \ No newline at end of file diff --git a/test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv b/test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv index b622adf..010f299 100644 --- a/test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv +++ b/test/Serilog.Expressions.Tests/Cases/expression-evaluation-cases.asv @@ -231,6 +231,9 @@ tostring('test', 42) ⇶ undefined() tostring(16, undefined()) ⇶ '16' tostring(16, null) ⇶ '16' +// Tests are in fr-FR +tostring(16.3) ⇶ '16,3' + // TypeOf typeof(undefined()) ⇶ 'undefined' typeof('test') ⇶ 'System.String' diff --git a/test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv b/test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv index 4c4a4b1..3547b36 100644 --- a/test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv +++ b/test/Serilog.Expressions.Tests/Cases/template-evaluation-cases.asv @@ -26,3 +26,4 @@ A{#if false}B{#else if true}C{#end} ⇶ AC {#each a, b in {x: 1, y: 2}}{a}.{b}{#end} ⇶ x.1y.2 {#if true}A{#each a in [1]}B{a}{#end}C{#end}D ⇶ AB1CD {#each a in []}{a}!{#else}none{#end} ⇶ none +Culture-specific {42.34} ⇶ Culture-specific 42,34 diff --git a/test/Serilog.Expressions.Tests/ExpressionEvaluationTests.cs b/test/Serilog.Expressions.Tests/ExpressionEvaluationTests.cs index 0532a9b..b741159 100644 --- a/test/Serilog.Expressions.Tests/ExpressionEvaluationTests.cs +++ b/test/Serilog.Expressions.Tests/ExpressionEvaluationTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using Serilog.Events; @@ -26,8 +27,9 @@ public void ExpressionsAreCorrectlyEvaluated(string expr, string result) new LogEventProperty("Id", new ScalarValue(42)), new LogEventProperty("Name", new ScalarValue("nblumhardt")), }))); - - var actual = SerilogExpression.Compile(expr)(evt); + + var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag("fr-FR"); + var actual = SerilogExpression.Compile(expr, formatProvider: frFr)(evt); var expected = SerilogExpression.Compile(result)(evt); if (expected is null) diff --git a/test/Serilog.Expressions.Tests/Expressions/NameResolverTests.cs b/test/Serilog.Expressions.Tests/Expressions/NameResolverTests.cs index 433017a..80ed59a 100644 --- a/test/Serilog.Expressions.Tests/Expressions/NameResolverTests.cs +++ b/test/Serilog.Expressions.Tests/Expressions/NameResolverTests.cs @@ -20,7 +20,7 @@ public void UserDefinedFunctionsAreCallableInExpressions() { var expr = SerilogExpression.Compile( "magic(10) + 3 = 55", - new StaticMemberNameResolver(typeof(NameResolverTests))); + nameResolver: new StaticMemberNameResolver(typeof(NameResolverTests))); Assert.True(Coerce.IsTrue(expr(Some.InformationEvent()))); } } diff --git a/test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs b/test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs index 352d9e3..e38e74c 100644 --- a/test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs +++ b/test/Serilog.Expressions.Tests/TemplateEvaluationTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Globalization; using System.IO; using Serilog.Expressions.Tests.Support; using Serilog.Templates; @@ -16,7 +17,8 @@ public class TemplateEvaluationTests public void TemplatesAreCorrectlyEvaluated(string template, string expected) { var evt = Some.InformationEvent("Hello, {Name}!", "nblumhardt"); - var compiled = new ExpressionTemplate(template); + var frFr = CultureInfo.GetCultureInfoByIetfLanguageTag("fr-FR"); + var compiled = new ExpressionTemplate(template, formatProvider: frFr); var output = new StringWriter(); compiled.Format(evt, output); var actual = output.ToString(); diff --git a/test/Serilog.Expressions.Tests/TemplateParserTests.cs b/test/Serilog.Expressions.Tests/TemplateParserTests.cs index 16f2be1..ee62d39 100644 --- a/test/Serilog.Expressions.Tests/TemplateParserTests.cs +++ b/test/Serilog.Expressions.Tests/TemplateParserTests.cs @@ -1,4 +1,6 @@ using Serilog.Templates; +using Serilog.Templates.Ast; +using Serilog.Templates.Parsing; using Xunit; namespace Serilog.Expressions.Tests @@ -17,8 +19,17 @@ public class TemplateParserTests [InlineData("Empty {Align,} digits", "Syntax error (line 1, column 14): unexpected `}`, expected alignment and width.")] public void ErrorsAreReported(string input, string error) { - Assert.False(ExpressionTemplate.TryParse(input, null, null, out _, out var actual)); + Assert.False(ExpressionTemplate.TryParse(input, null, null, null, out _, out var actual)); Assert.Equal(error, actual); } + + [Fact] + public void DefaultAlignmentIsNull() + { + var parser = new TemplateParser(); + Assert.True(parser.TryParse("{x}", out var template, out _)); + var avt = Assert.IsType(template); + Assert.Null(avt.Alignment); + } } } \ No newline at end of file