diff --git a/.idea/.idea.serilog-sinks-console/.idea/.gitignore b/.idea/.idea.serilog-sinks-console/.idea/.gitignore new file mode 100644 index 0000000..c239f02 --- /dev/null +++ b/.idea/.idea.serilog-sinks-console/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/.idea.serilog-sinks-console.iml +/modules.xml +/projectSettingsUpdater.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.idea.serilog-sinks-console/.idea/encodings.xml b/.idea/.idea.serilog-sinks-console/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.serilog-sinks-console/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.serilog-sinks-console/.idea/indexLayout.xml b/.idea/.idea.serilog-sinks-console/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.serilog-sinks-console/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.serilog-sinks-console/.idea/vcs.xml b/.idea/.idea.serilog-sinks-console/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.serilog-sinks-console/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 10b9e57..2ed804c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,10 @@ language: csharp matrix: include: - os: linux # Ubuntu 14.04 - dist: trusty + dist: xenial sudo: required - dotnet: 1.0.4 + dotnet: 2.2.401 + mono: none group: edge script: diff --git a/Build.ps1 b/Build.ps1 index ee4117d..b0ac93a 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,9 +1,12 @@ -echo "build: Build started" +Write-Output "build: Build started" + +& dotnet --info +& dotnet --list-sdks Push-Location $PSScriptRoot if(Test-Path .\artifacts) { - echo "build: Cleaning .\artifacts" + Write-Output "build: Cleaning .\artifacts" Remove-Item .\artifacts -Force -Recurse } @@ -11,17 +14,17 @@ if(Test-Path .\artifacts) { $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; $revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; -$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "master" -and $revision -ne "local"] +$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] $commitHash = $(git rev-parse --short HEAD) $buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""] -echo "build: Package version suffix is $suffix" -echo "build: Build version suffix is $buildSuffix" +Write-Output "build: Package version suffix is $suffix" +Write-Output "build: Build version suffix is $buildSuffix" -foreach ($src in ls src/*) { +foreach ($src in Get-ChildItem src/*) { Push-Location $src - echo "build: Packaging project in $src" + Write-Output "build: Packaging project in $src" & dotnet build -c Release --version-suffix=$buildSuffix if ($suffix) { @@ -34,10 +37,21 @@ foreach ($src in ls src/*) { Pop-Location } -foreach ($test in ls test/*.Tests) { +foreach ($sample in Get-ChildItem sample/*) { + Push-Location $sample + + Write-Output "build: Testing project in $sample" + + & dotnet build -c Release --version-suffix=$buildSuffix + if($LASTEXITCODE -ne 0) { exit 3 } + + Pop-Location +} + +foreach ($test in Get-ChildItem test/*.Tests) { Push-Location $test - echo "build: Testing project in $test" + Write-Output "build: Testing project in $test" & dotnet test -c Release if($LASTEXITCODE -ne 0) { exit 3 } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index caea530..8fbb2ed 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,3 @@ # Code of Conduct -Please refer to the [Serilog Code of Conduct](https://github.com/serilog/serilog/blob/dev/CONTRIBUTING.md) which covers all repositories within the Serilog Organisation. \ No newline at end of file +Please refer to the [Serilog Code of Conduct](https://github.com/serilog/serilog/blob/dev/CODE_OF_CONDUCT.md) which covers all repositories within the Serilog Organization. diff --git a/README.md b/README.md index 0ba2bc0..b380b56 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ A Serilog sink that writes log events to the Windows Console or an ANSI terminal To use the console sink, first install the [NuGet package](https://nuget.org/packages/serilog.sinks.console): -```powershell -Install-Package Serilog.Sinks.Console +```shell +dotnet add package Serilog.Sinks.Console ``` Then enable the sink using `WriteTo.Console()`: @@ -64,14 +64,14 @@ The default template, shown in the example above, uses built-in properties like The sink can write JSON output instead of plain text. `CompactJsonFormatter` or `RenderedCompactJsonFormatter` from [Serilog.Formatting.Compact](https://github.com/serilog/serilog-formatting-compact) is recommended: -```powershell -Install-Package Serilog.Formatting.Compact +```shell +dotnet add package Serilog.Formatting.Compact ``` Pass a formatter to the `Console()` configuration method: ```csharp - .WriteTo.Console(new CompactJsonFormatter()) + .WriteTo.Console(new RenderedCompactJsonFormatter()) ``` Output theming is not available when custom formatters are used. @@ -80,8 +80,8 @@ Output theming is not available when custom formatters are used. To use the console sink with the [Serilog.Settings.AppSettings](https://github.com/serilog/serilog-settings-appsettings) package, first install that package if you haven't already done so: -```powershell -Install-Package Serilog.Settings.AppSettings +```shell +dotnet add package Serilog.Settings.AppSettings ``` Instead of configuring the logger in code, call `ReadFrom.AppSettings()`: @@ -101,12 +101,21 @@ In your application's `App.config` or `Web.config` file, specify the console sin ``` +To configure the console sink with a different theme and include the `SourceContext` in the output, change your `App.config`/`Web.config` to: +```xml + + + + + +``` + ### JSON `appsettings.json` configuration To use the console sink with _Microsoft.Extensions.Configuration_, for example with ASP.NET Core or .NET Core, use the [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) package. First install that package if you have not already done so: -```powershell -Install-Package Serilog.Settings.Configuration +```shell +dotnet add package Serilog.Settings.Configuration ``` Instead of configuring the sink directly in code, call `ReadFrom.Configuration()`: @@ -122,7 +131,6 @@ var logger = new LoggerConfiguration() ``` In your `appsettings.json` file, under the `Serilog` node, : - ```json { "Serilog": { @@ -131,18 +139,23 @@ In your `appsettings.json` file, under the `Serilog` node, : } ``` -### Upgrading from _Serilog.Sinks.Console_ 2.x - -To achieve output identical to version 2 of this sink, specify a formatter and output template explicitly: - -```csharp - .WriteTo.Console(new MessageTemplateTextFormatter( - "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}", - null)) +To configure the console sink with a different theme and include the `SourceContext` in the output, change your `appsettings.json` to: +```json +{ + "Serilog": { + "WriteTo": [ + { + "Name": "Console", + "Args": { + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", + "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" + } + } + ] + } +} ``` -This will bypass theming and use Serilog's built-in message template formatting. - ### Contributing Would you like to help make the Serilog console sink even better? We keep a list of issues that are approachable for newcomers under the [up-for-grabs](https://github.com/serilog/serilog-sinks-console/issues?labels=up-for-grabs&state=open) label. Before starting work on a pull request, we suggest commenting on, or raising, an issue on the issue tracker so that we can help and coordinate efforts. For more details check out our [contributing guide](CONTRIBUTING.md). @@ -155,7 +168,7 @@ When contributing please keep in mind our [Code of Conduct](CODE_OF_CONDUCT.md). Branch | AppVeyor | Travis ------------- | ------------- |------------- dev | [![Build status](https://ci.appveyor.com/api/projects/status/w1w3m1wyk3in1c96/branch/dev?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-console/branch/dev) | [![Build Status](https://travis-ci.org/serilog/serilog-sinks-console.svg?branch=dev)](https://travis-ci.org/serilog/serilog-sinks-console) -master | [![Build status](https://ci.appveyor.com/api/projects/status/w1w3m1wyk3in1c96/branch/master?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-console/branch/master) | [![Build Status](https://travis-ci.org/serilog/serilog-sinks-console.svg?branch=master)](https://travis-ci.org/serilog/serilog-sinks-console) +main | [![Build status](https://ci.appveyor.com/api/projects/status/w1w3m1wyk3in1c96/branch/main?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-console/branch/main) | [![Build Status](https://travis-ci.org/serilog/serilog-sinks-console.svg?branch=main)](https://travis-ci.org/serilog/serilog-sinks-console) -_Copyright © 2017 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html)._ +_Copyright © Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html)._ diff --git a/appveyor.yml b/appveyor.yml index 7df8c2f..3c57f91 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2017 -configuration: Release +image: Visual Studio 2019 test: off build_script: - ps: ./Build.ps1 @@ -10,14 +9,14 @@ artifacts: deploy: - provider: NuGet api_key: - secure: nvZ/z+pMS91b3kG4DgfES5AcmwwGoBYQxr9kp4XiJHj25SAlgdIxFx++1N0lFH2x + secure: rbdBqxBpLt4MkB+mrDOYNDOd8aVZ1zMkysaVNAXNKnC41FYifzX3l9LM8DCrUWU5 skip_symbols: true on: - branch: /^(master|dev)$/ + branch: /^(main|dev)$/ - provider: GitHub auth_token: secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX artifact: /Serilog.*\.nupkg/ tag: v$(appveyor_build_version) on: - branch: master + branch: main diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..9bf45a3 Binary files /dev/null and b/assets/icon.png differ diff --git a/build.sh b/build.sh index b2dc450..64d87ea 100755 --- a/build.sh +++ b/build.sh @@ -5,15 +5,14 @@ dotnet --info dotnet restore for path in src/**/*.csproj; do - dotnet build -f netstandard1.3 -c Release ${path} - dotnet build -f netcoreapp1.1 -c Release ${path} + dotnet build -f netstandard2.0 -c Release ${path} done for path in test/*.Tests/*.csproj; do - dotnet test -f netcoreapp1.1 -c Release ${path} + dotnet test -f netcoreapp2.2 -c Release ${path} done for path in sample/ConsoleDemo/*.csproj; do - dotnet build -f netcoreapp1.1 -c Release ${path} - dotnet run -f netcoreapp1.1 --project ${path} + dotnet build -f netcoreapp2.2 -c Release ${path} + dotnet run -f netcoreapp2.2 --project ${path} done diff --git a/sample/ConsoleDemo/ConsoleDemo.csproj b/sample/ConsoleDemo/ConsoleDemo.csproj index 3a2245a..81b1a1f 100644 --- a/sample/ConsoleDemo/ConsoleDemo.csproj +++ b/sample/ConsoleDemo/ConsoleDemo.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp1.1;net45 + netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net452;net462;net472;net48;net5.0 diff --git a/sample/ConsoleDemo/Program.cs b/sample/ConsoleDemo/Program.cs index dcd9d75..73d50d8 100644 --- a/sample/ConsoleDemo/Program.cs +++ b/sample/ConsoleDemo/Program.cs @@ -1,11 +1,11 @@ using Serilog; +using Serilog.Sinks.SystemConsole.Themes; using System; using System.Threading; -using Serilog.Sinks.SystemConsole.Themes; namespace ConsoleDemo { - public class Program + public static class Program { public static void Main() { diff --git a/sample/SyncWritesDemo/Program.cs b/sample/SyncWritesDemo/Program.cs new file mode 100644 index 0000000..5120393 --- /dev/null +++ b/sample/SyncWritesDemo/Program.cs @@ -0,0 +1,57 @@ +using Serilog; +using Serilog.Sinks.SystemConsole.Themes; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace SyncWritesDemo +{ + public static class Program + { + public static void Main(string[] args) + { + Console.WriteLine("A sample of how to sync writes to the console sink."); + + if (args != null && args.Length == 1) + { + switch (args[0]) + { + case "--sync-root-default": + SystemConsoleSyncTest(syncRootForLogger1: null, syncRootForLogger2: null); + return; + case "--sync-root-separate": + SystemConsoleSyncTest(syncRootForLogger1: new object(), syncRootForLogger2: new object()); + return; + case "--sync-root-same": + var sameSyncRoot = new object(); + SystemConsoleSyncTest(syncRootForLogger1: sameSyncRoot, syncRootForLogger2: sameSyncRoot); + return; + } + } + + Console.WriteLine("Expecting one of the following arguments:{0}--sync-root-default{0}--sync-root-separate{0}--sync-root-same", Environment.NewLine); + } + + static void SystemConsoleSyncTest(object syncRootForLogger1, object syncRootForLogger2) + { + var logger1 = new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("Logger", "logger1") + .WriteTo.Console(theme: SystemConsoleTheme.Literate, syncRoot: syncRootForLogger1) + .CreateLogger(); + + var logger2 = new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("Logger", "logger2") + .WriteTo.Console(theme: SystemConsoleTheme.Literate, syncRoot: syncRootForLogger2) + .CreateLogger(); + + var options = new ParallelOptions { MaxDegreeOfParallelism = 8 }; + Parallel.For(0, 1000, options, (i, loopState) => + { + var logger = (i % 2 == 0) ? logger1 : logger2; + logger.Information("Event {Iteration} generated by {ThreadId}", i, Thread.CurrentThread.ManagedThreadId); + }); + } + } +} diff --git a/sample/SyncWritesDemo/SyncWritesDemo.csproj b/sample/SyncWritesDemo/SyncWritesDemo.csproj new file mode 100644 index 0000000..a72595e --- /dev/null +++ b/sample/SyncWritesDemo/SyncWritesDemo.csproj @@ -0,0 +1,12 @@ + + + + Exe + net5.0 + + + + + + + \ No newline at end of file diff --git a/serilog-sinks-console.sln b/serilog-sinks-console.sln index 6a6a8d4..e223156 100644 --- a/serilog-sinks-console.sln +++ b/serilog-sinks-console.sln @@ -13,7 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5 Build.ps1 = Build.ps1 CHANGES.md = CHANGES.md LICENSE = LICENSE - NuGet.Config = NuGet.Config README.md = README.md assets\Serilog.snk = assets\Serilog.snk EndProjectSection @@ -26,7 +25,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Console.Tests EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{CF817664-4CEC-4B6A-9C57-A0D687757D82}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleDemo", "sample\ConsoleDemo\ConsoleDemo.csproj", "{DBF4907A-63A2-4895-8DEF-59F90C20380B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleDemo", "sample\ConsoleDemo\ConsoleDemo.csproj", "{DBF4907A-63A2-4895-8DEF-59F90C20380B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncWritesDemo", "sample\SyncWritesDemo\SyncWritesDemo.csproj", "{633AE0AD-C9D4-440D-874A-C0F4632DB75F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -46,6 +47,10 @@ Global {DBF4907A-63A2-4895-8DEF-59F90C20380B}.Debug|Any CPU.Build.0 = Debug|Any CPU {DBF4907A-63A2-4895-8DEF-59F90C20380B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DBF4907A-63A2-4895-8DEF-59F90C20380B}.Release|Any CPU.Build.0 = Release|Any CPU + {633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {633AE0AD-C9D4-440D-874A-C0F4632DB75F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -54,5 +59,9 @@ Global {866A028E-27DB-49A0-AC78-E5FEF247C099} = {037440DE-440B-4129-9F7A-09B42D00397E} {1D56534C-4009-42C2-A573-789CAE6B8AA9} = {7D0692CD-F95D-4BF9-8C63-B4A1C078DF23} {DBF4907A-63A2-4895-8DEF-59F90C20380B} = {CF817664-4CEC-4B6A-9C57-A0D687757D82} + {633AE0AD-C9D4-440D-874A-C0F4632DB75F} = {CF817664-4CEC-4B6A-9C57-A0D687757D82} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {43C32ED4-D39A-4E27-AE99-7BB8C883833C} EndGlobalSection EndGlobal diff --git a/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs index 8c6fce8..e55f7a2 100644 --- a/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Console/ConsoleLoggerConfigurationExtensions.cs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using Serilog.Configuration; using Serilog.Core; using Serilog.Events; using Serilog.Formatting; using Serilog.Sinks.SystemConsole; -using Serilog.Sinks.SystemConsole.Themes; using Serilog.Sinks.SystemConsole.Output; +using Serilog.Sinks.SystemConsole.Themes; +using System; namespace Serilog { @@ -28,6 +28,7 @@ namespace Serilog /// public static class ConsoleLoggerConfigurationExtensions { + static readonly object DefaultSyncRoot = new object(); const string DefaultConsoleOutputTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"; /// @@ -36,33 +37,43 @@ public static class ConsoleLoggerConfigurationExtensions /// Logger sink configuration. /// The minimum level for /// events passed through the sink. Ignored when is specified. - /// A switch allowing the pass-through minimum level - /// to be changed at runtime. /// A message template describing the format used to write to the sink. - /// the default is "[{Timestamp:HH:mm:ss} {Level:u3}] {Message}{NewLine}{Exception}". + /// The default is "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}". + /// An object that will be used to `lock` (sync) access to the console output. If you specify this, you + /// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while + /// the lock is held. /// Supplies culture-specific formatting information, or null. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. /// Specifies the level at which events will be written to standard error. /// The theme to apply to the styled output. If not specified, /// uses . + /// Applies the selected or default theme even when output redirection is detected. /// Configuration object allowing method chaining. + /// When is null + /// When is null public static LoggerConfiguration Console( this LoggerSinkConfiguration sinkConfiguration, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, string outputTemplate = DefaultConsoleOutputTemplate, - IFormatProvider formatProvider = null, - LoggingLevelSwitch levelSwitch = null, + IFormatProvider? formatProvider = null, + LoggingLevelSwitch? levelSwitch = null, LogEventLevel? standardErrorFromLevel = null, - ConsoleTheme theme = null) + ConsoleTheme? theme = null, + bool applyThemeToRedirectedOutput = false, + object? syncRoot = null) { - if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); - if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); + if (sinkConfiguration is null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (outputTemplate is null) throw new ArgumentNullException(nameof(outputTemplate)); - var appliedTheme = System.Console.IsOutputRedirected || System.Console.IsErrorRedirected ? + var appliedTheme = !applyThemeToRedirectedOutput && (System.Console.IsOutputRedirected || System.Console.IsErrorRedirected) ? ConsoleTheme.None : theme ?? SystemConsoleThemes.Literate; + syncRoot ??= DefaultSyncRoot; + var formatter = new OutputTemplateRenderer(appliedTheme, outputTemplate, formatProvider); - return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel), restrictedToMinimumLevel, levelSwitch); + return sinkConfiguration.Sink(new ConsoleSink(appliedTheme, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); } /// @@ -71,23 +82,31 @@ public static LoggerConfiguration Console( /// Logger sink configuration. /// Controls the rendering of log events into text, for example to log JSON. To /// control plain text formatting, use the overload that accepts an output template. + /// An object that will be used to `lock` (sync) access to the console output. If you specify this, you + /// will have the ability to lock on this object, and guarantee that the console sink will not be about to output anything while + /// the lock is held. /// The minimum level for /// events passed through the sink. Ignored when is specified. /// A switch allowing the pass-through minimum level /// to be changed at runtime. /// Specifies the level at which events will be written to standard error. /// Configuration object allowing method chaining. + /// When is null + /// When is null public static LoggerConfiguration Console( this LoggerSinkConfiguration sinkConfiguration, ITextFormatter formatter, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null, - LogEventLevel? standardErrorFromLevel = null) + LoggingLevelSwitch? levelSwitch = null, + LogEventLevel? standardErrorFromLevel = null, + object? syncRoot = null) { - if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); - if (formatter == null) throw new ArgumentNullException(nameof(formatter)); + if (sinkConfiguration is null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (formatter is null) throw new ArgumentNullException(nameof(formatter)); + + syncRoot ??= DefaultSyncRoot; - return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel), restrictedToMinimumLevel, levelSwitch); + return sinkConfiguration.Sink(new ConsoleSink(ConsoleTheme.None, formatter, standardErrorFromLevel, syncRoot), restrictedToMinimumLevel, levelSwitch); } } } diff --git a/src/Serilog.Sinks.Console/Properties/AssemblyInfo.cs b/src/Serilog.Sinks.Console/Properties/AssemblyInfo.cs index 3ecea3a..34ef2fa 100644 --- a/src/Serilog.Sinks.Console/Properties/AssemblyInfo.cs +++ b/src/Serilog.Sinks.Console/Properties/AssemblyInfo.cs @@ -17,7 +17,7 @@ [assembly: CLSCompliant(true)] -[assembly: InternalsVisibleTo("Serilog.Sinks.SystemConsole.Tests, PublicKey=" + +[assembly: InternalsVisibleTo("Serilog.Sinks.Console.Tests, PublicKey=" + "0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + diff --git a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj index 693b36a..4a93619 100644 --- a/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj +++ b/src/Serilog.Sinks.Console/Serilog.Sinks.Console.csproj @@ -1,55 +1,37 @@ - + A Serilog sink that writes log events to the console/terminal. - 3.1.1 + 4.0.0 Serilog Contributors - net45;netstandard1.3;netcoreapp1.1 - Serilog.Sinks.Console + net45;netstandard1.3;netstandard2.0;net5.0 + 8.0 + enable ../../assets/Serilog.snk true true - Serilog.Sinks.Console serilog;console;terminal - http://serilog.net/images/serilog-sink-nuget.png + icon.png https://github.com/serilog/serilog-sinks-console - https://www.apache.org/licenses/LICENSE-2.0 - true - true + Apache-2.0 + https://github.com/serilog/serilog-sinks-console + git true - - true True Serilog - - $(DefineConstants);PINVOKE - - - - $(DefineConstants);PINVOKE;RUNTIME_INFORMATION + + $(DefineConstants);RUNTIME_INFORMATION - + + - - - - - - - - - - - - - - - + + + - diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/ConsoleSink.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/ConsoleSink.cs index 0aedb49..e1c1bea 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/ConsoleSink.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/ConsoleSink.cs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; -using System.IO; -using System.Text; using Serilog.Core; using Serilog.Events; -using Serilog.Sinks.SystemConsole.Themes; using Serilog.Formatting; using Serilog.Sinks.SystemConsole.Platform; +using Serilog.Sinks.SystemConsole.Themes; +using System; +using System.IO; +using System.Text; namespace Serilog.Sinks.SystemConsole { @@ -28,9 +28,9 @@ class ConsoleSink : ILogEventSink readonly LogEventLevel? _standardErrorFromLevel; readonly ConsoleTheme _theme; readonly ITextFormatter _formatter; - readonly object _syncRoot = new object(); + readonly object _syncRoot; - const int DefaultWriteBuffer = 256; + const int DefaultWriteBufferCapacity = 256; static ConsoleSink() { @@ -40,11 +40,13 @@ static ConsoleSink() public ConsoleSink( ConsoleTheme theme, ITextFormatter formatter, - LogEventLevel? standardErrorFromLevel) + LogEventLevel? standardErrorFromLevel, + object syncRoot) { _standardErrorFromLevel = standardErrorFromLevel; _theme = theme ?? throw new ArgumentNullException(nameof(theme)); _formatter = formatter; + _syncRoot = syncRoot ?? throw new ArgumentNullException(nameof(syncRoot)); } public void Emit(LogEvent logEvent) @@ -56,11 +58,12 @@ public void Emit(LogEvent logEvent) // buffered write here and have no effect when the line is actually written out. if (_theme.CanBuffer) { - var buffer = new StringWriter(new StringBuilder(DefaultWriteBuffer)); + var buffer = new StringWriter(new StringBuilder(DefaultWriteBufferCapacity)); _formatter.Format(logEvent, buffer); + var formattedLogEventText = buffer.ToString(); lock (_syncRoot) { - output.Write(buffer.ToString()); + output.Write(formattedLogEventText); output.Flush(); } } @@ -76,7 +79,7 @@ public void Emit(LogEvent logEvent) TextWriter SelectOutputStream(LogEventLevel logEventLevel) { - if (!_standardErrorFromLevel.HasValue) + if (_standardErrorFromLevel is null) return Console.Out; return logEventLevel < _standardErrorFromLevel ? Console.Out : Console.Error; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedDisplayValueFormatter.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedDisplayValueFormatter.cs index e6b3659..3093bd6 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedDisplayValueFormatter.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedDisplayValueFormatter.cs @@ -22,9 +22,9 @@ namespace Serilog.Sinks.SystemConsole.Formatting { class ThemedDisplayValueFormatter : ThemedValueFormatter { - readonly IFormatProvider _formatProvider; + readonly IFormatProvider? _formatProvider; - public ThemedDisplayValueFormatter(ConsoleTheme theme, IFormatProvider formatProvider) + public ThemedDisplayValueFormatter(ConsoleTheme theme, IFormatProvider? formatProvider) : base(theme) { _formatProvider = formatProvider; @@ -37,14 +37,14 @@ public override ThemedValueFormatter SwitchTheme(ConsoleTheme theme) protected override int VisitScalarValue(ThemedValueFormatterState state, ScalarValue scalar) { - if (scalar == null) + if (scalar is null) throw new ArgumentNullException(nameof(scalar)); return FormatLiteralValue(scalar, state.Output, state.Format); } protected override int VisitSequenceValue(ThemedValueFormatterState state, SequenceValue sequence) { - if (sequence == null) + if (sequence is null) throw new ArgumentNullException(nameof(sequence)); var count = 0; @@ -52,12 +52,14 @@ protected override int VisitSequenceValue(ThemedValueFormatterState state, Seque using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('['); - var delim = ""; + var delim = string.Empty; for (var index = 0; index < sequence.Elements.Count; ++index) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; Visit(state, sequence.Elements[index]); @@ -84,12 +86,14 @@ protected override int VisitStructureValue(ThemedValueFormatterState state, Stru using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('{'); - var delim = ""; + var delim = string.Empty; for (var index = 0; index < structure.Properties.Count; ++index) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; @@ -117,12 +121,14 @@ protected override int VisitDictionaryValue(ThemedValueFormatterState state, Dic using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('{'); - var delim = ""; + var delim = string.Empty; foreach (var element in dictionary.Elements) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; @@ -144,12 +150,12 @@ protected override int VisitDictionaryValue(ThemedValueFormatterState state, Dic return count; } - public int FormatLiteralValue(ScalarValue scalar, TextWriter output, string format) + public int FormatLiteralValue(ScalarValue scalar, TextWriter output, string? format) { var value = scalar.Value; var count = 0; - if (value == null) + if (value is null) { using (ApplyStyle(output, ConsoleThemeStyle.Null, ref count)) output.Write("null"); diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedJsonValueFormatter.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedJsonValueFormatter.cs index a8c7ec8..ec6d5d1 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedJsonValueFormatter.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedJsonValueFormatter.cs @@ -24,9 +24,9 @@ namespace Serilog.Sinks.SystemConsole.Formatting class ThemedJsonValueFormatter : ThemedValueFormatter { readonly ThemedDisplayValueFormatter _displayFormatter; - readonly IFormatProvider _formatProvider; + readonly IFormatProvider? _formatProvider; - public ThemedJsonValueFormatter(ConsoleTheme theme, IFormatProvider formatProvider) + public ThemedJsonValueFormatter(ConsoleTheme theme, IFormatProvider? formatProvider) : base(theme) { _displayFormatter = new ThemedDisplayValueFormatter(theme, formatProvider); @@ -40,7 +40,7 @@ public override ThemedValueFormatter SwitchTheme(ConsoleTheme theme) protected override int VisitScalarValue(ThemedValueFormatterState state, ScalarValue scalar) { - if (scalar == null) + if (scalar is null) throw new ArgumentNullException(nameof(scalar)); // At the top level, for scalar values, use "display" rendering. @@ -60,12 +60,14 @@ protected override int VisitSequenceValue(ThemedValueFormatterState state, Seque using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('['); - var delim = ""; + var delim = string.Empty; for (var index = 0; index < sequence.Elements.Count; ++index) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; Visit(state.Nest(), sequence.Elements[index]); @@ -84,12 +86,14 @@ protected override int VisitStructureValue(ThemedValueFormatterState state, Stru using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('{'); - var delim = ""; + var delim = string.Empty; for (var index = 0; index < structure.Properties.Count; ++index) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; @@ -132,12 +136,14 @@ protected override int VisitDictionaryValue(ThemedValueFormatterState state, Dic using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write('{'); - var delim = ""; + var delim = string.Empty; foreach (var element in dictionary.Elements) { if (delim.Length != 0) + { using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(delim); + } delim = ", "; @@ -148,7 +154,7 @@ protected override int VisitDictionaryValue(ThemedValueFormatterState state, Dic : ConsoleThemeStyle.Scalar; using (ApplyStyle(state.Output, style, ref count)) - JsonValueFormatter.WriteQuotedJsonString((element.Key.Value ?? "null").ToString(), state.Output); + JsonValueFormatter.WriteQuotedJsonString((element.Key.Value ?? "null").ToString() ?? "", state.Output); using (ApplyStyle(state.Output, ConsoleThemeStyle.TertiaryText, ref count)) state.Output.Write(": "); @@ -242,7 +248,7 @@ int FormatLiteralValue(ScalarValue scalar, TextWriter output) } using (ApplyStyle(output, ConsoleThemeStyle.Scalar, ref count)) - JsonValueFormatter.WriteQuotedJsonString(value.ToString(), output); + JsonValueFormatter.WriteQuotedJsonString(value.ToString() ?? "", output); return count; } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatter.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatter.cs index ea780e0..34be339 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatter.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatter.cs @@ -34,7 +34,7 @@ protected StyleReset ApplyStyle(TextWriter output, ConsoleThemeStyle style, ref return _theme.Apply(output, style, ref invisibleCharacterCount); } - public int Format(LogEventPropertyValue value, TextWriter output, string format, bool literalTopLevel = false) + public int Format(LogEventPropertyValue value, TextWriter output, string? format, bool literalTopLevel = false) { return Visit(new ThemedValueFormatterState { Output = output, Format = format, IsTopLevel = literalTopLevel }, value); } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatterState.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatterState.cs index 1c720c0..a5f5a86 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatterState.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Formatting/ThemedValueFormatterState.cs @@ -19,12 +19,9 @@ namespace Serilog.Sinks.SystemConsole.Formatting struct ThemedValueFormatterState { public TextWriter Output; - public string Format; + public string? Format; public bool IsTopLevel; - public ThemedValueFormatterState Nest() - { - return new ThemedValueFormatterState {Output = Output}; - } + public ThemedValueFormatterState Nest() => new ThemedValueFormatterState { Output = Output }; } } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/EventPropertyTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/EventPropertyTokenRenderer.cs index e2ea4dc..d4fe511 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/EventPropertyTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/EventPropertyTokenRenderer.cs @@ -25,9 +25,9 @@ class EventPropertyTokenRenderer : OutputTemplateTokenRenderer { readonly ConsoleTheme _theme; readonly PropertyToken _token; - readonly IFormatProvider _formatProvider; + readonly IFormatProvider? _formatProvider; - public EventPropertyTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider formatProvider) + public EventPropertyTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider? formatProvider) { _theme = theme; _token = token; @@ -37,10 +37,9 @@ public EventPropertyTokenRenderer(ConsoleTheme theme, PropertyToken token, IForm public override void Render(LogEvent logEvent, TextWriter output) { // If a property is missing, don't render anything (message templates render the raw token here). - LogEventPropertyValue propertyValue; - if (!logEvent.Properties.TryGetValue(_token.PropertyName, out propertyValue)) + if (!logEvent.Properties.TryGetValue(_token.PropertyName, out var propertyValue)) { - Padding.Apply(output, "", _token.Alignment); + Padding.Apply(output, string.Empty, _token.Alignment); return; } @@ -63,7 +62,7 @@ public override void Render(LogEvent logEvent, TextWriter output) if (_token.Alignment.HasValue) { - var str = writer.ToString(); + var str = writer.ToString()!; Padding.Apply(output, str, _token.Alignment); } } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/ExceptionTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/ExceptionTokenRenderer.cs index 31aa4c2..b3db007 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/ExceptionTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/ExceptionTokenRenderer.cs @@ -34,11 +34,11 @@ public override void Render(LogEvent logEvent, TextWriter output) { // Padding is never applied by this renderer. - if (logEvent.Exception == null) + if (logEvent.Exception is null) return; var lines = new StringReader(logEvent.Exception.ToString()); - string nextLine; + string? nextLine; while ((nextLine = lines.ReadLine()) != null) { var style = nextLine.StartsWith(StackFrameLinePrefix) ? ConsoleThemeStyle.SecondaryText : ConsoleThemeStyle.Text; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs index 1d4f59f..d703bb8 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelOutputFormat.cs @@ -25,36 +25,39 @@ namespace Serilog.Sinks.SystemConsole.Output /// static class LevelOutputFormat { - static readonly string[][] TitleCaseLevelMap = { - new []{ "V", "Vb", "Vrb", "Verb" }, - new []{ "D", "De", "Dbg", "Dbug" }, - new []{ "I", "In", "Inf", "Info" }, - new []{ "W", "Wn", "Wrn", "Warn" }, - new []{ "E", "Er", "Err", "Eror" }, - new []{ "F", "Fa", "Ftl", "Fatl" } + static readonly string[][] TitleCaseLevelMap = + { + new[] { "V", "Vb", "Vrb", "Verb" }, + new[] { "D", "De", "Dbg", "Dbug" }, + new[] { "I", "In", "Inf", "Info" }, + new[] { "W", "Wn", "Wrn", "Warn" }, + new[] { "E", "Er", "Err", "Eror" }, + new[] { "F", "Fa", "Ftl", "Fatl" }, }; - static readonly string[][] LowercaseLevelMap = { - new []{ "v", "vb", "vrb", "verb" }, - new []{ "d", "de", "dbg", "dbug" }, - new []{ "i", "in", "inf", "info" }, - new []{ "w", "wn", "wrn", "warn" }, - new []{ "e", "er", "err", "eror" }, - new []{ "f", "fa", "ftl", "fatl" } + static readonly string[][] LowercaseLevelMap = + { + new[] { "v", "vb", "vrb", "verb" }, + new[] { "d", "de", "dbg", "dbug" }, + new[] { "i", "in", "inf", "info" }, + new[] { "w", "wn", "wrn", "warn" }, + new[] { "e", "er", "err", "eror" }, + new[] { "f", "fa", "ftl", "fatl" }, }; - static readonly string[][] UppercaseLevelMap = { - new []{ "V", "VB", "VRB", "VERB" }, - new []{ "D", "DE", "DBG", "DBUG" }, - new []{ "I", "IN", "INF", "INFO" }, - new []{ "W", "WN", "WRN", "WARN" }, - new []{ "E", "ER", "ERR", "EROR" }, - new []{ "F", "FA", "FTL", "FATL" } + static readonly string[][] UppercaseLevelMap = + { + new[] { "V", "VB", "VRB", "VERB" }, + new[] { "D", "DE", "DBG", "DBUG" }, + new[] { "I", "IN", "INF", "INFO" }, + new[] { "W", "WN", "WRN", "WARN" }, + new[] { "E", "ER", "ERR", "EROR" }, + new[] { "F", "FA", "FTL", "FATL" }, }; - public static string GetLevelMoniker(LogEventLevel value, string format = null) + public static string GetLevelMoniker(LogEventLevel value, string? format = null) { - if (format == null || format.Length != 2 && format.Length != 3) + if (format is null || format.Length != 2 && format.Length != 3) return Casing.Format(value.ToString(), format); // Using int.Parse() here requires allocating a string to exclude the first character prefix. @@ -78,7 +81,7 @@ public static string GetLevelMoniker(LogEventLevel value, string format = null) } var index = (int)value; - if (index >= 0 && index <= (int) LogEventLevel.Fatal) + if (index >= 0 && index <= (int)LogEventLevel.Fatal) { switch (format[0]) { diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelTokenRenderer.cs index 466632d..9cb9ed1 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/LevelTokenRenderer.cs @@ -25,6 +25,7 @@ class LevelTokenRenderer : OutputTemplateTokenRenderer { readonly ConsoleTheme _theme; readonly PropertyToken _levelToken; + static readonly Dictionary Levels = new Dictionary { { LogEventLevel.Verbose, ConsoleThemeStyle.LevelVerbose }, @@ -32,7 +33,7 @@ class LevelTokenRenderer : OutputTemplateTokenRenderer { LogEventLevel.Information, ConsoleThemeStyle.LevelInformation }, { LogEventLevel.Warning, ConsoleThemeStyle.LevelWarning }, { LogEventLevel.Error, ConsoleThemeStyle.LevelError }, - { LogEventLevel.Fatal, ConsoleThemeStyle.LevelFatal } + { LogEventLevel.Fatal, ConsoleThemeStyle.LevelFatal }, }; public LevelTokenRenderer(ConsoleTheme theme, PropertyToken levelToken) @@ -41,10 +42,6 @@ public LevelTokenRenderer(ConsoleTheme theme, PropertyToken levelToken) _levelToken = levelToken; } - protected LevelTokenRenderer() - { - } - public override void Render(LogEvent logEvent, TextWriter output) { var moniker = LevelOutputFormat.GetLevelMoniker(logEvent.Level, _levelToken.Format); diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/MessageTemplateOutputTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/MessageTemplateOutputTokenRenderer.cs index 6ca5fb4..9f0997b 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/MessageTemplateOutputTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/MessageTemplateOutputTokenRenderer.cs @@ -28,10 +28,11 @@ class MessageTemplateOutputTokenRenderer : OutputTemplateTokenRenderer readonly PropertyToken _token; readonly ThemedMessageTemplateRenderer _renderer; - public MessageTemplateOutputTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider formatProvider) + public MessageTemplateOutputTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider? formatProvider) { _theme = theme ?? throw new ArgumentNullException(nameof(theme)); _token = token ?? throw new ArgumentNullException(nameof(token)); + bool isLiteral = false, isJson = false; if (token.Format != null) @@ -54,7 +55,7 @@ public MessageTemplateOutputTokenRenderer(ConsoleTheme theme, PropertyToken toke public override void Render(LogEvent logEvent, TextWriter output) { - if (_token.Alignment == null || !_theme.CanBuffer) + if (_token.Alignment is null || !_theme.CanBuffer) { _renderer.Render(logEvent.MessageTemplate, logEvent.Properties, output); return; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs index c2ef206..c62ab67 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/OutputTemplateRenderer.cs @@ -27,9 +27,9 @@ class OutputTemplateRenderer : ITextFormatter { readonly OutputTemplateTokenRenderer[] _renderers; - public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormatProvider formatProvider) + public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormatProvider? formatProvider) { - if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); + if (outputTemplate is null) throw new ArgumentNullException(nameof(outputTemplate)); var template = new MessageTemplateParser().Parse(outputTemplate); var renderers = new List(); @@ -77,8 +77,8 @@ public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormat public void Format(LogEvent logEvent, TextWriter output) { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (output == null) throw new ArgumentNullException(nameof(output)); + if (logEvent is null) throw new ArgumentNullException(nameof(logEvent)); + if (output is null) throw new ArgumentNullException(nameof(output)); foreach (var renderer in _renderers) renderer.Render(logEvent, output); diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/PropertiesTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/PropertiesTokenRenderer.cs index 8bfe16e..8e48fd0 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/PropertiesTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/PropertiesTokenRenderer.cs @@ -31,11 +31,12 @@ class PropertiesTokenRenderer : OutputTemplateTokenRenderer readonly PropertyToken _token; readonly ThemedValueFormatter _valueFormatter; - public PropertiesTokenRenderer(ConsoleTheme theme, PropertyToken token, MessageTemplate outputTemplate, IFormatProvider formatProvider) + public PropertiesTokenRenderer(ConsoleTheme theme, PropertyToken token, MessageTemplate outputTemplate, IFormatProvider? formatProvider) { _outputTemplate = outputTemplate; _theme = theme ?? throw new ArgumentNullException(nameof(theme)); _token = token ?? throw new ArgumentNullException(nameof(token)); + var isJson = false; if (token.Format != null) @@ -61,7 +62,7 @@ public override void Render(LogEvent logEvent, TextWriter output) var value = new StructureValue(included); - if (_token.Alignment == null || !_theme.CanBuffer) + if (_token.Alignment is null || !_theme.CanBuffer) { _valueFormatter.Format(value, output, null); return; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs index 409a230..c763279 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Output/TimestampTokenRenderer.cs @@ -25,9 +25,9 @@ class TimestampTokenRenderer : OutputTemplateTokenRenderer { readonly ConsoleTheme _theme; readonly PropertyToken _token; - readonly IFormatProvider _formatProvider; + readonly IFormatProvider? _formatProvider; - public TimestampTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider formatProvider) + public TimestampTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider? formatProvider) { _theme = theme; _token = token; @@ -43,8 +43,10 @@ public override void Render(LogEvent logEvent, TextWriter output) var _ = 0; using (_theme.Apply(output, ConsoleThemeStyle.SecondaryText, ref _)) { - if (_token.Alignment == null) + if (_token.Alignment is null) + { sv.Render(output, _token.Format, _formatProvider); + } else { var buffer = new StringWriter(); diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Platform/WindowsConsole.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Platform/WindowsConsole.cs index f50af78..fce3ecf 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Platform/WindowsConsole.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Platform/WindowsConsole.cs @@ -12,18 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if PINVOKE using System; using System.Runtime.InteropServices; -#endif namespace Serilog.Sinks.SystemConsole.Platform { static class WindowsConsole { -#if PINVOKE public static void EnableVirtualTerminalProcessing() - { + { #if RUNTIME_INFORMATION if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; @@ -49,11 +46,6 @@ public static void EnableVirtualTerminalProcessing() static extern bool GetConsoleMode(IntPtr handle, out uint mode); [DllImport("kernel32.dll", SetLastError = true)] - static extern bool SetConsoleMode(IntPtr handle, uint mode); -#else - public static void EnableVirtualTerminalProcessing() - { - } -#endif + static extern bool SetConsoleMode(IntPtr handle, uint mode); } } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Casing.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Casing.cs index 27e866c..66ce179 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Casing.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Casing.cs @@ -18,10 +18,12 @@ static class Casing { /// /// Apply upper or lower casing to when is provided. - /// Returns when no or invalid format provided + /// Returns when no or invalid format provided. /// - /// The provided with formatting applied - public static string Format(string value, string format = null) + /// Provided string for formatting. + /// Format string. + /// The provided with formatting applied. + public static string Format(string value, string? format = null) { switch (format) { diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Padding.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Padding.cs index 4d6451e..12d2595 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Padding.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/Padding.cs @@ -24,9 +24,12 @@ static class Padding /// /// Writes the provided value to the output, applying direction-based padding when is provided. /// + /// Output object to write result. + /// Provided value. + /// The alignment settings to apply when rendering . public static void Apply(TextWriter output, string value, Alignment? alignment) { - if (!alignment.HasValue || value.Length >= alignment.Value.Width) + if (alignment is null || value.Length >= alignment.Value.Width) { output.Write(value); return; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/ThemedMessageTemplateRenderer.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/ThemedMessageTemplateRenderer.cs index 9fa9ca4..c4ef9f1 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/ThemedMessageTemplateRenderer.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Rendering/ThemedMessageTemplateRenderer.cs @@ -66,8 +66,7 @@ int RenderTextToken(TextToken tt, TextWriter output) int RenderPropertyToken(PropertyToken pt, IReadOnlyDictionary properties, TextWriter output) { - LogEventPropertyValue propertyValue; - if (!properties.TryGetValue(pt.PropertyName, out propertyValue)) + if (!properties.TryGetValue(pt.PropertyName, out var propertyValue)) { var count = 0; using (_theme.Apply(output, ConsoleThemeStyle.Invalid, ref count)) @@ -103,11 +102,12 @@ int RenderPropertyToken(PropertyToken pt, IReadOnlyDictionary= pt.Alignment.Value.Width) { return RenderValue(_theme, _valueFormatter, propertyValue, output, pt.Format); @@ -116,15 +116,15 @@ int RenderAlignedPropertyTokenUnbuffered(PropertyToken pt, TextWriter output, Lo if (pt.Alignment.Value.Direction == AlignmentDirection.Left) { var invisible = RenderValue(_theme, _valueFormatter, propertyValue, output, pt.Format); - Padding.Apply(output, "", pt.Alignment.Value.Widen(-valueLength)); + Padding.Apply(output, string.Empty, pt.Alignment.Value.Widen(-valueLength)); return invisible; } - Padding.Apply(output, "", pt.Alignment.Value.Widen(-valueLength)); + Padding.Apply(output, string.Empty, pt.Alignment.Value.Widen(-valueLength)); return RenderValue(_theme, _valueFormatter, propertyValue, output, pt.Format); } - int RenderValue(ConsoleTheme theme, ThemedValueFormatter valueFormatter, LogEventPropertyValue propertyValue, TextWriter output, string format) + int RenderValue(ConsoleTheme theme, ThemedValueFormatter valueFormatter, LogEventPropertyValue propertyValue, TextWriter output, string? format) { if (_isLiteral && propertyValue is ScalarValue sv && sv.Value is string) { diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleTheme.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleTheme.cs index 77345da..ea95ee3 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleTheme.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleTheme.cs @@ -36,7 +36,7 @@ public class AnsiConsoleTheme : ConsoleTheme public static AnsiConsoleTheme Grayscale { get; } = AnsiConsoleThemes.Grayscale; /// - /// A theme in the syle of the original Serilog.Sinks.Literate. + /// A theme in the style of the original Serilog.Sinks.Literate. /// public static AnsiConsoleTheme Literate { get; } = AnsiConsoleThemes.Literate; @@ -47,9 +47,10 @@ public class AnsiConsoleTheme : ConsoleTheme /// Construct a theme given a set of styles. /// /// Styles to apply within the theme. + /// When is null public AnsiConsoleTheme(IReadOnlyDictionary styles) { - if (styles == null) throw new ArgumentNullException(nameof(styles)); + if (styles is null) throw new ArgumentNullException(nameof(styles)); _styles = styles.ToDictionary(kv => kv.Key, kv => kv.Value); } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleThemes.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleThemes.cs index 997143f..5cbb051 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleThemes.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/AnsiConsoleThemes.cs @@ -36,7 +36,7 @@ static class AnsiConsoleThemes [ConsoleThemeStyle.LevelInformation] = "\x1b[38;5;0015m", [ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0011m", [ConsoleThemeStyle.LevelError] = "\x1b[38;5;0015m\x1b[48;5;0196m", - [ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0015m\x1b[48;5;0196m" + [ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0015m\x1b[48;5;0196m", }); public static AnsiConsoleTheme Grayscale { get; } = new AnsiConsoleTheme( @@ -54,10 +54,10 @@ static class AnsiConsoleThemes [ConsoleThemeStyle.Scalar] = "\x1b[1m\x1b[37;1m", [ConsoleThemeStyle.LevelVerbose] = "\x1b[30;1m", [ConsoleThemeStyle.LevelDebug] = "\x1b[30;1m", - [ConsoleThemeStyle.LevelInformation] ="\x1b[37;1m", + [ConsoleThemeStyle.LevelInformation] = "\x1b[37;1m", [ConsoleThemeStyle.LevelWarning] = "\x1b[37;1m\x1b[47m", [ConsoleThemeStyle.LevelError] = "\x1b[30m\x1b[47m", - [ConsoleThemeStyle.LevelFatal] = "\x1b[30m\x1b[47m" + [ConsoleThemeStyle.LevelFatal] = "\x1b[30m\x1b[47m", }); public static AnsiConsoleTheme Code { get; } = new AnsiConsoleTheme( @@ -65,7 +65,7 @@ static class AnsiConsoleThemes { [ConsoleThemeStyle.Text] = "\x1b[38;5;0253m", [ConsoleThemeStyle.SecondaryText] = "\x1b[38;5;0246m", - [ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0253m", + [ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0242m", [ConsoleThemeStyle.Invalid] = "\x1b[33;1m", [ConsoleThemeStyle.Null] = "\x1b[38;5;0038m", [ConsoleThemeStyle.Name] = "\x1b[38;5;0081m", @@ -78,7 +78,7 @@ static class AnsiConsoleThemes [ConsoleThemeStyle.LevelInformation] = "\x1b[37;1m", [ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0229m", [ConsoleThemeStyle.LevelError] = "\x1b[38;5;0197m\x1b[48;5;0238m", - [ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0197m\x1b[48;5;0238m" + [ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0197m\x1b[48;5;0238m", }); } } diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleTheme.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleTheme.cs index d220cdb..b3921bf 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleTheme.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleTheme.cs @@ -37,13 +37,13 @@ public abstract class ConsoleTheme /// /// Output destination. /// Style to apply. - /// + /// The number of characters written to . public abstract int Set(TextWriter output, ConsoleThemeStyle style); /// /// Reset the output to un-styled colors. /// - /// The output. + /// Output destination. public abstract void Reset(TextWriter output); /// diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleThemeStyle.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleThemeStyle.cs index 2106801..69d231f 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleThemeStyle.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/ConsoleThemeStyle.cs @@ -33,7 +33,7 @@ public enum ConsoleThemeStyle SecondaryText, /// - /// De-emphasized text, for example literal text in output templates and + /// De-emphasized text, for example literal text in output templates and /// punctuation used when writing structured data. /// TertiaryText, @@ -63,9 +63,9 @@ public enum ConsoleThemeStyle /// Numbers. /// Number, - + /// - /// values. + /// values. /// Boolean, @@ -75,9 +75,10 @@ public enum ConsoleThemeStyle Scalar, /// - /// Unrecogized literal values, e.g. instances. + /// Unrecognized literal values, e.g. instances. /// - [Obsolete("Use ConsoleThemeStyle.Scalar instead"), EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use ConsoleThemeStyle.Scalar instead")] + [EditorBrowsable(EditorBrowsableState.Never)] Object = Scalar, /// @@ -108,6 +109,6 @@ public enum ConsoleThemeStyle /// /// Level indicator. /// - LevelFatal + LevelFatal, } } \ No newline at end of file diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/EmptyConsoleTheme.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/EmptyConsoleTheme.cs index 7be5ef4..de74f8c 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/EmptyConsoleTheme.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/EmptyConsoleTheme.cs @@ -20,12 +20,9 @@ class EmptyConsoleTheme : ConsoleTheme { public override bool CanBuffer => true; - protected override int ResetCharCount { get; } = 0; + protected override int ResetCharCount { get; } - public override int Set(TextWriter output, ConsoleThemeStyle style) - { - return 0; - } + public override int Set(TextWriter output, ConsoleThemeStyle style) => 0; public override void Reset(TextWriter output) { diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleTheme.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleTheme.cs index 828cf07..a4518c8 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleTheme.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleTheme.cs @@ -31,7 +31,7 @@ public class SystemConsoleTheme : ConsoleTheme public static SystemConsoleTheme Grayscale { get; } = SystemConsoleThemes.Grayscale; /// - /// A theme in the syle of the original Serilog.Sinks.Literate. + /// A theme in the style of the original Serilog.Sinks.Literate. /// public static SystemConsoleTheme Literate { get; } = SystemConsoleThemes.Literate; @@ -40,31 +40,30 @@ public class SystemConsoleTheme : ConsoleTheme /// public static SystemConsoleTheme Colored { get; } = SystemConsoleThemes.Colored; - readonly IReadOnlyDictionary _styles; - /// /// Construct a theme given a set of styles. /// /// Styles to apply within the theme. + /// When is null public SystemConsoleTheme(IReadOnlyDictionary styles) { - if (styles == null) throw new ArgumentNullException(nameof(styles)); - _styles = styles.ToDictionary(kv => kv.Key, kv => kv.Value); + if (styles is null) throw new ArgumentNullException(nameof(styles)); + Styles = styles.ToDictionary(kv => kv.Key, kv => kv.Value); } /// - public IReadOnlyDictionary Styles => _styles; + public IReadOnlyDictionary Styles { get; } /// public override bool CanBuffer => false; /// - protected override int ResetCharCount { get; } = 0; + protected override int ResetCharCount { get; } /// public override int Set(TextWriter output, ConsoleThemeStyle style) { - if (_styles.TryGetValue(style, out var wcts)) + if (Styles.TryGetValue(style, out var wcts)) { if (wcts.Foreground.HasValue) Console.ForegroundColor = wcts.Foreground.Value; diff --git a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleThemes.cs b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleThemes.cs index 5e77b39..5e202f9 100644 --- a/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleThemes.cs +++ b/src/Serilog.Sinks.Console/Sinks/SystemConsole/Themes/SystemConsoleThemes.cs @@ -37,7 +37,7 @@ static class SystemConsoleThemes [ConsoleThemeStyle.LevelInformation] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White }, [ConsoleThemeStyle.LevelWarning] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Yellow }, [ConsoleThemeStyle.LevelError] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red }, - [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red } + [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red }, }); public static SystemConsoleTheme Grayscale { get; } = new SystemConsoleTheme( @@ -46,7 +46,7 @@ static class SystemConsoleThemes [ConsoleThemeStyle.Text] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White }, [ConsoleThemeStyle.SecondaryText] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Gray }, [ConsoleThemeStyle.TertiaryText] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.DarkGray }, - [ConsoleThemeStyle.Invalid] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.DarkGray}, + [ConsoleThemeStyle.Invalid] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.DarkGray }, [ConsoleThemeStyle.Null] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White }, [ConsoleThemeStyle.Name] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Gray }, [ConsoleThemeStyle.String] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White }, @@ -56,9 +56,9 @@ static class SystemConsoleThemes [ConsoleThemeStyle.LevelVerbose] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.DarkGray }, [ConsoleThemeStyle.LevelDebug] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.DarkGray }, [ConsoleThemeStyle.LevelInformation] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White }, - [ConsoleThemeStyle.LevelWarning] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.DarkGray}, + [ConsoleThemeStyle.LevelWarning] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.DarkGray }, [ConsoleThemeStyle.LevelError] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Black, Background = ConsoleColor.White }, - [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Black, Background = ConsoleColor.White } + [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.Black, Background = ConsoleColor.White }, }); public static SystemConsoleTheme Colored { get; } = new SystemConsoleTheme( @@ -79,7 +79,7 @@ static class SystemConsoleThemes [ConsoleThemeStyle.LevelInformation] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Blue }, [ConsoleThemeStyle.LevelWarning] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.DarkGray, Background = ConsoleColor.Yellow }, [ConsoleThemeStyle.LevelError] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red }, - [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red } + [ConsoleThemeStyle.LevelFatal] = new SystemConsoleThemeStyle { Foreground = ConsoleColor.White, Background = ConsoleColor.Red }, }); } } diff --git a/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs new file mode 100644 index 0000000..74596c5 --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Configuration/ConsoleLoggerConfigurationExtensionsTests.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Linq; +using Xunit; +using Serilog.Sinks.SystemConsole.Themes; + +namespace Serilog.Sinks.Console.Tests.Configuration +{ + public class ConsoleLoggerConfigurationExtensionsTests + { + [Fact] + public void OutputFormattingIsIgnored() + { + using (var stream = new MemoryStream()) + { + var sw = new StreamWriter(stream); + + System.Console.SetOut(sw); + var config = new LoggerConfiguration() + .WriteTo.Console(theme: AnsiConsoleTheme.Literate, + applyThemeToRedirectedOutput: false); + + var logger = config.CreateLogger(); + + logger.Error("test"); + stream.Position = 0; + + using (var streamReader = new StreamReader(stream)) + { + var result = streamReader.ReadToEnd(); + var controlCharacterCount = result.Count(c => Char.IsControl(c) && !Char.IsWhiteSpace(c)); + Assert.Equal(0, controlCharacterCount); + } + } + } + + [Fact] + public void OutputFormattingIsPresent() + { + using (var stream = new MemoryStream()) + { + var sw = new StreamWriter(stream); + + System.Console.SetOut(sw); + var config = new LoggerConfiguration() + .WriteTo.Console(theme: AnsiConsoleTheme.Literate, + applyThemeToRedirectedOutput: true); + + var logger = config.CreateLogger(); + + logger.Error("test"); + stream.Position = 0; + + using (var streamReader = new StreamReader(stream)) + { + var result = streamReader.ReadToEnd(); + var controlCharacterCount = result.Count(c => Char.IsControl(c) && !Char.IsWhiteSpace(c)); + Assert.NotEqual(0, controlCharacterCount); + } + } + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Console.Tests/Formatting/ThemedDisplayValueFormatterTests.cs b/test/Serilog.Sinks.Console.Tests/Formatting/ThemedDisplayValueFormatterTests.cs index c5c9833..2770069 100644 --- a/test/Serilog.Sinks.Console.Tests/Formatting/ThemedDisplayValueFormatterTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Formatting/ThemedDisplayValueFormatterTests.cs @@ -11,11 +11,11 @@ public class ThemedDisplayValueFormatterTests [Theory] [InlineData("Hello", null, "\"Hello\"")] [InlineData("Hello", "l", "Hello")] - public void StringFormattingIsApplied(string s, string format, string expected) + public void StringFormattingIsApplied(string value, string format, string expected) { - var f = new ThemedDisplayValueFormatter(ConsoleTheme.None, null); + var formatter = new ThemedDisplayValueFormatter(ConsoleTheme.None, null); var sw = new StringWriter(); - f.FormatLiteralValue(new ScalarValue(s), sw, format); + formatter.FormatLiteralValue(new ScalarValue(value), sw, format); var actual = sw.ToString(); Assert.Equal(expected, actual); } diff --git a/test/Serilog.Sinks.Console.Tests/Formatting/ThemedJsonValueFormatterTests.cs b/test/Serilog.Sinks.Console.Tests/Formatting/ThemedJsonValueFormatterTests.cs index 3e28425..45a3814 100644 --- a/test/Serilog.Sinks.Console.Tests/Formatting/ThemedJsonValueFormatterTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Formatting/ThemedJsonValueFormatterTests.cs @@ -20,7 +20,7 @@ public TestThemedJsonValueFormatter() public string Format(object literal) { var output = new StringWriter(); - Format(new SequenceValue(new [] {new ScalarValue(literal)}), output, null); + Format(new SequenceValue(new[] { new ScalarValue(literal) }), output, null); var o = output.ToString(); return o.Substring(1, o.Length - 2); } @@ -114,8 +114,9 @@ public void StructuresFormatAsAnObject() [Fact] public void DictionaryWithScalarKeyFormatsAsAnObject() { - var dict = new DictionaryValue(new Dictionary { - { new ScalarValue(12), new ScalarValue(345) } + var dict = new DictionaryValue(new Dictionary + { + { new ScalarValue(12), new ScalarValue(345) }, }); var f = Format(dict); diff --git a/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs new file mode 100644 index 0000000..1a71415 --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Output/OutputTemplateRendererTests.cs @@ -0,0 +1,356 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using Serilog.Events; +using Serilog.Sinks.Console.Tests.Support; +using Serilog.Sinks.SystemConsole.Output; +using Serilog.Sinks.SystemConsole.Themes; +using Xunit; + +namespace Serilog.Sinks.Console.Tests.Output +{ + public class OutputTemplateRendererTests + { + [Fact] + public void UsesFormatProvider() + { + var french = new CultureInfo("fr-FR"); + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message}", french); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{0}", 12.345)); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("12,345", sw.ToString()); + } + + [Fact] + public void MessageTemplatesContainingFormatStringPropertiesRenderCorrectly() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{Message}", "Hello, world!")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("\"Hello, world!\"", sw.ToString()); + } + + [Fact] + public void UppercaseFormatSpecifierIsSupportedForStrings() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Name:u}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{Name}", "Nick")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("NICK", sw.ToString()); + } + + [Fact] + public void LowercaseFormatSpecifierIsSupportedForStrings() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Name:w}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{Name}", "Nick")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("nick", sw.ToString()); + } + + [Theory] + [InlineData(LogEventLevel.Verbose, 1, "V")] + [InlineData(LogEventLevel.Verbose, 2, "Vb")] + [InlineData(LogEventLevel.Verbose, 3, "Vrb")] + [InlineData(LogEventLevel.Verbose, 4, "Verb")] + [InlineData(LogEventLevel.Verbose, 5, "Verbo")] + [InlineData(LogEventLevel.Verbose, 6, "Verbos")] + [InlineData(LogEventLevel.Verbose, 7, "Verbose")] + [InlineData(LogEventLevel.Verbose, 8, "Verbose")] + [InlineData(LogEventLevel.Debug, 1, "D")] + [InlineData(LogEventLevel.Debug, 2, "De")] + [InlineData(LogEventLevel.Debug, 3, "Dbg")] + [InlineData(LogEventLevel.Debug, 4, "Dbug")] + [InlineData(LogEventLevel.Debug, 5, "Debug")] + [InlineData(LogEventLevel.Debug, 6, "Debug")] + [InlineData(LogEventLevel.Information, 1, "I")] + [InlineData(LogEventLevel.Information, 2, "In")] + [InlineData(LogEventLevel.Information, 3, "Inf")] + [InlineData(LogEventLevel.Information, 4, "Info")] + [InlineData(LogEventLevel.Information, 5, "Infor")] + [InlineData(LogEventLevel.Information, 6, "Inform")] + [InlineData(LogEventLevel.Information, 7, "Informa")] + [InlineData(LogEventLevel.Information, 8, "Informat")] + [InlineData(LogEventLevel.Information, 9, "Informati")] + [InlineData(LogEventLevel.Information, 10, "Informatio")] + [InlineData(LogEventLevel.Information, 11, "Information")] + [InlineData(LogEventLevel.Information, 12, "Information")] + [InlineData(LogEventLevel.Error, 1, "E")] + [InlineData(LogEventLevel.Error, 2, "Er")] + [InlineData(LogEventLevel.Error, 3, "Err")] + [InlineData(LogEventLevel.Error, 4, "Eror")] + [InlineData(LogEventLevel.Error, 5, "Error")] + [InlineData(LogEventLevel.Error, 6, "Error")] + [InlineData(LogEventLevel.Fatal, 1, "F")] + [InlineData(LogEventLevel.Fatal, 2, "Fa")] + [InlineData(LogEventLevel.Fatal, 3, "Ftl")] + [InlineData(LogEventLevel.Fatal, 4, "Fatl")] + [InlineData(LogEventLevel.Fatal, 5, "Fatal")] + [InlineData(LogEventLevel.Fatal, 6, "Fatal")] + [InlineData(LogEventLevel.Warning, 1, "W")] + [InlineData(LogEventLevel.Warning, 2, "Wn")] + [InlineData(LogEventLevel.Warning, 3, "Wrn")] + [InlineData(LogEventLevel.Warning, 4, "Warn")] + [InlineData(LogEventLevel.Warning, 5, "Warni")] + [InlineData(LogEventLevel.Warning, 6, "Warnin")] + [InlineData(LogEventLevel.Warning, 7, "Warning")] + [InlineData(LogEventLevel.Warning, 8, "Warning")] + public void FixedLengthLevelIsSupported( + LogEventLevel level, + int width, + string expected) + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, $"{{Level:t{width}}}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Write(level, "Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } + + [Fact] + public void FixedLengthLevelSupportsUpperCasing() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Level:u3}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("INF", sw.ToString()); + } + + [Fact] + public void FixedLengthLevelSupportsLowerCasing() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Level:w3}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("inf", sw.ToString()); + } + + [Fact] + public void DefaultLevelLengthIsFullText() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Level}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("Information", sw.ToString()); + } + + [Fact] + public void AlignmentAndWidthCanBeCombined() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Level,5:w3}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(" inf", sw.ToString()); + } + + enum Size + { + Large + } + + class SizeFormatter : IFormatProvider, ICustomFormatter + { + readonly IFormatProvider _innerFormatProvider; + + public SizeFormatter(IFormatProvider innerFormatProvider) + { + _innerFormatProvider = innerFormatProvider; + } + + public object? GetFormat(Type? formatType) + { + return formatType == typeof(ICustomFormatter) ? this : _innerFormatProvider.GetFormat(formatType) ?? this; + } + + public string Format(string? format, object? arg, IFormatProvider? formatProvider) + { + if (arg is Size size) + return size == Size.Large ? "Huge" : size.ToString(); + + if (arg is IFormattable formattable) + return formattable.ToString(format, _innerFormatProvider); + + return arg?.ToString() ?? ""; + } + } + + [Fact] + public void AppliesCustomFormatterToEnums() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message}", new SizeFormatter(CultureInfo.InvariantCulture)); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Size {Size}", Size.Large)); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("Size Huge", sw.ToString()); + } + + [Fact] + public void NonMessagePropertiesAreRendered() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Properties}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.ForContext("Foo", 42).Information("Hello from {Bar}!", "bar")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("{Foo=42}", sw.ToString()); + } + + [Fact] + public void NonMessagePositionalPropertiesAreRendered() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Properties}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.ForContext("Foo", 42).Information("Hello from {0}!", "bar")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("{Foo=42}", sw.ToString()); + } + + [Fact] + public void DoNotDuplicatePropertiesAlreadyRenderedInOutputTemplate() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Foo} {Properties}", CultureInfo.InvariantCulture); + var evt = DelegatingSink.GetLogEvent(l => l.ForContext("Foo", 42).ForContext("Bar", 42).Information("Hello from bar!")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal("42 {Bar=42}", sw.ToString()); + } + + [Theory] + [InlineData("", "Hello, \"World\"!")] + [InlineData(":j", "Hello, \"World\"!")] + [InlineData(":l", "Hello, World!")] + [InlineData(":lj", "Hello, World!")] + [InlineData(":jl", "Hello, World!")] + public void AppliesLiteralFormattingToMessageStringsWhenSpecified(string format, string expected) + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message" + format + "}", null); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello, {Name}!", "World")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } + + [Theory] + [InlineData("", "{Name=\"World\"}")] + [InlineData(":j", "{\"Name\": \"World\"}")] + [InlineData(":lj", "{\"Name\": \"World\"}")] + [InlineData(":jl", "{\"Name\": \"World\"}")] + public void AppliesJsonFormattingToMessageStructuresWhenSpecified(string format, string expected) + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message" + format + "}", null); + var evt = DelegatingSink.GetLogEvent(l => l.Information("{@Obj}", new { Name = "World" })); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } + + [Theory] + [InlineData("", "{Name=\"World\"}")] + [InlineData(":j", "{\"Name\": \"World\"}")] + [InlineData(":lj", "{\"Name\": \"World\"}")] + [InlineData(":jl", "{\"Name\": \"World\"}")] + public void AppliesJsonFormattingToPropertiesTokenWhenSpecified(string format, string expected) + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Properties" + format + "}", null); + var evt = DelegatingSink.GetLogEvent(l => l.ForContext("Name", "World").Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + Assert.Equal(expected, sw.ToString()); + } + + [Fact] + public void AnEmptyPropertiesTokenIsAnEmptyStructureValue() + { + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Properties}", null); + var evt = DelegatingSink.GetLogEvent(l => l.Information("Hello")); + var sw = new StringWriter(); + formatter.Format(evt, sw); + + // /!\ different behavior from Serilog Core : https://github.com/serilog/serilog/blob/5c3a7821aa0f654e551dc21e8e19089f6767666b/test/Serilog.Tests/Formatting/Display/MessageTemplateTextFormatterTests.cs#L268-L278 + // + // var expected = new StructureValue(Enumerable.Empty()).ToString(); + // // expected == "{ }" + // Assert.Equal(expected, sw.ToString()); + // + Assert.Equal("{}", sw.ToString()); + } + + [Theory] + [InlineData("", true)] + [InlineData(":lj", true)] + [InlineData(":jl", true)] + [InlineData(":j", false)] + [InlineData(":l", true)] + public void FormatProviderWithScalarProperties(string format, bool shouldUseCustomFormatter) + { + var frenchFormatProvider = new CultureInfo("fr-FR"); + var defaultFormatProvider = CultureInfo.InvariantCulture; + + var date = new DateTime(2018, 01, 01); + var number = 12.345; + + var expectedFormattedDate = shouldUseCustomFormatter + ? date.ToString(frenchFormatProvider) + : date.ToString("O", defaultFormatProvider); + var expectedFormattedNumber = shouldUseCustomFormatter + ? number.ToString(frenchFormatProvider) + : number.ToString(defaultFormatProvider); + + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message" + format + "}", frenchFormatProvider); + var evt = DelegatingSink.GetLogEvent(l => + { + l.Information("{MyDate}{MyNumber}", date, number); + }); + var sw = new StringWriter(); + formatter.Format(evt, sw); + + Assert.Contains(expectedFormattedDate, sw.ToString()); + Assert.Contains(expectedFormattedNumber, sw.ToString()); + } + + [Theory] + [InlineData("", true)] + [InlineData(":lj", false)] + [InlineData(":jl", false)] + [InlineData(":j", false)] + [InlineData(":l", true)] + public void FormatProviderWithDestructuredProperties(string format, bool shouldUseCustomFormatter) + { + var frenchFormatProvider = new CultureInfo("fr-FR"); + var defaultFormatProvider = CultureInfo.InvariantCulture; + + var date = new DateTime(2018, 01, 01); + var number = 12.345; + + var expectedFormattedDate = shouldUseCustomFormatter + ? date.ToString(frenchFormatProvider) + : date.ToString("O", defaultFormatProvider); + var expectedFormattedNumber = shouldUseCustomFormatter + ? number.ToString(frenchFormatProvider) + : number.ToString(defaultFormatProvider); + + var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Message" + format + "}", frenchFormatProvider); + var evt = DelegatingSink.GetLogEvent(l => + { + l.Information("{@Item}", new + { + MyDate = date, + MyNumber = number, + }); + }); + var sw = new StringWriter(); + formatter.Format(evt, sw); + + Assert.Contains(expectedFormattedDate, sw.ToString()); + Assert.Contains(expectedFormattedNumber, sw.ToString()); + } + } +} diff --git a/test/Serilog.Sinks.Console.Tests/Properties/AssemblyInfo.cs b/test/Serilog.Sinks.Console.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 4911a24..0000000 --- a/test/Serilog.Sinks.Console.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Serilog.Sinks.Trace.Tests")] -[assembly: AssemblyTrademark("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1d56534c-4009-42c2-a573-789cae6b8aa9")] diff --git a/test/Serilog.Sinks.Console.Tests/Rendering/ThemedMessageTemplateRendererTests.cs b/test/Serilog.Sinks.Console.Tests/Rendering/ThemedMessageTemplateRendererTests.cs index 18debf0..031f118 100644 --- a/test/Serilog.Sinks.Console.Tests/Rendering/ThemedMessageTemplateRendererTests.cs +++ b/test/Serilog.Sinks.Console.Tests/Rendering/ThemedMessageTemplateRendererTests.cs @@ -1,12 +1,12 @@ -using System; +using Serilog.Sinks.SystemConsole.Formatting; +using Serilog.Sinks.SystemConsole.Rendering; +using Serilog.Sinks.SystemConsole.Themes; +using System; using System.Globalization; using System.IO; using System.Linq; using System.Text; using Xunit; -using Serilog.Sinks.SystemConsole.Themes; -using Serilog.Sinks.SystemConsole.Formatting; -using Serilog.Sinks.SystemConsole.Rendering; namespace Serilog.Sinks.Console.Tests.Rendering { @@ -18,25 +18,20 @@ class Chair public string Back => "straight"; public int[] Legs => new[] { 1, 2, 3, 4 }; + // ReSharper restore UnusedMember.Local - public override string ToString() - { - return "a chair"; - } + public override string ToString() => "a chair"; } class Receipt { // ReSharper disable UnusedMember.Local public decimal Sum => 12.345m; - + public DateTime When => new DateTime(2013, 5, 20, 16, 39, 0); - // ReSharper restore UnusedMember.Local - public override string ToString() - { - return "a receipt"; - } + // ReSharper restore UnusedMember.Local + public override string ToString() => "a receipt"; } [Fact] @@ -126,10 +121,12 @@ static string Render(string messageTemplate, params object[] properties) return Render(null, messageTemplate, properties); } - static string Render(IFormatProvider formatProvider, string messageTemplate, params object[] properties) + static string Render(IFormatProvider? formatProvider, string messageTemplate, params object[] properties) { var binder = new LoggerConfiguration().CreateLogger(); - binder.BindMessageTemplate(messageTemplate, properties, out var mt, out var props); + if (binder.BindMessageTemplate(messageTemplate, properties, out var mt, out var props) == false) + throw new InvalidOperationException(); + var output = new StringBuilder(); var writer = new StringWriter(output); var renderer = new ThemedMessageTemplateRenderer(ConsoleTheme.None, diff --git a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj index dc49e7f..5e141e3 100644 --- a/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj +++ b/test/Serilog.Sinks.Console.Tests/Serilog.Sinks.Console.Tests.csproj @@ -1,19 +1,13 @@ - + - net461;netcoreapp1.1 - Serilog.Sinks.SystemConsole.Tests - Serilog.Sinks.Console.Tests + netcoreapp2.1;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net452;net462;net472;net48;net5.0 true - 1.6.0 - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.0.4 - false - false - false ../../assets/Serilog.snk true true + 8.0 + enable @@ -21,14 +15,10 @@ - - - - - - - - + + + + diff --git a/test/Serilog.Sinks.Console.Tests/Support/DelegatingSink.cs b/test/Serilog.Sinks.Console.Tests/Support/DelegatingSink.cs new file mode 100644 index 0000000..1863a41 --- /dev/null +++ b/test/Serilog.Sinks.Console.Tests/Support/DelegatingSink.cs @@ -0,0 +1,33 @@ +using System; +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.Sinks.Console.Tests.Support +{ + public class DelegatingSink : ILogEventSink + { + readonly Action _write; + + public DelegatingSink(Action write) + { + _write = write ?? throw new ArgumentNullException(nameof(write)); + } + + public void Emit(LogEvent logEvent) + { + _write(logEvent); + } + + public static LogEvent GetLogEvent(Action writeAction) + { + LogEvent? result = null; + var logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.Sink(new DelegatingSink(le => result = le)) + .CreateLogger(); + + writeAction(logger); + return result!; + } + } +}