From 1e0fbe36c48e3e2e61d9e8190c5beb3857f65086 Mon Sep 17 00:00:00 2001 From: River Phillips Date: Tue, 20 Oct 2020 16:37:35 +0100 Subject: [PATCH 01/42] Upgrade to app insights 2.15.0 Signed-off-by: River Phillips --- .../Serilog.Sinks.ApplicationInsights.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 6defc7e..34c9017 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -29,7 +29,7 @@ - + From 6b1fa702a69ebdefddf6d9d5e666cc3947df1af2 Mon Sep 17 00:00:00 2001 From: River Phillips Date: Tue, 20 Oct 2020 17:04:16 +0100 Subject: [PATCH 02/42] Revert "Upgrade to app insights 2.15.0" This reverts commit 1e0fbe36c48e3e2e61d9e8190c5beb3857f65086. --- .../Serilog.Sinks.ApplicationInsights.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 34c9017..6defc7e 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -29,7 +29,7 @@ - + From ece2a7be7b4843e17cb60d7c61aa950aa284f5c6 Mon Sep 17 00:00:00 2001 From: River Phillips Date: Tue, 20 Oct 2020 17:09:16 +0100 Subject: [PATCH 03/42] Make .NET 4.5.2 minimum targeted version --- .../Serilog.Sinks.ApplicationInsights.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 6defc7e..336cc3f 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -4,7 +4,7 @@ Serilog event sink that writes to Microsoft Application Insights. 2.5.0 Joerg Battermann, Ivan Gavryliuk - net45;netstandard1.3;netstandard1.6;netstandard2.0 + net452;netstandard1.3;netstandard1.6;netstandard2.0 Serilog.Sinks.ApplicationInsights ../../assets/Serilog.snk true @@ -23,7 +23,7 @@ 3.1.0 - + @@ -32,7 +32,7 @@ - + From 4db9ef0203b53a133682d4ec73ec76dfe1ebf96f Mon Sep 17 00:00:00 2001 From: River Phillips Date: Wed, 21 Oct 2020 10:51:14 +0100 Subject: [PATCH 04/42] Update target frameworks to support all versions that app insights 2.15.0 supports Signed-off-by: River Phillips --- .../Serilog.Sinks.ApplicationInsights.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 336cc3f..3dfe294 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -4,7 +4,7 @@ Serilog event sink that writes to Microsoft Application Insights. 2.5.0 Joerg Battermann, Ivan Gavryliuk - net452;netstandard1.3;netstandard1.6;netstandard2.0 + net452;net46;netstandard2.0 Serilog.Sinks.ApplicationInsights ../../assets/Serilog.snk true From 956a963a917c1cf9835fec4026c5b35d51e56f97 Mon Sep 17 00:00:00 2001 From: River Phillips Date: Wed, 21 Oct 2020 10:54:48 +0100 Subject: [PATCH 05/42] Updgrade to app insights 2.15.0 Signed-off-by: River Phillips --- .../Serilog.Sinks.ApplicationInsights.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 3dfe294..08d781b 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -29,7 +29,7 @@ - + From 081b44ac3c860da6b72b3fa6640e1739b2156c73 Mon Sep 17 00:00:00 2001 From: Jorge Bustos Date: Tue, 6 Apr 2021 13:21:44 +0200 Subject: [PATCH 06/42] Add logging level swithc to config --- ...figurationApplicationInsightsExtensions.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs index 5f697be..32c9704 100644 --- a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs +++ b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs @@ -16,6 +16,7 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; using Serilog.Configuration; +using Serilog.Core; using Serilog.Events; using Serilog.Sinks.ApplicationInsights; using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; @@ -36,16 +37,18 @@ public static class LoggerConfigurationApplicationInsightsExtensions /// Required Application Insights configuration settings. /// Required telemetry converter. /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink /// public static LoggerConfiguration ApplicationInsights( this LoggerSinkConfiguration loggerConfiguration, TelemetryConfiguration telemetryConfiguration, ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { var client = new TelemetryClient(telemetryConfiguration ?? TelemetryConfiguration.Active); - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel); + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); } /// @@ -55,16 +58,18 @@ public static LoggerConfiguration ApplicationInsights( /// The logger configuration. /// Required telemetry converter. /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink /// public static LoggerConfiguration ApplicationInsights( this LoggerSinkConfiguration loggerConfiguration, ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { var client = new TelemetryClient(TelemetryConfiguration.Active); - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel); + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); } /// @@ -75,14 +80,16 @@ public static LoggerConfiguration ApplicationInsights( /// Required Application Insights telemetry client. /// Required telemetry converter. /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink /// public static LoggerConfiguration ApplicationInsights( this LoggerSinkConfiguration loggerConfiguration, TelemetryClient telemetryClient, ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { - return loggerConfiguration.Sink(new ApplicationInsightsSink(telemetryClient, telemetryConverter), restrictedToMinimumLevel); + return loggerConfiguration.Sink(new ApplicationInsightsSink(telemetryClient, telemetryConverter), restrictedToMinimumLevel, levelSwitch); } @@ -95,12 +102,14 @@ public static LoggerConfiguration ApplicationInsights( /// Required Application Insights key. /// Required telemetry converter. /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink /// public static LoggerConfiguration ApplicationInsights( this LoggerSinkConfiguration loggerConfiguration, string instrumentationKey, ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { var client = new TelemetryClient(); @@ -109,7 +118,7 @@ public static LoggerConfiguration ApplicationInsights( client.InstrumentationKey = instrumentationKey; } - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel); + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); } } } From 32f909f99b210dd5fbf9f336bbb4e21950f4f8a2 Mon Sep 17 00:00:00 2001 From: Diego Garcia Lozano Date: Sat, 10 Apr 2021 13:50:23 +0200 Subject: [PATCH 07/42] Update README.md Add documentation on how to use the sink using .Net Core now that `TelemetryConfiguration.Active` has been deprecated. --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cfbca55..3463432 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,58 @@ var log = new LoggerConfiguration() **Note:** Whether you choose `Events` or `Traces`, if the LogEvent contains any exceptions it will always be sent as `ExceptionTelemetry`. +### TelemetryConfiguration.Active is deprecated in the App Insights SDK for .NET Core, what do I do? + +The singleton [`TelemetryConfiguration.Active` has been deprecated in the Application Insights SDK on .NET Core in favor of dependency injection pattern](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152). + +Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added by `services.AddApplicationInsightsTelemetry()` during Startup in `ConfigureServices`. + +```csharp +var log = new LoggerConfiguration() + .WriteTo + .ApplicationInsights(serviceProvider.GetRequiredService(), TelemetryConverter.Traces) + .CreateLogger(); +``` + +However, you probably want to setup your Logger as close to the entry point of your application as possible, so that any startup errors can be caught and properly logged. The problem is that now we're in a chicken-and-egg situation: we want to setup the logger early, but we need the `TelemetryConfiguration` which still haven't been added to our DI container. + +Luckily [from version 4.0.x of the `Serilog.Extensions.Hosting` we have the possibility to configure a bootstrap logger](https://nblumhardt.com/2020/10/bootstrap-logger/) to capture early errors, and then change it using DI dependant services once they are configured. + +```csharp +// dotnet add package serilog.extensions.hosting -v 4.0.0-* + +public static class Program +{ + public static void Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateBootstrapLogger(); + + try + { + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog((context, services, loggerConfiguration) => + loggerConfiguration + .WriteTo + .ApplicationInsights(services.GetRequiredService(), TelemetryConverter.Traces)) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); +} +``` + ### Configuring with ReadFrom.Configuration() The following configuration shows how to create an ApplicationInsights sink with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration) - the telemetry converter has to be specified with the full type name and the assembly name: @@ -299,6 +351,6 @@ public static class MyFunctions } ``` -Copyright © 2019 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html). +Copyright © 2021 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html). See also: [Serilog Documentation](https://github.com/serilog/serilog/wiki) From 33c9f0792c6acb9d3f1c6b2a1ac6f979b6bcc623 Mon Sep 17 00:00:00 2001 From: Geovanny Alzate Sandoval Date: Wed, 26 May 2021 21:03:19 -0500 Subject: [PATCH 08/42] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3463432..7afbd6c 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ var log = new LoggerConfiguration() The singleton [`TelemetryConfiguration.Active` has been deprecated in the Application Insights SDK on .NET Core in favor of dependency injection pattern](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152). -Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added by `services.AddApplicationInsightsTelemetry()` during Startup in `ConfigureServices`. +Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added either by `services.AddApplicationInsightsTelemetryWorkerService()` (if you're developing a [non-http applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service)) or `services.AddApplicationInsightsTelemetry()` (if you're developing an [ASP.Net Core applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core)) during Startup in `ConfigureServices`. ```csharp var log = new LoggerConfiguration() From 7fd17916a58a38341e2248798c0a363cbe855103 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 19 Aug 2021 16:30:16 +1000 Subject: [PATCH 09/42] Build and packaging updates - Update to the latest Serilog org `Build.ps1` and `appveyor.yml` (incl. finds tests in `tests/`) - Fresh NuGet.org publishing key - Use `` so that dev packages can be built - Run tests on currently-supported .NET/core versions Via #168 --- .gitignore | 1 + Build.ps1 | 25 +++++++++++++------ appveyor.yml | 16 +++--------- serilog-sinks-applicationinsights.sln | 14 ++++++----- .../Serilog.Sinks.ApplicationInsights.csproj | 3 +-- .../ApplicationInsightsTest.cs | 0 .../CustomTelemetryConversionTest.cs | 0 .../CustomiseEventTelemetryConverterTest.cs | 0 .../DottedOutFormattingTest.cs | 0 .../FormattingTests.cs | 0 ...log.Sinks.ApplicationInsights.Tests.csproj | 5 ++-- .../TelemetryConversionTest.cs | 0 .../UnitTestTelemetryChannel.cs | 0 13 files changed, 34 insertions(+), 30 deletions(-) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj (73%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs (100%) rename {src => test}/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs (100%) diff --git a/.gitignore b/.gitignore index 219f7c3..791717f 100644 --- a/.gitignore +++ b/.gitignore @@ -182,3 +182,4 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ +/.idea diff --git a/Build.ps1 b/Build.ps1 index 6dbb2ff..3328331 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -16,7 +16,7 @@ $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" +echo "build: Build version suffix is $buildSuffix" foreach ($src in ls src/*) { Push-Location $src @@ -25,14 +25,25 @@ foreach ($src in ls src/*) { & dotnet build -c Release --version-suffix=$buildSuffix - if ($suffix) { - & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts --version-suffix=$suffix + if($suffix) { + & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts --version-suffix=$suffix } else { - & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts - } - if($LASTEXITCODE -ne 0) { exit 1 } + & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts + } + if($LASTEXITCODE -ne 0) { exit 1 } Pop-Location } -Pop-Location \ No newline at end of file +foreach ($test in ls test/*.Tests) { + Push-Location $test + + echo "build: Testing project in $test" + + & dotnet test -c Release + if($LASTEXITCODE -ne 0) { exit 3 } + + Pop-Location +} + +Pop-Location diff --git a/appveyor.yml b/appveyor.yml index 5c4cb89..aa20d35 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,24 +1,16 @@ version: '{build}' skip_tags: true -image: Visual Studio 2017 +image: Visual Studio 2019 configuration: Release -test_script: -- cmd: dotnet test src/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj +test: off build_script: - ps: ./Build.ps1 artifacts: - path: artifacts/Serilog.*.nupkg -only_commits: - files: - - serilog-sinks-applicationinsights.sln - - src/Serilog.Sinks.ApplicationInsights/ - - Build.ps1 - - assets/ - - appveyor.yml deploy: - provider: NuGet api_key: - secure: N59tiJECUYpip6tEn0xvdmDAEiP9SIzyLEFLpwiigm/8WhJvBNs13QxzT1/3/JW/ + secure: rbdBqxBpLt4MkB+mrDOYNDOd8aVZ1zMkysaVNAXNKnC41FYifzX3l9LM8DCrUWU5 skip_symbols: true on: branch: /^(master|dev)$/ @@ -28,4 +20,4 @@ deploy: artifact: /Serilog.*\.nupkg/ tag: v$(appveyor_build_version) on: - branch: master \ No newline at end of file + branch: master diff --git a/serilog-sinks-applicationinsights.sln b/serilog-sinks-applicationinsights.sln index f3769b8..ef8921a 100644 --- a/serilog-sinks-applicationinsights.sln +++ b/serilog-sinks-applicationinsights.sln @@ -17,7 +17,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.ApplicationInsights", "src\Serilog.Sinks.ApplicationInsights\Serilog.Sinks.ApplicationInsights.csproj", "{B572E129-3568-435F-A8E9-366AD7014D8E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.ApplicationInsights.Tests", "src\Serilog.Sinks.ApplicationInsights.Tests\Serilog.Sinks.ApplicationInsights.Tests.csproj", "{B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7D3C2A24-D009-41B5-B68D-2B87F7C017FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.ApplicationInsights.Tests", "test\Serilog.Sinks.ApplicationInsights.Tests\Serilog.Sinks.ApplicationInsights.Tests.csproj", "{94A45A9E-5576-4B49-A769-DD62B83B25A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,17 +31,17 @@ Global {B572E129-3568-435F-A8E9-366AD7014D8E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B572E129-3568-435F-A8E9-366AD7014D8E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B572E129-3568-435F-A8E9-366AD7014D8E}.Release|Any CPU.Build.0 = Release|Any CPU - {B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3}.Release|Any CPU.Build.0 = Release|Any CPU + {94A45A9E-5576-4B49-A769-DD62B83B25A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94A45A9E-5576-4B49-A769-DD62B83B25A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94A45A9E-5576-4B49-A769-DD62B83B25A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94A45A9E-5576-4B49-A769-DD62B83B25A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {B572E129-3568-435F-A8E9-366AD7014D8E} = {037440DE-440B-4129-9F7A-09B42D00397E} - {B20E07E8-FA9F-4CC4-926D-2A23A4BF9EF3} = {037440DE-440B-4129-9F7A-09B42D00397E} + {94A45A9E-5576-4B49-A769-DD62B83B25A2} = {7D3C2A24-D009-41B5-B68D-2B87F7C017FB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {08126E49-8621-4994-8B2C-DF15B3D9A881} diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 6defc7e..63dec70 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -2,7 +2,7 @@ Serilog event sink that writes to Microsoft Application Insights. - 2.5.0 + 3.1.1 Joerg Battermann, Ivan Gavryliuk net45;netstandard1.3;netstandard1.6;netstandard2.0 Serilog.Sinks.ApplicationInsights @@ -20,7 +20,6 @@ false false true - 3.1.0 diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj similarity index 73% rename from src/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj rename to test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj index ea0dc33..50c1578 100644 --- a/src/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj @@ -1,8 +1,7 @@ - netcoreapp2.2 - + net5.0;netcoreapp3.1;netcoreapp2.1 false @@ -16,7 +15,7 @@ - + diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs diff --git a/src/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs similarity index 100% rename from src/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs rename to test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs From 4026467fc4c09e0c3d8ce8c8a7f7272539d584d3 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 31 Aug 2021 09:39:57 +1000 Subject: [PATCH 10/42] Point build badge to dev; master will not be green until the next major release, since its version number is non-incrementing and this causes publishing to fail --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cfbca55..ca1729c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A sink for Serilog that writes events to Microsoft Application Insights. -[![Build status](https://ci.appveyor.com/api/projects/status/ccgd7k98kbmifl5v/branch/master?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-applicationinsights/branch/master) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) +[![Build status](https://ci.appveyor.com/api/projects/status/ccgd7k98kbmifl5v/branch/dev?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-applicationinsights/branch/dev) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) This Sink comes with several defaults that send Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. From c9b18772fc99353baf41604d3b9d71c4eb46b782 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sat, 2 Oct 2021 14:40:16 +1000 Subject: [PATCH 11/42] Update Serilog to 2.10 and App Insights to 2.18 --- .../Serilog.Sinks.ApplicationInsights.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 08d781b..1ad4ea0 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -1,4 +1,4 @@ - + Serilog event sink that writes to Microsoft Application Insights. @@ -28,8 +28,8 @@ - - + + From 0aaad401fef4a5b3e5d0e8a9ffea811c01201e7d Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 2 May 2022 08:13:12 +1000 Subject: [PATCH 12/42] Add GitHub issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 27 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 4 ++++ .github/ISSUE_TEMPLATE/feature_request.md | 17 ++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..13efc81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Report a bug and help us to improve Serilog.Sinks.ApplicationInsights +title: '' +labels: bug +assignees: '' + +--- + +The maintainers want you to have a great experience using Serilog.Sinks.ApplicationInsights, and will happily track down and resolve bugs. We all have limited time, though, so please think through all of the factors that might be involved and include as much useful information as possible 😊. + +👉 Please ensure that this is the most appropriate repository for your bug report. Only reports for bugs directly caused by code in this specific repository can be accepted. + +**Description** +What's going wrong? + +**Reproduction** +Please provide code samples showing how you're configuring and calling the sink and Serilog to produce the behavior. + +**Expected behavior** +A concise description of what you expected to happen. + +**Relevant package, tooling and runtime versions** +What package version are you using, on what platform? + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ee427f8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Ask for help + url: https://stackoverflow.com/questions/tagged/serilog + about: Ask the community for help with using Serilog and this sink diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..f27f6ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an improvement to Serilog.Sinks.ApplicationInsights +title: '' +labels: enhancement +assignees: '' + +--- + +**Describe your suggestion** +A clear and concise description of what you want to happen. Please include enough information about your reasoning and expected benefits: feature suggestions need to go into _why_ the feature is needed. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From d87919799318187be0e5c61e8d3d9e8e2f302a3f Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 2 May 2022 08:29:27 +1000 Subject: [PATCH 13/42] Add GitHub Actions build and publishing --- .github/workflows/build.yml | 21 ++++++++++++ Build.ps1 | 67 ++++++++++++++++--------------------- README.md | 2 +- appveyor.yml | 23 ------------- 4 files changed, 50 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 appveyor.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..75d9788 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +name: Build + +on: [push, pull_request] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + - name: Invoke Build.ps1 with PowerShell Core + shell: pwsh + run: ./Build.ps1 + - name: Push + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + shell: pwsh + run: | + dotnet nuget push (get-item ./artifacts/*.nupkg).FullName --api-key="$env:NUGET_API_KEY" -s https://api.nuget.org/v3/index.json + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} diff --git a/Build.ps1 b/Build.ps1 index 3328331..961866b 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -2,48 +2,37 @@ echo "build: Build started" Push-Location $PSScriptRoot -if(Test-Path .\artifacts) { - echo "build: Cleaning .\artifacts" - Remove-Item .\artifacts -Force -Recurse +if (Test-Path ./artifacts) { + echo "build: Cleaning ./artifacts" + Remove-Item ./artifacts -Force -Recurse } +echo "build: Restoring" & dotnet restore --no-cache - -$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"] -$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" - -foreach ($src in ls src/*) { - Push-Location $src - - echo "build: Packaging project in $src" - - & dotnet build -c Release --version-suffix=$buildSuffix - - if($suffix) { - & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts --version-suffix=$suffix - } else { - & dotnet pack -c Release --include-source --no-build -o ..\..\artifacts - } - if($LASTEXITCODE -ne 0) { exit 1 } - - Pop-Location -} - -foreach ($test in ls test/*.Tests) { - Push-Location $test - - echo "build: Testing project in $test" - - & dotnet test -c Release - if($LASTEXITCODE -ne 0) { exit 3 } - - Pop-Location +if($LASTEXITCODE -ne 0) { exit 1 } + +$projectName = "Serilog.Sinks.ApplicationInsights" +$ref = $env:GITHUB_REF ?? "" +$run = $env:GITHUB_RUN_NUMBER ?? "0" +$branch = @{ $true = $ref.Substring($ref.LastIndexOf("/") + 1); $false = $(git symbolic-ref --short -q HEAD) }[$ref -ne ""]; +$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $run, 10); $false = "local" }[$run -ne "0"]; +$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] + +echo "build: Version suffix is $suffix" + +echo "build: Testing" +& dotnet test -c Release "./test/$projectName/$projectName.Tests.csproj" +if ($LASTEXITCODE -ne 0) { exit 3 } + +echo "build: Publishing and packing" +$src = "./src/$projectName" +if ($suffix) { + & dotnet publish -c Release -o "$src/obj/publish" --version-suffix=$suffix "$src/$projectName.csproj" + & dotnet pack -c Release -o ./artifacts --no-build --version-suffix=$suffix "$src/$projectName.csproj" +} else { + & dotnet publish -c Release -o "$src/obj/publish" "$src/$projectName.csproj" + & dotnet pack -c Release -o ./artifacts --no-build "$src/$projectName.csproj" } +if ($LASTEXITCODE -ne 0) { exit 1 } Pop-Location diff --git a/README.md b/README.md index 91d1765..e462613 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A sink for Serilog that writes events to Microsoft Application Insights. -[![Build status](https://ci.appveyor.com/api/projects/status/ccgd7k98kbmifl5v/branch/dev?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-applicationinsights/branch/dev) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) +[![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) This Sink comes with several defaults that send Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index aa20d35..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '{build}' -skip_tags: true -image: Visual Studio 2019 -configuration: Release -test: off -build_script: -- ps: ./Build.ps1 -artifacts: -- path: artifacts/Serilog.*.nupkg -deploy: -- provider: NuGet - api_key: - secure: rbdBqxBpLt4MkB+mrDOYNDOd8aVZ1zMkysaVNAXNKnC41FYifzX3l9LM8DCrUWU5 - skip_symbols: true - on: - branch: /^(master|dev)$/ -- provider: GitHub - auth_token: - secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX - artifact: /Serilog.*\.nupkg/ - tag: v$(appveyor_build_version) - on: - branch: master From 9aba89683e727735dd0e4e318542b9f454846655 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 2 May 2022 08:40:01 +1000 Subject: [PATCH 14/42] Dependency updates, CSPROJ modernization --- Build.ps1 | 2 +- assets/CommonAssemblyInfo.cs | 5 ---- assets/icon.png | Bin 0 -> 20352 bytes .../Properties/AssemblyInfo.cs | 12 -------- .../Serilog.Sinks.ApplicationInsights.csproj | 27 +++++++++--------- 5 files changed, 15 insertions(+), 31 deletions(-) delete mode 100644 assets/CommonAssemblyInfo.cs create mode 100644 assets/icon.png delete mode 100644 src/Serilog.Sinks.ApplicationInsights/Properties/AssemblyInfo.cs diff --git a/Build.ps1 b/Build.ps1 index 961866b..99ee2ff 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -21,7 +21,7 @@ $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch echo "build: Version suffix is $suffix" echo "build: Testing" -& dotnet test -c Release "./test/$projectName/$projectName.Tests.csproj" +& dotnet test -c Release "./test/$projectName.Tests/$projectName.Tests.csproj" if ($LASTEXITCODE -ne 0) { exit 3 } echo "build: Publishing and packing" diff --git a/assets/CommonAssemblyInfo.cs b/assets/CommonAssemblyInfo.cs deleted file mode 100644 index 0f5c380..0000000 --- a/assets/CommonAssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.1.1.1")] -[assembly: AssemblyInformationalVersion("1.0.0")] diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9bf45a319eec8b1a6a2641c282739944d100c9c1 GIT binary patch literal 20352 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 literal 0 HcmV?d00001 diff --git a/src/Serilog.Sinks.ApplicationInsights/Properties/AssemblyInfo.cs b/src/Serilog.Sinks.ApplicationInsights/Properties/AssemblyInfo.cs deleted file mode 100644 index 2eac124..0000000 --- a/src/Serilog.Sinks.ApplicationInsights/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("Serilog.Sinks.ApplicationInsights")] -[assembly: AssemblyDescription("Serilog sink for Microsoft Application Insights")] -[assembly: AssemblyCopyright("Copyright © Serilog Contributors 2018")] - -[assembly: InternalsVisibleTo("Serilog.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + - "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + - "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + - "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + - "b19485ec")] \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index f873638..ca76bf5 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -1,24 +1,21 @@ - + - Serilog event sink that writes to Microsoft Application Insights. + Serilog sink for Application Insights. 3.1.1 - Joerg Battermann, Ivan Gavryliuk - net452;net46;netstandard2.0 + Joerg Battermann, Ivan Gavryliuk, contributors + net452;net46;netstandard2.0;net6.0 Serilog.Sinks.ApplicationInsights ../../assets/Serilog.snk true true Serilog.Sinks.ApplicationInsights serilog;logging;azure;applicationinsights;application;insights - http://serilog.net/images/serilog-sink-nuget.png - https://github.com/serilog/serilog-sinks-applicationinsights - http://www.apache.org/licenses/LICENSE-2.0 - https://github.com/serilog/serilog-sinks-applicationinsights + icon.png + https://github.com/serilog-contrib/serilog-sinks-applicationinsights + Apache-2.0 + https://github.com/serilog-contrib/serilog-sinks-applicationinsights git - false - false - false true @@ -27,8 +24,8 @@ - - + + @@ -36,4 +33,8 @@ + + + + From 8e4e26a8fdfa12da6ed15afcc94889e5f399ff97 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 2 May 2022 09:01:08 +1000 Subject: [PATCH 15/42] Fix namespaces --- .github/workflows/build.yml | 2 +- Build.ps1 | 12 ++++++------ global.json | 1 - serilog-sinks-applicationinsights.sln | 9 +++++---- ...erConfigurationApplicationInsightsExtensions.cs | 2 +- .../Serilog.Sinks.ApplicationInsights.csproj | 14 +++----------- .../ApplicationInsights/ApplicationInsightsSink.cs | 2 +- .../ApplicationInsightsDottedValueFormatter.cs | 2 +- .../ApplicationInsightsJsonValueFormatter.cs | 2 +- .../Formatters/IValueFormatter.cs | 6 +++--- .../TelemetryConverters/EventTelemetryConverter.cs | 2 +- .../TelemetryConverters/ITelemetryConverter.cs | 11 ++++------- .../TelemetryConverters/TelemetryConverterBase.cs | 4 ++-- .../TelemetryConverters/TraceTelemetryConverter.cs | 2 +- .../TelemetryConverter.cs | 2 +- .../ApplicationInsightsTest.cs | 11 +++-------- .../CustomTelemetryConversionTest.cs | 2 +- .../CustomiseEventTelemetryConverterTest.cs | 4 +--- .../DottedOutFormattingTest.cs | 7 ++----- .../Serilog.Sinks.ApplicationInsights.Tests.csproj | 2 +- .../TelemetryConversionTest.cs | 2 +- 21 files changed, 40 insertions(+), 61 deletions(-) delete mode 100644 global.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75d9788..516a924 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: windows-latest steps: - name: Check out the repository uses: actions/checkout@v2 diff --git a/Build.ps1 b/Build.ps1 index 99ee2ff..c61d48d 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -20,19 +20,19 @@ $suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch echo "build: Version suffix is $suffix" -echo "build: Testing" & dotnet test -c Release "./test/$projectName.Tests/$projectName.Tests.csproj" -if ($LASTEXITCODE -ne 0) { exit 3 } +if ($LASTEXITCODE -ne 0) { throw "dotnet test failed" } -echo "build: Publishing and packing" $src = "./src/$projectName" + +& dotnet build -c Release --version-suffix=$suffix "$src/$projectName.csproj" +if ($LASTEXITCODE -ne 0) { throw "dotnet build failed" } + if ($suffix) { - & dotnet publish -c Release -o "$src/obj/publish" --version-suffix=$suffix "$src/$projectName.csproj" & dotnet pack -c Release -o ./artifacts --no-build --version-suffix=$suffix "$src/$projectName.csproj" } else { - & dotnet publish -c Release -o "$src/obj/publish" "$src/$projectName.csproj" & dotnet pack -c Release -o ./artifacts --no-build "$src/$projectName.csproj" } -if ($LASTEXITCODE -ne 0) { exit 1 } +if ($LASTEXITCODE -ne 0) { throw "dotnet pack failed" } Pop-Location diff --git a/global.json b/global.json deleted file mode 100644 index bd52dc5..0000000 --- a/global.json +++ /dev/null @@ -1 +0,0 @@ -{"projects":["src"]} \ No newline at end of file diff --git a/serilog-sinks-applicationinsights.sln b/serilog-sinks-applicationinsights.sln index ef8921a..527e542 100644 --- a/serilog-sinks-applicationinsights.sln +++ b/serilog-sinks-applicationinsights.sln @@ -5,14 +5,15 @@ VisualStudioVersion = 15.0.26621.2 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440B-4129-9F7A-09B42D00397E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 - assets\CommonAssemblyInfo.cs = assets\CommonAssemblyInfo.cs README.md = README.md - assets\Serilog.snk = assets\Serilog.snk + .gitattributes = .gitattributes + .gitignore = .gitignore + CHANGES.md = CHANGES.md + LICENSE = LICENSE EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.ApplicationInsights", "src\Serilog.Sinks.ApplicationInsights\Serilog.Sinks.ApplicationInsights.csproj", "{B572E129-3568-435F-A8E9-366AD7014D8E}" diff --git a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs index 5f697be..0fc9ec1 100644 --- a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs +++ b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs @@ -18,7 +18,7 @@ using Serilog.Configuration; using Serilog.Events; using Serilog.Sinks.ApplicationInsights; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; namespace Serilog { diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index ca76bf5..532cf5d 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -2,9 +2,9 @@ Serilog sink for Application Insights. - 3.1.1 + 4.0.0 Joerg Battermann, Ivan Gavryliuk, contributors - net452;net46;netstandard2.0;net6.0 + net4.6.2;netstandard2.0;net6.0 Serilog.Sinks.ApplicationInsights ../../assets/Serilog.snk true @@ -17,10 +17,7 @@ https://github.com/serilog-contrib/serilog-sinks-applicationinsights git true - - - - + Serilog @@ -28,11 +25,6 @@ - - - - - diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs index 98ca990..cdb21f6 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs @@ -21,7 +21,7 @@ using Microsoft.ApplicationInsights.Channel; using Serilog.Core; using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; namespace Serilog.Sinks.ApplicationInsights { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs index 3f76d45..ce4742b 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs @@ -4,7 +4,7 @@ using Serilog.Debugging; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters { public class ApplicationInsightsDottedValueFormatter : IValueFormatter { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs index d40f290..f308e17 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs @@ -4,7 +4,7 @@ using Serilog.Events; using Serilog.Formatting.Json; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters { public class ApplicationInsightsJsonValueFormatter : IValueFormatter { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs index 15dd701..c8745d6 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs @@ -1,7 +1,7 @@ -using Serilog.Events; -using System.Collections.Generic; +using System.Collections.Generic; +using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters { public interface IValueFormatter { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs index 1bd3103..e530ff9 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs @@ -4,7 +4,7 @@ using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { public class EventTelemetryConverter : TelemetryConverterBase { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs index f041657..0d4b940 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs @@ -1,12 +1,9 @@ -using Microsoft.ApplicationInsights.Channel; -using Serilog.Events; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.ApplicationInsights.Channel; +using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { public interface ITelemetryConverter { diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index 2635576..3784afd 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -4,9 +4,9 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.Formatters; +using Serilog.Sinks.ApplicationInsights.Formatters; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { /// /// Base class for telemetry converters diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index cb74f6e..36ccc92 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -4,7 +4,7 @@ using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { public class TraceTelemetryConverter : TelemetryConverterBase { diff --git a/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs index 95e4e11..88c0eec 100644 --- a/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; namespace Serilog { diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs index 9118af4..e45e631 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs @@ -1,13 +1,9 @@ -using Microsoft.ApplicationInsights; -using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; -using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; -using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; namespace Serilog.Sinks.ApplicationInsights.Tests { @@ -34,8 +30,7 @@ protected ApplicationInsightsTest(ITelemetryConverter converter = null) protected TraceTelemetry LastSubmittedTraceTelemetry => _channel.SubmittedTelemetry - .Where(t => t is TraceTelemetry) - .Select(t => (TraceTelemetry)t) + .OfType() .LastOrDefault(); } diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs index 2f1a09f..2c2c06b 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs @@ -1,10 +1,10 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; using System; using System.Collections.Generic; using System.Linq; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; namespace Serilog.Sinks.ApplicationInsights.Tests diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs index 6dbb4df..a1625f3 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; -using System.Text; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; namespace Serilog.Sinks.ApplicationInsights.Tests { diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs index 17a4aca..2e7596a 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.Formatters; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; +using Serilog.Sinks.ApplicationInsights.Formatters; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; namespace Serilog.Sinks.ApplicationInsights.Tests diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj index 50c1578..e03b2f8 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj @@ -1,7 +1,7 @@ - net5.0;netcoreapp3.1;netcoreapp2.1 + net6.0;net4.8 false diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs index 0a79a13..9f035ee 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs @@ -1,10 +1,10 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -using Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters; using System; using System.Collections.Generic; using System.Linq; +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; namespace Serilog.Sinks.ApplicationInsights.Tests From 44839c95ebdda74e949117f90219f7535dc53d27 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 2 May 2022 10:36:53 +1000 Subject: [PATCH 16/42] Trivial README formatting --- README.md | 59 ++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index e462613..3e427ce 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,14 @@ -# Serilog.Sinks.ApplicationInsights +# Serilog.Sinks.ApplicationInsights [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) -A sink for Serilog that writes events to Microsoft Application Insights. - -[![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) - -This Sink comes with several defaults that send Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. +A sink for Serilog that writes events to Microsoft Application Insights. This sink comes with several defaults that send Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. ## Configuring -The simplest way to configure Serilog to send data to a ApplicationInsights dashboard via Instrumentation key is to use current active *telemetry configuration* which is already initialised in most application types like ASP.NET Core, Azure Functions etc.: +The simplest way to configure Serilog to send data to a Application Insights dashboard via instrumentation key is to use current active *telemetry configuration* which is already initialised in most application types like ASP.NET Core, Azure Functions etc.: ```csharp var log = new LoggerConfiguration() - .WriteTo - .ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces) + .WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces) .CreateLogger(); ``` @@ -23,8 +18,7 @@ var log = new LoggerConfiguration() ```csharp var log = new LoggerConfiguration() - .WriteTo - .ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Events) + .WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Events) .CreateLogger(); ``` @@ -32,16 +26,17 @@ var log = new LoggerConfiguration() **Note:** Whether you choose `Events` or `Traces`, if the LogEvent contains any exceptions it will always be sent as `ExceptionTelemetry`. -### TelemetryConfiguration.Active is deprecated in the App Insights SDK for .NET Core, what do I do? +### `TelemetryConfiguration.Active` is deprecated in the App Insights SDK for .NET Core, what do I do? The singleton [`TelemetryConfiguration.Active` has been deprecated in the Application Insights SDK on .NET Core in favor of dependency injection pattern](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152). Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added either by `services.AddApplicationInsightsTelemetryWorkerService()` (if you're developing a [non-http applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service)) or `services.AddApplicationInsightsTelemetry()` (if you're developing an [ASP.Net Core applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core)) during Startup in `ConfigureServices`. ```csharp -var log = new LoggerConfiguration() - .WriteTo - .ApplicationInsights(serviceProvider.GetRequiredService(), TelemetryConverter.Traces) +Log.Logger = new LoggerConfiguration() + .WriteTo.ApplicationInsights( + serviceProvider.GetRequiredService(), + TelemetryConverter.Traces) .CreateLogger(); ``` @@ -76,15 +71,15 @@ public static class Program public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .UseSerilog((context, services, loggerConfiguration) => - loggerConfiguration - .WriteTo - .ApplicationInsights(services.GetRequiredService(), TelemetryConverter.Traces)) + .UseSerilog((context, services, loggerConfiguration) => loggerConfiguration + .WriteTo.ApplicationInsights( + services.GetRequiredService(), + TelemetryConverter.Traces)) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } ``` -### Configuring with ReadFrom.Configuration() +### Configuring with `ReadFrom.Configuration()` The following configuration shows how to create an ApplicationInsights sink with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration) - the telemetry converter has to be specified with the full type name and the assembly name: @@ -105,7 +100,8 @@ The following configuration shows how to create an ApplicationInsights sink with "Name": "ApplicationInsights", "Args": { "restrictedToMinimumLevel": "Information", - "telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights" + "telemetryConverter": + "Serilog.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights" } } ], @@ -153,7 +149,8 @@ var position = new { Latitude = 25, Longitude = 134 }; var elapsedMs = 34; var numbers = new int[] { 1, 2, 3, 4 }; -Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", position, elapsedMs, "test", numbers); +Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", + position, elapsedMs, "test", numbers); ``` will produce the following properties in *customDimensions*: @@ -185,15 +182,14 @@ private class DottedOutTraceTelemetryConverter : TraceTelemetryConverter } ``` -## Customising +## Customizing Additionally, you can also customize *whether* to send the LogEvents at all, if so *which type(s)* of Telemetry to send and also *what to send* (all or no LogEvent properties at all) by passing your own `ITelemetryConverter` instead of `TelemetryConverter.Traces` or `TelemetryConverter.Events` by either implementing your own `ITelemetryConverter` or deriving from `TraceTelemetryConverter` or `EventTelemetryConverter` and overriding specific bits. ```csharp -var log = new LoggerConfiguration() - .WriteTo - .ApplicationInsights(configuration, new CustomConverter()) +Log.Logger = new LoggerConfiguration() + .WriteTo.ApplicationInsights(configuration, new CustomConverter()) .CreateLogger(); // ... @@ -248,7 +244,8 @@ The easiest way to customise included properties is to subclass one of the `ITel ```csharp private class IncludeRenderedMessageConverter : EventTelemetryConverter { - public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, ISupportProperties telemetryProperties, IFormatProvider formatProvider) + public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, + ISupportProperties telemetryProperties, IFormatProvider formatProvider) { base.ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, includeLogLevel: false, @@ -292,7 +289,7 @@ var log = new LoggerConfiguration() ```csharp _telemetryClient.Flush(); -// The AI Documentation mentions that calling .Flush() *can* be asynchronous and non-blocking so +// The AI documentation mentions that calling `Flush()` *can* be asynchronous and non-blocking so // depending on the underlying Channel to AI you might want to wait some time // specific to your application and its connectivity constraints for the flush to finish. @@ -304,9 +301,9 @@ System.Threading.Thread.Sleep(1000); ``` -## Including Operation ID +## Including Operation Id -Application Insight's Operation ID is pushed out if you set `operationId` LogEvent property. If it's present, AI's operation ID will be overriden by the value from this property. +Application Insight's operation id is pushed out if you set `operationId` LogEvent property. If it's present, AI's operation id will be overriden by the value from this property. This can be set like so: @@ -326,7 +323,7 @@ public class OperationIdEnricher : ILogEventEnricher ## Including Version -Application Insight supports component version and is pushed out if you set `version` LogEvent property. If it's present, AI's operation version will include the value from this property. +Application Insight supports component version and is pushed out if you set `version` log event property. If it's present, AI's operation version will include the value from this property. ## Using with Azure Functions From f89099ced4ffbc3de6dd383300f2a4812947fc4e Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Tue, 3 May 2022 08:55:07 +0200 Subject: [PATCH 17/42] Add failing tests --- .../FormattingTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index 1b06ac0..ed5d6af 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -68,5 +68,23 @@ public void Version_from_logContext_is_included() Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Component.Version); } } + + [Fact] + public void Message_quotes_are_not_escaped() + { + var value = "This string is \"quoted\""; + Logger.Information("Data:\n{JsonData}", value); + + Assert.Equal($"Data: \"{value}\"", LastSubmittedTraceTelemetry.Message); + } + + [Fact] + public void Message_property_quotes_are_not_escaped() + { + var value = "This string is \"quoted\""; + Logger.Information("Data:\n{JsonData}", value); + + Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["JsonData"]); + } } } From fab0b65d29c1c4a03194774e6619469c6b6df8b8 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Tue, 3 May 2022 08:58:11 +0200 Subject: [PATCH 18/42] Add suggested solution from https://github.com/serilog-contrib/serilog-sinks-applicationinsights/issues/137#issuecomment-1115434026 --- .../TelemetryConverters/TelemetryConverterBase.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index 3784afd..6de0b9c 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; +using Serilog.Formatting.Display; using Serilog.Sinks.ApplicationInsights.Formatters; namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters @@ -13,6 +15,8 @@ namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters /// public abstract class TelemetryConverterBase : ITelemetryConverter { + private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:lj}"); + /// The is forwarded to the underlying AI Telemetry and its .Properties using this key. /// public const string TelemetryPropertiesLogLevel = "LogLevel"; @@ -108,7 +112,9 @@ public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, if (includeRenderedMessage) { - telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, logEvent.RenderMessage(formatProvider)); + var sw = new StringWriter(); + MessageTemplateTextFormatter.Format(logEvent, sw); + telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, sw.ToString()); } if (includeMessageTemplate) @@ -161,4 +167,4 @@ public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, return null; } } -} \ No newline at end of file +} From 330d5cccb5d5de4501299595a91064b04247dc4b Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Tue, 3 May 2022 08:59:56 +0200 Subject: [PATCH 19/42] Fix tests --- .../FormattingTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index ed5d6af..646cd9b 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -73,18 +73,18 @@ public void Version_from_logContext_is_included() public void Message_quotes_are_not_escaped() { var value = "This string is \"quoted\""; - Logger.Information("Data:\n{JsonData}", value); + Logger.Information("Data: {MyData}", value); - Assert.Equal($"Data: \"{value}\"", LastSubmittedTraceTelemetry.Message); + Assert.Equal($"Data: {value}", LastSubmittedTraceTelemetry.Message); } [Fact] public void Message_property_quotes_are_not_escaped() { var value = "This string is \"quoted\""; - Logger.Information("Data:\n{JsonData}", value); + Logger.Information("Data: {MyData}", value); - Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["JsonData"]); + Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["MyData"]); } } } From 51370f68ccf14c75c01ded76e65456de192a29d8 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:32:05 +0200 Subject: [PATCH 20/42] Add test to verify messages are formatted without quoted strings Code by @nblumhardt https://github.com/serilog-contrib/serilog-sinks-applicationinsights/pull/186#issuecomment-1116838041 --- .../ApplicationInsightsTest.cs | 5 +++++ .../EventTelemetryConverterTest.cs | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs index e45e631..305ab18 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs @@ -33,5 +33,10 @@ protected ApplicationInsightsTest(ITelemetryConverter converter = null) .OfType() .LastOrDefault(); + protected EventTelemetry LastSubmittedEventTelemetry => + _channel.SubmittedTelemetry + .OfType() + .LastOrDefault(); + } } diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs new file mode 100644 index 0000000..c8170bd --- /dev/null +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -0,0 +1,19 @@ +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; +using Xunit; + +namespace Serilog.Sinks.ApplicationInsights.Tests +{ + public class EventTelemetryConverterTest : ApplicationInsightsTest + { + public EventTelemetryConverterTest() : base(new EventTelemetryConverter()) + { + } + + [Fact] + public void MessagesAreFormattedWithoutQuotedStrings() + { + Logger.Information("Hello, {Name}!", "world"); + Assert.Equal("Hello, world!", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); + } + } +} From 39814a2481a11de307c53aeb7799636e68231823 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:34:34 +0200 Subject: [PATCH 21/42] Remove old test --- .../FormattingTests.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index 646cd9b..7b93343 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -69,15 +69,6 @@ public void Version_from_logContext_is_included() } } - [Fact] - public void Message_quotes_are_not_escaped() - { - var value = "This string is \"quoted\""; - Logger.Information("Data: {MyData}", value); - - Assert.Equal($"Data: {value}", LastSubmittedTraceTelemetry.Message); - } - [Fact] public void Message_property_quotes_are_not_escaped() { From f25e70bb9e06020a6eab5667dc1403279926e20d Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:35:38 +0200 Subject: [PATCH 22/42] Move tests --- .../EventTelemetryConverterTest.cs | 18 ++++++++++++++++++ .../FormattingTests.cs | 9 --------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index c8170bd..724433f 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -15,5 +15,23 @@ public void MessagesAreFormattedWithoutQuotedStrings() Logger.Information("Hello, {Name}!", "world"); Assert.Equal("Hello, world!", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } + + [Fact] + public void MessageQuotesAreNotEscaped() + { + var value = "This string is \"quoted\""; + Logger.Information("Data: {MyData}", value); + + Assert.Equal($"Data: {value}", LastSubmittedTraceTelemetry.Message); + } + + [Fact] + public void MessagePropertyQuotesAreNotEscaped() + { + var value = "This string is \"quoted\""; + Logger.Information("Data: {MyData}", value); + + Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["MyData"]); + } } } diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index 7b93343..1b06ac0 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -68,14 +68,5 @@ public void Version_from_logContext_is_included() Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Component.Version); } } - - [Fact] - public void Message_property_quotes_are_not_escaped() - { - var value = "This string is \"quoted\""; - Logger.Information("Data: {MyData}", value); - - Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["MyData"]); - } } } From 8c2dff72c6af151d47dd3e9721e8839abeb45958 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:36:28 +0200 Subject: [PATCH 23/42] Improve tests --- .../EventTelemetryConverterTest.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index 724433f..846867c 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -19,19 +19,15 @@ public void MessagesAreFormattedWithoutQuotedStrings() [Fact] public void MessageQuotesAreNotEscaped() { - var value = "This string is \"quoted\""; - Logger.Information("Data: {MyData}", value); - - Assert.Equal($"Data: {value}", LastSubmittedTraceTelemetry.Message); + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); } [Fact] public void MessagePropertyQuotesAreNotEscaped() { - var value = "This string is \"quoted\""; - Logger.Information("Data: {MyData}", value); - - Assert.Equal($"{value}", LastSubmittedTraceTelemetry.Properties["MyData"]); + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("This string is \"quoted\"", LastSubmittedTraceTelemetry.Properties["MyData"]); } } } From 93c227511665a507bafa73d18b7166cdc90ddaf6 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:40:43 +0200 Subject: [PATCH 24/42] Fix tests --- .../EventTelemetryConverterTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index 846867c..b4aba66 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -20,14 +20,14 @@ public void MessagesAreFormattedWithoutQuotedStrings() public void MessageQuotesAreNotEscaped() { Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); + Assert.Equal("Data: This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } [Fact] public void MessagePropertyQuotesAreNotEscaped() { Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("This string is \"quoted\"", LastSubmittedTraceTelemetry.Properties["MyData"]); + Assert.Equal("This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["MyData"]); } } } From 9944d4294c1bf79202012283a35a53dbe95e05c1 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:40:52 +0200 Subject: [PATCH 25/42] Add trace telemetry converter tests --- .../TraceTelemetryConverterTest.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs new file mode 100644 index 0000000..d565dc4 --- /dev/null +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -0,0 +1,33 @@ +using Serilog.Sinks.ApplicationInsights.TelemetryConverters; +using Xunit; + +namespace Serilog.Sinks.ApplicationInsights.Tests +{ + public class TraceTelemetryConverterTest : ApplicationInsightsTest + { + public TraceTelemetryConverterTest() : base(new TraceTelemetryConverter()) + { + } + + [Fact] + public void MessagesAreFormattedWithoutQuotedStrings() + { + Logger.Information("Hello, {Name}!", "world"); + Assert.Equal("Hello, world!", LastSubmittedTraceTelemetry.Message); + } + + [Fact] + public void MessageQuotesAreNotEscaped() + { + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); + } + + [Fact] + public void MessagePropertyQuotesAreNotEscaped() + { + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("This string is \"quoted\"", LastSubmittedTraceTelemetry.Properties["MyData"]); + } + } +} From 3e11d647c4562c8d66a460b37b3fd24703559b0e Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:55:18 +0200 Subject: [PATCH 26/42] Make two trace telemetry converter tests pass --- .../TelemetryConverters/TraceTelemetryConverter.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index 36ccc92..db5ee93 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -1,13 +1,17 @@ using System; using System.Collections.Generic; +using System.IO; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; +using Serilog.Formatting.Display; namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { public class TraceTelemetryConverter : TelemetryConverterBase { + private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:lj}"); + public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { if (logEvent == null) @@ -15,7 +19,9 @@ public override IEnumerable Convert(LogEvent logEvent, IFormatProvid if (logEvent.Exception == null) { - var renderedMessage = logEvent.RenderMessage(formatProvider); + var sw = new StringWriter(); + MessageTemplateTextFormatter.Format(logEvent, sw); + var renderedMessage = sw.ToString(); var telemetry = new TraceTelemetry(renderedMessage) { From 29526f8fb30f94e45f6ca58c41a845264a8ac712 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 08:56:06 +0200 Subject: [PATCH 27/42] Inline variable --- .../TelemetryConverters/TraceTelemetryConverter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index db5ee93..a81b1da 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -21,9 +21,8 @@ public override IEnumerable Convert(LogEvent logEvent, IFormatProvid { var sw = new StringWriter(); MessageTemplateTextFormatter.Format(logEvent, sw); - var renderedMessage = sw.ToString(); - var telemetry = new TraceTelemetry(renderedMessage) + var telemetry = new TraceTelemetry(sw.ToString()) { Timestamp = logEvent.Timestamp, SeverityLevel = ToSeverityLevel(logEvent.Level) From 6fc7f0ee0af13fd2327194cc4257f6092c2acf2b Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 09:30:16 +0200 Subject: [PATCH 28/42] Remove tests --- .../EventTelemetryConverterTest.cs | 7 ------- .../TraceTelemetryConverterTest.cs | 7 ------- 2 files changed, 14 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index b4aba66..7163662 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -22,12 +22,5 @@ public void MessageQuotesAreNotEscaped() Logger.Information("Data: {MyData}", "This string is \"quoted\""); Assert.Equal("Data: This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } - - [Fact] - public void MessagePropertyQuotesAreNotEscaped() - { - Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["MyData"]); - } } } diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs index d565dc4..bf76535 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -22,12 +22,5 @@ public void MessageQuotesAreNotEscaped() Logger.Information("Data: {MyData}", "This string is \"quoted\""); Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); } - - [Fact] - public void MessagePropertyQuotesAreNotEscaped() - { - Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("This string is \"quoted\"", LastSubmittedTraceTelemetry.Properties["MyData"]); - } } } From 9e40132cdf68bcd3333d837c8d4fb96f3e9ad50c Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 22:23:10 +0200 Subject: [PATCH 29/42] Add failing tests to verify message format when destructuring --- .../EventTelemetryConverterTest.cs | 7 +++++++ .../TraceTelemetryConverterTest.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index 7163662..8d40f3e 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -16,6 +16,13 @@ public void MessagesAreFormattedWithoutQuotedStrings() Assert.Equal("Hello, world!", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } + [Fact] + public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() + { + Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); + } + [Fact] public void MessageQuotesAreNotEscaped() { diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs index bf76535..ba1360e 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -16,6 +16,13 @@ public void MessagesAreFormattedWithoutQuotedStrings() Assert.Equal("Hello, world!", LastSubmittedTraceTelemetry.Message); } + [Fact] + public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() + { + Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedTraceTelemetry.Message); + } + [Fact] public void MessageQuotesAreNotEscaped() { From 93fda52de3ae9063c827eddb403d346ecebcc5ff Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 22:26:28 +0200 Subject: [PATCH 30/42] Make tests pass by removing :j flag --- .../TelemetryConverters/TelemetryConverterBase.cs | 3 ++- .../TelemetryConverters/TraceTelemetryConverter.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index 6de0b9c..7c22626 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -15,7 +15,7 @@ namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters /// public abstract class TelemetryConverterBase : ITelemetryConverter { - private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:lj}"); + private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:l}"); /// The is forwarded to the underlying AI Telemetry and its .Properties using this key. /// @@ -115,6 +115,7 @@ public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, var sw = new StringWriter(); MessageTemplateTextFormatter.Format(logEvent, sw); telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, sw.ToString()); + // telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, logEvent.RenderMessage(formatProvider)); } if (includeMessageTemplate) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index a81b1da..f1ca693 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -10,7 +10,7 @@ namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters { public class TraceTelemetryConverter : TelemetryConverterBase { - private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:lj}"); + private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:l}"); public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { @@ -23,6 +23,7 @@ public override IEnumerable Convert(LogEvent logEvent, IFormatProvid MessageTemplateTextFormatter.Format(logEvent, sw); var telemetry = new TraceTelemetry(sw.ToString()) + // var telemetry = new TraceTelemetry(logEvent.RenderMessage(formatProvider)) { Timestamp = logEvent.Timestamp, SeverityLevel = ToSeverityLevel(logEvent.Level) From e729f26eff7c676241b692680606025fec5d7054 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Wed, 4 May 2022 22:39:03 +0200 Subject: [PATCH 31/42] Remove commented-out code --- .../TelemetryConverters/TelemetryConverterBase.cs | 1 - .../TelemetryConverters/TraceTelemetryConverter.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index 7c22626..5141579 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -115,7 +115,6 @@ public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, var sw = new StringWriter(); MessageTemplateTextFormatter.Format(logEvent, sw); telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, sw.ToString()); - // telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, logEvent.RenderMessage(formatProvider)); } if (includeMessageTemplate) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index f1ca693..f621491 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -23,7 +23,6 @@ public override IEnumerable Convert(LogEvent logEvent, IFormatProvid MessageTemplateTextFormatter.Format(logEvent, sw); var telemetry = new TraceTelemetry(sw.ToString()) - // var telemetry = new TraceTelemetry(logEvent.RenderMessage(formatProvider)) { Timestamp = logEvent.Timestamp, SeverityLevel = ToSeverityLevel(logEvent.Level) From 4809aea65ba6ec335e8d21b384297a62c00a3c62 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 09:49:57 +1000 Subject: [PATCH 32/42] Apply Rider's 'Reformat Code' helper; use C# 10; treat warnings as errors --- CHANGES.md | 3 +- README.md | 80 +++-- ...figurationApplicationInsightsExtensions.cs | 181 ++++++----- .../Serilog.Sinks.ApplicationInsights.csproj | 8 +- .../ApplicationInsightsSink.cs | 300 +++++++++--------- ...ApplicationInsightsDottedValueFormatter.cs | 120 +++---- .../ApplicationInsightsJsonValueFormatter.cs | 45 +-- .../Formatters/IValueFormatter.cs | 13 +- .../EventTelemetryConverter.cs | 52 +-- .../ITelemetryConverter.cs | 13 +- .../TelemetryConverterBase.cs | 282 ++++++++-------- .../TraceTelemetryConverter.cs | 56 ++-- .../TelemetryConverter.cs | 15 +- .../ApplicationInsightsTest.cs | 58 ++-- .../CustomTelemetryConversionTest.cs | 96 +++--- .../CustomiseEventTelemetryConverterTest.cs | 27 +- .../DottedOutFormattingTest.cs | 52 ++- .../EventTelemetryConverterTest.cs | 47 ++- .../FormattingTests.cs | 96 +++--- ...log.Sinks.ApplicationInsights.Tests.csproj | 8 +- .../TelemetryConversionTest.cs | 70 ++-- .../TraceTelemetryConverterTest.cs | 47 ++- .../UnitTestTelemetryChannel.cs | 57 ++-- 23 files changed, 878 insertions(+), 848 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7647d86..e58c098 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,2 +1,3 @@ 2.0.0 - * Moved the Application Insights sink from its [original location](https://github.com/serilog/serilog) + +* Moved the Application Insights sink from its [original location](https://github.com/serilog/serilog) diff --git a/README.md b/README.md index 3e427ce..d121a38 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # Serilog.Sinks.ApplicationInsights [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.ApplicationInsights.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.ApplicationInsights/) -A sink for Serilog that writes events to Microsoft Application Insights. This sink comes with several defaults that send Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. +A sink for Serilog that writes events to Microsoft Application Insights. This sink comes with several defaults that send +Serilog `LogEvent` messages to Application Insights as either `EventTelemetry` or `TraceTelemetry`. ## Configuring -The simplest way to configure Serilog to send data to a Application Insights dashboard via instrumentation key is to use current active *telemetry configuration* which is already initialised in most application types like ASP.NET Core, Azure Functions etc.: +The simplest way to configure Serilog to send data to a Application Insights dashboard via instrumentation key is to use +current active *telemetry configuration* which is already initialised in most application types like ASP.NET Core, Azure +Functions etc.: ```csharp var log = new LoggerConfiguration() @@ -12,25 +15,34 @@ var log = new LoggerConfiguration() .CreateLogger(); ``` - .. or as `EventTelemetry`: - ```csharp var log = new LoggerConfiguration() .WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Events) .CreateLogger(); ``` -> You can also pass an *instrumentation key* and this sink will create a new `TelemetryConfiguration` based on it, however it's actively discouraged compared to using already initialised telemetry configuration, as your telemetry won't be properly correlated. +> You can also pass an *instrumentation key* and this sink will create a new `TelemetryConfiguration` based on it, +> however it's actively discouraged compared to using already initialised telemetry configuration, as your telemetry +> won't +> be properly correlated. -**Note:** Whether you choose `Events` or `Traces`, if the LogEvent contains any exceptions it will always be sent as `ExceptionTelemetry`. +**Note:** Whether you choose `Events` or `Traces`, if the LogEvent contains any exceptions it will always be sent +as `ExceptionTelemetry`. ### `TelemetryConfiguration.Active` is deprecated in the App Insights SDK for .NET Core, what do I do? -The singleton [`TelemetryConfiguration.Active` has been deprecated in the Application Insights SDK on .NET Core in favor of dependency injection pattern](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152). +The +singleton [`TelemetryConfiguration.Active` has been deprecated in the Application Insights SDK on .NET Core in favor of dependency injection pattern](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1152) +. -Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added either by `services.AddApplicationInsightsTelemetryWorkerService()` (if you're developing a [non-http applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service)) or `services.AddApplicationInsightsTelemetry()` (if you're developing an [ASP.Net Core applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core)) during Startup in `ConfigureServices`. +Therefore, now we need to pass in the `TelemetryConfiguration` instance that was added either +by `services.AddApplicationInsightsTelemetryWorkerService()` (if you're developing +a [non-http applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service)) +or `services.AddApplicationInsightsTelemetry()` (if you're developing +an [ASP.Net Core applciation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core)) during Startup +in `ConfigureServices`. ```csharp Log.Logger = new LoggerConfiguration() @@ -40,9 +52,12 @@ Log.Logger = new LoggerConfiguration() .CreateLogger(); ``` -However, you probably want to setup your Logger as close to the entry point of your application as possible, so that any startup errors can be caught and properly logged. The problem is that now we're in a chicken-and-egg situation: we want to setup the logger early, but we need the `TelemetryConfiguration` which still haven't been added to our DI container. +However, you probably want to setup your Logger as close to the entry point of your application as possible, so that any +startup errors can be caught and properly logged. The problem is that now we're in a chicken-and-egg situation: we want +to setup the logger early, but we need the `TelemetryConfiguration` which still haven't been added to our DI container. -Luckily [from version 4.0.x of the `Serilog.Extensions.Hosting` we have the possibility to configure a bootstrap logger](https://nblumhardt.com/2020/10/bootstrap-logger/) to capture early errors, and then change it using DI dependant services once they are configured. +Luckily [from version 4.0.x of the `Serilog.Extensions.Hosting` we have the possibility to configure a bootstrap logger](https://nblumhardt.com/2020/10/bootstrap-logger/) +to capture early errors, and then change it using DI dependant services once they are configured. ```csharp // dotnet add package serilog.extensions.hosting -v 4.0.0-* @@ -81,7 +96,9 @@ public static class Program ### Configuring with `ReadFrom.Configuration()` -The following configuration shows how to create an ApplicationInsights sink with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration) - the telemetry converter has to be specified with the full type name and the assembly name: +The following configuration shows how to create an ApplicationInsights sink +with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration) - the telemetry +converter has to be specified with the full type name and the assembly name: ```json { @@ -120,6 +137,7 @@ The following configuration shows how to create an ApplicationInsights sink with ## What do we submit? By default, trace telemetry submits: + - **rendered message** in trace's standard *message* property. - **severity** in trace's standard *severityLevel* property. - **timestamp** in trace's standard *timestamp* property. @@ -127,18 +145,21 @@ By default, trace telemetry submits: - **custom log properties** as *customDimensions*. Event telemetry submits: + - **message template** as *event name*. - **renderedMessage** in *customDimensions*. - **timestamp** in event's standard *timestamp* property. - **custom log properties** as *customDimensions*. Exception telemetry submits: + - **exception** as standard AI exception. - **severity** in trace's standard *severityLevel* property. - **timestamp** in trace's standard *timestamp* property. - **custom log properties** as *customDimensions*. -> Note that **log context** properties are also included in *customDimensions* when Serilog is configured with `.Enrich.FromLogContext()`. +> Note that **log context** properties are also included in *customDimensions* when Serilog is configured +> with `.Enrich.FromLogContext()`. ## How custom properties are logged? @@ -184,8 +205,10 @@ private class DottedOutTraceTelemetryConverter : TraceTelemetryConverter ## Customizing -Additionally, you can also customize *whether* to send the LogEvents at all, if so *which type(s)* of Telemetry to send and also *what to send* (all or no LogEvent properties at all) by passing your own `ITelemetryConverter` instead of `TelemetryConverter.Traces` or `TelemetryConverter.Events` by either implementing your own `ITelemetryConverter` or deriving from `TraceTelemetryConverter` or `EventTelemetryConverter` and overriding specific bits. - +Additionally, you can also customize *whether* to send the LogEvents at all, if so *which type(s)* of Telemetry to send +and also *what to send* (all or no LogEvent properties at all) by passing your own `ITelemetryConverter` instead +of `TelemetryConverter.Traces` or `TelemetryConverter.Events` by either implementing your own `ITelemetryConverter` or +deriving from `TraceTelemetryConverter` or `EventTelemetryConverter` and overriding specific bits. ```csharp Log.Logger = new LoggerConfiguration() @@ -239,7 +262,8 @@ If you want to skip sending a particular LogEvent, just return `null` from your ### Customising included properties -The easiest way to customise included properties is to subclass one of the `ITelemetryConverter` implementations. For instance, let's include `renderedMessage` in event telemetry: +The easiest way to customise included properties is to subclass one of the `ITelemetryConverter` implementations. For +instance, let's include `renderedMessage` in event telemetry: ```csharp private class IncludeRenderedMessageConverter : EventTelemetryConverter @@ -256,10 +280,14 @@ private class IncludeRenderedMessageConverter : EventTelemetryConverter ``` ## How, When and Why to Flush Messages Manually - + ### Or: Where did my Messages go? -As explained by the [Application Insights documentation](https://azure.microsoft.com/en-us/documentation/articles/app-insights-api-custom-events-metrics/#flushing-data), the default behaviour of the AI client is to buffer messages and send them to AI in batches whenever the client seems fit. However, this may lead to lost messages when your application terminates while there are still unsent messages in said buffer. +As explained by +the [Application Insights documentation](https://azure.microsoft.com/en-us/documentation/articles/app-insights-api-custom-events-metrics/#flushing-data) +, the default behaviour of the AI client is to buffer messages and send them to AI in batches whenever the client seems +fit. However, this may lead to lost messages when your application terminates while there are still unsent messages in +said buffer. You can control when AI shall flush its messages, for example when your application closes: @@ -303,7 +331,8 @@ System.Threading.Thread.Sleep(1000); ## Including Operation Id -Application Insight's operation id is pushed out if you set `operationId` LogEvent property. If it's present, AI's operation id will be overriden by the value from this property. +Application Insight's operation id is pushed out if you set `operationId` LogEvent property. If it's present, AI's +operation id will be overriden by the value from this property. This can be set like so: @@ -323,13 +352,19 @@ public class OperationIdEnricher : ILogEventEnricher ## Including Version -Application Insight supports component version and is pushed out if you set `version` log event property. If it's present, AI's operation version will include the value from this property. +Application Insight supports component version and is pushed out if you set `version` log event property. If it's +present, AI's operation version will include the value from this property. ## Using with Azure Functions -Azure functions has out of the box integration with Application Insights, which automatically logs functions execution start, end, and any exception. Please refer to the [original documenation](https://docs.microsoft.com/en-us/azure/azure-functions/functions-monitoring) on how to enable it. +Azure functions has out of the box integration with Application Insights, which automatically logs functions execution +start, end, and any exception. Please refer to +the [original documenation](https://docs.microsoft.com/en-us/azure/azure-functions/functions-monitoring) on how to +enable it. -This sink can enrich AI messages, preserving *operation_Id* and other context information which is *already provided by functions runtime*. The easiest way to configure Serilog in this case is to use **TelemetryConfiguration.Active** which is already properly configured. You can, for instance, initialise logging in the static constructor: +This sink can enrich AI messages, preserving *operation_Id* and other context information which is *already provided by +functions runtime*. The easiest way to configure Serilog in this case is to use **TelemetryConfiguration.Active** which +is already properly configured. You can, for instance, initialise logging in the static constructor: ```csharp public static class MyFunctions @@ -348,6 +383,7 @@ public static class MyFunctions } ``` -Copyright © 2021 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html). +Copyright © 2021 Serilog Contributors - Provided under +the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html). See also: [Serilog Documentation](https://github.com/serilog/serilog/wiki) diff --git a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs index e007154..c4dacb1 100644 --- a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs +++ b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs @@ -21,104 +21,109 @@ using Serilog.Sinks.ApplicationInsights; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; -namespace Serilog -{ +namespace Serilog; +/// +/// Adds the WriteTo.ApplicationInsights() extension method to . +/// +public static class LoggerConfigurationApplicationInsightsExtensions +{ /// - /// Adds the WriteTo.ApplicationInsights() extension method to . + /// Adds a Serilog sink that writes log events to Microsoft Application Insights + /// using a custom converter / constructor. /// - public static class LoggerConfigurationApplicationInsightsExtensions + /// The logger configuration. + /// Required Application Insights configuration settings. + /// Required telemetry converter. + /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink + /// + public static LoggerConfiguration ApplicationInsights( + this LoggerSinkConfiguration loggerConfiguration, + TelemetryConfiguration telemetryConfiguration, + ITelemetryConverter telemetryConverter, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) { - /// - /// Adds a Serilog sink that writes log events to Microsoft Application Insights - /// using a custom converter / constructor. - /// - /// The logger configuration. - /// Required Application Insights configuration settings. - /// Required telemetry converter. - /// The minimum log event level required in order to write an event to the sink. - /// Logging level switch for this sink - /// - public static LoggerConfiguration ApplicationInsights( - this LoggerSinkConfiguration loggerConfiguration, - TelemetryConfiguration telemetryConfiguration, - ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - { - var client = new TelemetryClient(telemetryConfiguration ?? TelemetryConfiguration.Active); +#pragma warning disable CS0618 + var client = new TelemetryClient(telemetryConfiguration ?? TelemetryConfiguration.Active); +#pragma warning restore CS0618 - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); - } - - /// - /// Adds a Serilog sink that writes log events to Microsoft Application Insights - /// using the active - /// - /// The logger configuration. - /// Required telemetry converter. - /// The minimum log event level required in order to write an event to the sink. - /// Logging level switch for this sink - /// - public static LoggerConfiguration ApplicationInsights( - this LoggerSinkConfiguration loggerConfiguration, - ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - { + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), + restrictedToMinimumLevel, levelSwitch); + } - var client = new TelemetryClient(TelemetryConfiguration.Active); + /// + /// Adds a Serilog sink that writes log events to Microsoft Application Insights + /// using the active + /// + /// The logger configuration. + /// Required telemetry converter. + /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink + /// + public static LoggerConfiguration ApplicationInsights( + this LoggerSinkConfiguration loggerConfiguration, + ITelemetryConverter telemetryConverter, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { +#pragma warning disable CS0618 + var client = new TelemetryClient(TelemetryConfiguration.Active); +#pragma warning restore CS0618 - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); - } + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), + restrictedToMinimumLevel, levelSwitch); + } - /// - /// Adds a Serilog sink that writes log events to Microsoft Application Insights - /// using a custom converter / constructor. - /// - /// The logger configuration. - /// Required Application Insights telemetry client. - /// Required telemetry converter. - /// The minimum log event level required in order to write an event to the sink. - /// Logging level switch for this sink - /// - public static LoggerConfiguration ApplicationInsights( - this LoggerSinkConfiguration loggerConfiguration, - TelemetryClient telemetryClient, - ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - { - return loggerConfiguration.Sink(new ApplicationInsightsSink(telemetryClient, telemetryConverter), restrictedToMinimumLevel, levelSwitch); - } + /// + /// Adds a Serilog sink that writes log events to Microsoft Application Insights + /// using a custom converter / constructor. + /// + /// The logger configuration. + /// Required Application Insights telemetry client. + /// Required telemetry converter. + /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink + /// + public static LoggerConfiguration ApplicationInsights( + this LoggerSinkConfiguration loggerConfiguration, + TelemetryClient telemetryClient, + ITelemetryConverter telemetryConverter, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { + return loggerConfiguration.Sink(new ApplicationInsightsSink(telemetryClient, telemetryConverter), + restrictedToMinimumLevel, levelSwitch); + } - /// - /// Adds a Serilog sink that writes log events to Microsoft Application Insights - /// using a custom converter / constructor. Only use in rare cases when your application doesn't - /// have already constructed AI telemetry configuration, which is extremely rare. - /// - /// The logger configuration. - /// Required Application Insights key. - /// Required telemetry converter. - /// The minimum log event level required in order to write an event to the sink. - /// Logging level switch for this sink - /// - public static LoggerConfiguration ApplicationInsights( - this LoggerSinkConfiguration loggerConfiguration, - string instrumentationKey, - ITelemetryConverter telemetryConverter, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - LoggingLevelSwitch levelSwitch = null) - { - var client = new TelemetryClient(); + /// + /// Adds a Serilog sink that writes log events to Microsoft Application Insights + /// using a custom converter / constructor. Only use in rare cases when your application + /// doesn't + /// have already constructed AI telemetry configuration, which is extremely rare. + /// + /// The logger configuration. + /// Required Application Insights key. + /// Required telemetry converter. + /// The minimum log event level required in order to write an event to the sink. + /// Logging level switch for this sink + /// + public static LoggerConfiguration ApplicationInsights( + this LoggerSinkConfiguration loggerConfiguration, + string instrumentationKey, + ITelemetryConverter telemetryConverter, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + LoggingLevelSwitch levelSwitch = null) + { +#pragma warning disable CS0618 + var client = new TelemetryClient(); +#pragma warning restore CS0618 - if (!string.IsNullOrWhiteSpace(instrumentationKey)) - { - client.InstrumentationKey = instrumentationKey; - } + if (!string.IsNullOrWhiteSpace(instrumentationKey)) client.InstrumentationKey = instrumentationKey; - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); - } + return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), + restrictedToMinimumLevel, levelSwitch); } -} +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj index 532cf5d..215a3b8 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj +++ b/src/Serilog.Sinks.ApplicationInsights/Serilog.Sinks.ApplicationInsights.csproj @@ -18,15 +18,17 @@ git true Serilog + latest + true - - + + - + diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs index cdb21f6..d4235b8 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/ApplicationInsightsSink.cs @@ -13,7 +13,6 @@ // limitations under the License. using System; -using System.Collections.Generic; using System.Reflection; using System.Runtime.ExceptionServices; using System.Threading; @@ -23,188 +22,175 @@ using Serilog.Events; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; -namespace Serilog.Sinks.ApplicationInsights +namespace Serilog.Sinks.ApplicationInsights; + +/// +/// Base class for Microsoft Azure Application Insights based Sinks. +/// Inspired by their NLog Appender implementation. +/// +public class ApplicationInsightsSink : ILogEventSink, IDisposable { + readonly IFormatProvider _formatProvider; + + readonly ITelemetryConverter _telemetryConverter; + long _isDisposed; + long _isDisposing; + + TelemetryClient _telemetryClient; + /// - /// Base class for Microsoft Azure Application Insights based Sinks. - /// Inspired by their NLog Appender implementation. + /// Creates a sink that saves logs to the Application Insights account for the given + /// instance. /// - public class ApplicationInsightsSink : ILogEventSink, IDisposable + /// Required Application Insights . + /// The to converter. + /// Supplies culture-specific formatting information, or null for default provider. + /// cannot be null + public ApplicationInsightsSink( + TelemetryClient telemetryClient, + ITelemetryConverter telemetryConverter, + IFormatProvider formatProvider = null) { - private long _isDisposing = 0; - private long _isDisposed = 0; - - private TelemetryClient _telemetryClient; - - /// - /// Gets or sets a value indicating whether this instance is being disposed. - /// - /// - /// true if this instance is being disposed; otherwise, false. - /// - public bool IsDisposing - { - get => Interlocked.Read(ref _isDisposing) == 1; - protected set => Interlocked.Exchange(ref _isDisposing, value ? 1 : 0); - } + _telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient)); + _telemetryConverter = telemetryConverter ?? throw new ArgumentNullException(nameof(telemetryConverter)); - /// - /// Gets a value indicating whether this instance has been disposed. - /// - /// - /// true if this instance has been disposed; otherwise, false. - /// - public bool IsDisposed - { - get => Interlocked.Read(ref _isDisposed) == 1; - protected set => Interlocked.Exchange(ref _isDisposed, value ? 1 : 0); - } + _formatProvider = formatProvider; + } - private readonly IFormatProvider _formatProvider; - - private readonly ITelemetryConverter _telemetryConverter; - - /// - /// Creates a sink that saves logs to the Application Insights account for the given instance. - /// - /// Required Application Insights . - /// The to converter. - /// Supplies culture-specific formatting information, or null for default provider. - /// cannot be null - public ApplicationInsightsSink( - TelemetryClient telemetryClient, - ITelemetryConverter telemetryConverter, - IFormatProvider formatProvider = null) - { - _telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient)); - _telemetryConverter = telemetryConverter ?? throw new ArgumentNullException(nameof(telemetryConverter)); + /// + /// Gets or sets a value indicating whether this instance is being disposed. + /// + /// + /// true if this instance is being disposed; otherwise, false. + /// + public bool IsDisposing + { + get => Interlocked.Read(ref _isDisposing) == 1; + protected set => Interlocked.Exchange(ref _isDisposing, value ? 1 : 0); + } - _formatProvider = formatProvider; - } + /// + /// Gets a value indicating whether this instance has been disposed. + /// + /// + /// true if this instance has been disposed; otherwise, false. + /// + public bool IsDisposed + { + get => Interlocked.Read(ref _isDisposed) == 1; + protected set => Interlocked.Exchange(ref _isDisposed, value ? 1 : 0); + } - #region AI specifc Helper methods + #region Implementation of ILogEventSink - /// - /// Hands over the to the AI telemetry client. - /// - /// The telemetry. - /// telemetry - protected virtual void TrackTelemetry(ITelemetry telemetry) - { - if (telemetry == null) throw new ArgumentNullException(nameof(telemetry)); + /// + /// Emit the provided log event to the sink. + /// + /// The log event to write. + /// A delegate callback throws an exception. + /// A delegate callback throws an exception. + public virtual void Emit(LogEvent logEvent) + { + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - CheckForAndThrowIfDisposed(); + CheckForAndThrowIfDisposed(); - // the .Track() method is save to use (even though documented otherwise) - // see https://github.com/Microsoft/ApplicationInsights-dotnet/issues/244 - _telemetryClient?.Track(telemetry); + try + { + var telemetries = _telemetryConverter.Convert(logEvent, _formatProvider); + + // if 'null' is returned (& we therefore there's nothing to track), the logEvent is basically skipped + if (telemetries != null) + foreach (var telemetry in telemetries) + if (telemetry != null) + TrackTelemetry(telemetry); + } + catch (TargetInvocationException targetInvocationException) + { + // rethrow original exception (inside the TargetInvocationException) if any + if (targetInvocationException.InnerException != null) + ExceptionDispatchInfo.Capture(targetInvocationException.InnerException).Throw(); + else + throw; } + } - #endregion AI specifc Helper methods + #endregion - #region Implementation of ILogEventSink + #region AI specifc Helper methods - /// - /// Emit the provided log event to the sink. - /// - /// The log event to write. - /// A delegate callback throws an exception. - /// A delegate callback throws an exception. - public virtual void Emit(LogEvent logEvent) - { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + /// + /// Hands over the to the AI telemetry client. + /// + /// The telemetry. + /// telemetry + protected virtual void TrackTelemetry(ITelemetry telemetry) + { + if (telemetry == null) throw new ArgumentNullException(nameof(telemetry)); - CheckForAndThrowIfDisposed(); + CheckForAndThrowIfDisposed(); - try - { - IEnumerable telemetries = _telemetryConverter.Convert(logEvent, _formatProvider); + // the .Track() method is save to use (even though documented otherwise) + // see https://github.com/Microsoft/ApplicationInsights-dotnet/issues/244 + _telemetryClient?.Track(telemetry); + } - // if 'null' is returned (& we therefore there's nothing to track), the logEvent is basically skipped - if (telemetries != null) - { - foreach (ITelemetry telemetry in telemetries) - { - if (telemetry != null) - { - TrackTelemetry(telemetry); - } - } - } - } - catch (TargetInvocationException targetInvocationException) - { - // rethrow original exception (inside the TargetInvocationException) if any - if (targetInvocationException.InnerException != null) - { - ExceptionDispatchInfo.Capture(targetInvocationException.InnerException).Throw(); - } - else - { - throw; - } - } - } + #endregion AI specifc Helper methods - #endregion + #region Implementation of IDisposable - #region Implementation of IDisposable + /// + /// Checks whether this instance has been disposed and if so, throws an . + /// + /// + protected void CheckForAndThrowIfDisposed() + { + if (IsDisposed) throw new ObjectDisposedException(GetType().Name); + } - /// - /// Checks whether this instance has been disposed and if so, throws an . - /// - /// - protected void CheckForAndThrowIfDisposed() - { - if (IsDisposed) - { - throw new ObjectDisposedException(this.GetType().Name); - } - } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(disposeManagedResources: true); + GC.SuppressFinalize(this); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// + /// true to release both managed and unmanaged resources; false to + /// release only unmanaged resources. + /// + protected virtual void Dispose(bool disposeManagedResources) + { + if (IsDisposing || IsDisposed) + return; - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposeManagedResources) + try { - if (IsDisposing || IsDisposed) - return; + IsDisposing = true; - try - { - IsDisposing = true; - - // we only have managed resources to dispose of - if (disposeManagedResources) + // we only have managed resources to dispose of + if (disposeManagedResources) + // attempt to free managed resources + try + { + _telemetryClient?.Flush(); + } + finally { - // attempt to free managed resources - try - { - _telemetryClient?.Flush(); - } - finally - { - _telemetryClient = null; - } + _telemetryClient = null; } - } - finally - { - IsDisposed = true; - IsDisposing = false; - } } - - #endregion Implementation of IDisposable + finally + { + IsDisposed = true; + IsDisposing = false; + } } -} + + #endregion Implementation of IDisposable +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs index ce4742b..3957ba1 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs @@ -4,75 +4,81 @@ using Serilog.Debugging; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters; + +#pragma warning disable CS1591 + +public class ApplicationInsightsDottedValueFormatter : IValueFormatter { - public class ApplicationInsightsDottedValueFormatter : IValueFormatter + static readonly IDictionary>> LiteralWriters = + new Dictionary + >> { + { typeof(SequenceValue), (k, v, p) => WriteSequenceValue(k, (SequenceValue)v, p) }, + { typeof(DictionaryValue), (k, v, p) => WriteDictionaryValue(k, (DictionaryValue)v, p) }, + { typeof(StructureValue), (k, v, p) => WriteStructureValue(k, (StructureValue)v, p) }, + { typeof(ScalarValue), (k, v, p) => WriteValue(k, ((ScalarValue)v).Value, p) }, + { typeof(DateTime), (k, v, p) => AppendProperty(p, k, ((DateTime)v).ToString("o")) }, + { typeof(DateTimeOffset), (k, v, p) => AppendProperty(p, k, ((DateTimeOffset)v).ToString("o")) }, { + typeof(float), + (k, v, p) => AppendProperty(p, k, ((float)v).ToString("R", CultureInfo.InvariantCulture)) + }, { + typeof(double), + (k, v, p) => AppendProperty(p, k, ((double)v).ToString("R", CultureInfo.InvariantCulture)) + } + }; + + public void Format(string propertyName, LogEventPropertyValue propertyValue, + IDictionary properties) { - private static readonly IDictionary>> LiteralWriters = new Dictionary - >> - { - {typeof (SequenceValue), (k, v, p) => WriteSequenceValue(k, (SequenceValue) v, p)}, - {typeof (DictionaryValue), (k, v, p) => WriteDictionaryValue(k, (DictionaryValue) v, p)}, - {typeof (StructureValue), (k, v, p) => WriteStructureValue(k, (StructureValue) v, p)}, - {typeof (ScalarValue), (k, v, p) => WriteValue(k,((ScalarValue)v).Value,p)}, - {typeof (DateTime), (k, v, p) => AppendProperty(p,k,((DateTime)v).ToString("o"))}, - {typeof (DateTimeOffset), (k, v, p) => AppendProperty(p,k,((DateTimeOffset)v).ToString("o"))}, - {typeof (float), (k, v, p) => AppendProperty(p,k,((float)v).ToString("R", CultureInfo.InvariantCulture))}, - {typeof (double), (k, v, p) => AppendProperty(p,k,((double)v).ToString("R", CultureInfo.InvariantCulture))}, - }; + WriteValue(propertyName, propertyValue, properties); + } - public void Format(string propertyName, LogEventPropertyValue propertyValue, IDictionary properties) - { - WriteValue(propertyName, propertyValue, properties); - } + static void WriteStructureValue(string key, StructureValue structureValue, + IDictionary properties) + { + foreach (var eventProperty in structureValue.Properties) + WriteValue(key + "." + eventProperty.Name, eventProperty.Value, properties); + } - private static void WriteStructureValue(string key, StructureValue structureValue, IDictionary properties) - { - foreach (var eventProperty in structureValue.Properties) - { - WriteValue(key + "." + eventProperty.Name, eventProperty.Value, properties); - } - } + static void WriteDictionaryValue(string key, DictionaryValue dictionaryValue, + IDictionary properties) + { + foreach (var eventProperty in dictionaryValue.Elements) + WriteValue(key + "." + eventProperty.Key.Value, eventProperty.Value, properties); + } - private static void WriteDictionaryValue(string key, DictionaryValue dictionaryValue, IDictionary properties) + static void WriteSequenceValue(string key, SequenceValue sequenceValue, IDictionary properties) + { + var index = 0; + foreach (var eventProperty in sequenceValue.Elements) { - foreach (var eventProperty in dictionaryValue.Elements) - { - WriteValue(key + "." + eventProperty.Key.Value, eventProperty.Value, properties); - } + WriteValue(key + "." + index, eventProperty, properties); + index++; } - private static void WriteSequenceValue(string key, SequenceValue sequenceValue, IDictionary properties) - { - int index = 0; - foreach (var eventProperty in sequenceValue.Elements) - { - WriteValue(key + "." + index, eventProperty, properties); - index++; - } - AppendProperty(properties, key + ".Count", index.ToString()); - } + AppendProperty(properties, key + ".Count", index.ToString()); + } - public static void WriteValue(string key, object value, IDictionary properties) + public static void WriteValue(string key, object value, IDictionary properties) + { + Action> writer; + if (value == null || !LiteralWriters.TryGetValue(value.GetType(), out writer)) { - Action> writer; - if (value == null || !LiteralWriters.TryGetValue(value.GetType(), out writer)) - { - AppendProperty(properties, key, value?.ToString()); - return; - } - - writer(key, value, properties); + AppendProperty(properties, key, value?.ToString()); + return; } - private static void AppendProperty(IDictionary propDictionary, string key, string value) + writer(key, value, properties); + } + + static void AppendProperty(IDictionary propDictionary, string key, string value) + { + if (propDictionary.ContainsKey(key)) { - if (propDictionary.ContainsKey(key)) - { - SelfLog.WriteLine("The key {0} is not unique after simplification. Ignoring new value {1}", key, value); - return; - } - propDictionary.Add(key, value); + SelfLog.WriteLine("The key {0} is not unique after simplification. Ignoring new value {1}", key, value); + return; } + + propDictionary.Add(key, value); } -} +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs index f308e17..20da43d 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs @@ -4,31 +4,34 @@ using Serilog.Events; using Serilog.Formatting.Json; -namespace Serilog.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters; + +#pragma warning disable CS1591 + +public class ApplicationInsightsJsonValueFormatter : IValueFormatter { - public class ApplicationInsightsJsonValueFormatter : IValueFormatter - { - private readonly JsonValueFormatter _formatter = new JsonValueFormatter(); - private static readonly char[] TrimChars = new[] { '\"' }; + static readonly char[] TrimChars = { '\"' }; + readonly JsonValueFormatter _formatter = new(); - public void Format(string propertyName, LogEventPropertyValue propertyValue, IDictionary properties) + public void Format(string propertyName, LogEventPropertyValue propertyValue, + IDictionary properties) + { + string value; + using (var sw = new StringWriter()) { - string value; - using (var sw = new StringWriter()) - { - _formatter.Format(propertyValue, sw); - value = sw.ToString(); - } - - value = value.Trim(TrimChars); + _formatter.Format(propertyValue, sw); + value = sw.ToString(); + } - if (properties.ContainsKey(propertyName)) - { - SelfLog.WriteLine("The key {0} is not unique after simplification. Ignoring new value {1}", propertyName, value); - return; - } + value = value.Trim(TrimChars); - properties.Add(propertyName, value); + if (properties.ContainsKey(propertyName)) + { + SelfLog.WriteLine("The key {0} is not unique after simplification. Ignoring new value {1}", + propertyName, value); + return; } + + properties.Add(propertyName, value); } -} +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs index c8745d6..168c96e 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.Formatters +namespace Serilog.Sinks.ApplicationInsights.Formatters; + +#pragma warning disable CS1591 + +public interface IValueFormatter { - public interface IValueFormatter - { - void Format(string propertyName, LogEventPropertyValue propertyValue, IDictionary properties); - } -} + void Format(string propertyName, LogEventPropertyValue propertyValue, IDictionary properties); +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs index e530ff9..e1cbb3e 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/EventTelemetryConverter.cs @@ -4,37 +4,39 @@ using Microsoft.ApplicationInsights.DataContracts; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters; + +#pragma warning disable CS1591 + +public class EventTelemetryConverter : TelemetryConverterBase { - public class EventTelemetryConverter : TelemetryConverterBase + public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { - public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) - { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (logEvent.Exception == null) - { - var telemetry = new EventTelemetry(logEvent.MessageTemplate.Text) { - Timestamp = logEvent.Timestamp - }; + if (logEvent.Exception == null) + { + var telemetry = new EventTelemetry(logEvent.MessageTemplate.Text) { + Timestamp = logEvent.Timestamp + }; - // write logEvent's .Properties to the AI one - ForwardPropertiesToTelemetryProperties(logEvent, telemetry, formatProvider); + // write logEvent's .Properties to the AI one + ForwardPropertiesToTelemetryProperties(logEvent, telemetry, formatProvider); - yield return telemetry; - } - else - { - yield return ToExceptionTelemetry(logEvent, formatProvider); - } + yield return telemetry; } - - public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, ISupportProperties telemetryProperties, IFormatProvider formatProvider) + else { - ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, - includeLogLevel: false, - includeRenderedMessage: true, - includeMessageTemplate: false); + yield return ToExceptionTelemetry(logEvent, formatProvider); } } -} + + public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, + ISupportProperties telemetryProperties, IFormatProvider formatProvider) + { + ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, + includeLogLevel: false, + includeRenderedMessage: true, + includeMessageTemplate: false); + } +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs index 0d4b940..f4073c5 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/ITelemetryConverter.cs @@ -3,10 +3,11 @@ using Microsoft.ApplicationInsights.Channel; using Serilog.Events; -namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters; + +#pragma warning disable CS1591 + +public interface ITelemetryConverter { - public interface ITelemetryConverter - { - IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider); - } -} + IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider); +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index 5141579..a0aaeed 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -8,163 +8,175 @@ using Serilog.Formatting.Display; using Serilog.Sinks.ApplicationInsights.Formatters; -namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters; + +/// +/// Base class for telemetry converters +/// +public abstract class TelemetryConverterBase : ITelemetryConverter { /// - /// Base class for telemetry converters + /// The + /// is forwarded to the underlying AI Telemetry and its .Properties using this key. /// - public abstract class TelemetryConverterBase : ITelemetryConverter - { - private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:l}"); + public const string TelemetryPropertiesLogLevel = "LogLevel"; - /// The is forwarded to the underlying AI Telemetry and its .Properties using this key. - /// - public const string TelemetryPropertiesLogLevel = "LogLevel"; + /// + /// The is forwarded to the underlying AI Telemetry and its .Properties using + /// this key. + /// + public const string TelemetryPropertiesMessageTemplate = "MessageTemplate"; - /// - /// The is forwarded to the underlying AI Telemetry and its .Properties using this key. - /// - public const string TelemetryPropertiesMessageTemplate = "MessageTemplate"; + /// + /// The result of is forwarded to the underlying AI + /// Telemetry and its .Properties using this key. + /// + public const string TelemetryPropertiesRenderedMessage = "RenderedMessage"; - /// - /// The result of is forwarded to the underlying AI Telemetry and its .Properties using this key. - /// - public const string TelemetryPropertiesRenderedMessage = "RenderedMessage"; + /// + /// Property that is included when in log context, will be pushed out as AI operation ID. + /// + public const string OperationIdProperty = "operationId"; - /// - /// Property that is included when in log context, will be pushed out as AI operation ID. - /// - public const string OperationIdProperty = "operationId"; + /// + /// Property that is included when in log context, will be pushed out as AI component version. + /// + public const string VersionProperty = "version"; - /// - /// Property that is included when in log context, will be pushed out as AI component version. - /// - public const string VersionProperty = "version"; + static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:l}"); - public virtual IValueFormatter ValueFormatter { get; } + /// + /// Creates an instance of using default value formatter ( + /// ). + /// + public TelemetryConverterBase() + { + ValueFormatter = new ApplicationInsightsJsonValueFormatter(); + } - public abstract IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider); +#pragma warning disable CS1591 + public virtual IValueFormatter ValueFormatter { get; } +#pragma warning restore CS1591 - /// - /// Creates an instance of using default value formatter (). - /// - public TelemetryConverterBase() - { - ValueFormatter = new ApplicationInsightsJsonValueFormatter(); - } +#pragma warning disable CS1591 + public abstract IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider); +#pragma warning restore CS1591 - public virtual ExceptionTelemetry ToExceptionTelemetry( - LogEvent logEvent, - IFormatProvider formatProvider) - { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (logEvent.Exception == null) throw new ArgumentException("Must have an Exception", nameof(logEvent)); +#pragma warning disable CS1591 + public virtual ExceptionTelemetry ToExceptionTelemetry( +#pragma warning restore CS1591 + LogEvent logEvent, + IFormatProvider formatProvider) + { + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + if (logEvent.Exception == null) throw new ArgumentException("Must have an Exception", nameof(logEvent)); - var exceptionTelemetry = new ExceptionTelemetry(logEvent.Exception) { - SeverityLevel = ToSeverityLevel(logEvent.Level), - Timestamp = logEvent.Timestamp - }; + var exceptionTelemetry = new ExceptionTelemetry(logEvent.Exception) { + SeverityLevel = ToSeverityLevel(logEvent.Level), + Timestamp = logEvent.Timestamp + }; - // write logEvent's .Properties to the AI one - ForwardPropertiesToTelemetryProperties(logEvent, exceptionTelemetry, formatProvider); + // write logEvent's .Properties to the AI one + ForwardPropertiesToTelemetryProperties(logEvent, exceptionTelemetry, formatProvider); - return exceptionTelemetry; - } + return exceptionTelemetry; + } + +#pragma warning disable CS1591 + public virtual void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, +#pragma warning restore CS1591 + ISupportProperties telemetryProperties, + IFormatProvider formatProvider) + { + ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, + includeLogLevel: false, + includeRenderedMessage: false, + includeMessageTemplate: true); + } + + /// + /// Forwards all data to the including the log level, + /// rendered message, message template and all properties to the telemetry. + /// + /// The log event. + /// The telemetry properties. + /// The format provider. + /// + /// if set to true the is added to the + /// using the key. + /// + /// + /// if set to true the output is added to the + /// using the key. + /// + /// + /// if set to true the is added to the + /// using the key. + /// + /// + /// Thrown if or + /// is null. + /// + public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, + ISupportProperties telemetryProperties, + IFormatProvider formatProvider, + bool includeLogLevel, + bool includeRenderedMessage, + bool includeMessageTemplate) + { + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + if (telemetryProperties == null) throw new ArgumentNullException(nameof(telemetryProperties)); - public virtual void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, - ISupportProperties telemetryProperties, - IFormatProvider formatProvider) + if (includeLogLevel) + telemetryProperties.Properties.Add(TelemetryPropertiesLogLevel, logEvent.Level.ToString()); + + if (includeRenderedMessage) { - ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, - includeLogLevel: false, - includeRenderedMessage: false, - includeMessageTemplate: true); + var sw = new StringWriter(); + MessageTemplateTextFormatter.Format(logEvent, sw); + telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, sw.ToString()); } - /// - /// Forwards all data to the including the log level, - /// rendered message, message template and all properties to the telemetry. - /// - /// The log event. - /// The telemetry properties. - /// The format provider. - /// if set to true the is added to the - /// using the key. - /// if set to true the output is added to the - /// using the key. - /// if set to true the is added to the - /// using the key. - /// Thrown if or is null. - public void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, - ISupportProperties telemetryProperties, - IFormatProvider formatProvider, - bool includeLogLevel, - bool includeRenderedMessage, - bool includeMessageTemplate) + if (includeMessageTemplate) + telemetryProperties.Properties.Add(TelemetryPropertiesMessageTemplate, logEvent.MessageTemplate.Text); + + if (telemetryProperties is ITelemetry telemetry) { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (telemetryProperties == null) throw new ArgumentNullException(nameof(telemetryProperties)); - - if (includeLogLevel) - { - telemetryProperties.Properties.Add(TelemetryPropertiesLogLevel, logEvent.Level.ToString()); - } - - if (includeRenderedMessage) - { - var sw = new StringWriter(); - MessageTemplateTextFormatter.Format(logEvent, sw); - telemetryProperties.Properties.Add(TelemetryPropertiesRenderedMessage, sw.ToString()); - } - - if (includeMessageTemplate) - { - telemetryProperties.Properties.Add(TelemetryPropertiesMessageTemplate, logEvent.MessageTemplate.Text); - } - - if (telemetryProperties is ITelemetry telemetry) - { - if (logEvent.Properties.TryGetValue(OperationIdProperty, out LogEventPropertyValue operationId)) - { - telemetry.Context.Operation.Id = operationId.ToString().Trim('\"'); - } - - if (logEvent.Properties.TryGetValue(VersionProperty, out LogEventPropertyValue version) - && telemetry.Context?.Component != null) - { - telemetry.Context.Component.Version = version.ToString().Trim('\"'); - } - } - - foreach (KeyValuePair property in logEvent.Properties.Where(property => property.Value != null && !telemetryProperties.Properties.ContainsKey(property.Key))) - { - ValueFormatter.Format(property.Key, property.Value, telemetryProperties.Properties); - } + if (logEvent.Properties.TryGetValue(OperationIdProperty, out var operationId)) + telemetry.Context.Operation.Id = operationId.ToString().Trim('\"'); + + if (logEvent.Properties.TryGetValue(VersionProperty, out var version) + && telemetry.Context?.Component != null) + telemetry.Context.Component.Version = version.ToString().Trim('\"'); } - /// - /// To the severity level. - /// - /// The log event level. - /// - public SeverityLevel? ToSeverityLevel(LogEventLevel logEventLevel) + foreach (var property in logEvent.Properties.Where(property => + property.Value != null && !telemetryProperties.Properties.ContainsKey(property.Key))) + ValueFormatter.Format(property.Key, property.Value, telemetryProperties.Properties); + } + + /// + /// To the severity level. + /// + /// The log event level. + /// + public SeverityLevel? ToSeverityLevel(LogEventLevel logEventLevel) + { + switch (logEventLevel) { - switch (logEventLevel) - { - case LogEventLevel.Verbose: - case LogEventLevel.Debug: - return SeverityLevel.Verbose; - case LogEventLevel.Information: - return SeverityLevel.Information; - case LogEventLevel.Warning: - return SeverityLevel.Warning; - case LogEventLevel.Error: - return SeverityLevel.Error; - case LogEventLevel.Fatal: - return SeverityLevel.Critical; - } - - return null; + case LogEventLevel.Verbose: + case LogEventLevel.Debug: + return SeverityLevel.Verbose; + case LogEventLevel.Information: + return SeverityLevel.Information; + case LogEventLevel.Warning: + return SeverityLevel.Warning; + case LogEventLevel.Error: + return SeverityLevel.Error; + case LogEventLevel.Fatal: + return SeverityLevel.Critical; } + + return null; } -} +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index f621491..1c82650 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -6,37 +6,37 @@ using Serilog.Events; using Serilog.Formatting.Display; -namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters +namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters; + +#pragma warning disable CS1591 + +public class TraceTelemetryConverter : TelemetryConverterBase { - public class TraceTelemetryConverter : TelemetryConverterBase + static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:l}"); + + public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { - private static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new MessageTemplateTextFormatter("{Message:l}"); + if (logEvent == null) + throw new ArgumentNullException(nameof(logEvent)); + + if (logEvent.Exception == null) + { + var sw = new StringWriter(); + MessageTemplateTextFormatter.Format(logEvent, sw); + + var telemetry = new TraceTelemetry(sw.ToString()) { + Timestamp = logEvent.Timestamp, + SeverityLevel = ToSeverityLevel(logEvent.Level) + }; - public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) + // write logEvent's .Properties to the AI one + ForwardPropertiesToTelemetryProperties(logEvent, telemetry, formatProvider); + + yield return telemetry; + } + else { - if (logEvent == null) - throw new ArgumentNullException(nameof(logEvent)); - - if (logEvent.Exception == null) - { - var sw = new StringWriter(); - MessageTemplateTextFormatter.Format(logEvent, sw); - - var telemetry = new TraceTelemetry(sw.ToString()) - { - Timestamp = logEvent.Timestamp, - SeverityLevel = ToSeverityLevel(logEvent.Level) - }; - - // write logEvent's .Properties to the AI one - ForwardPropertiesToTelemetryProperties(logEvent, telemetry, formatProvider); - - yield return telemetry; - } - else - { - yield return ToExceptionTelemetry(logEvent, formatProvider); - } + yield return ToExceptionTelemetry(logEvent, formatProvider); } } -} +} \ No newline at end of file diff --git a/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs index 88c0eec..d8496e0 100644 --- a/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/TelemetryConverter.cs @@ -14,12 +14,13 @@ using Serilog.Sinks.ApplicationInsights.TelemetryConverters; -namespace Serilog +namespace Serilog; + +#pragma warning disable CS1591 + +public static class TelemetryConverter { - public static class TelemetryConverter - { - public static ITelemetryConverter Traces => new TraceTelemetryConverter(); + public static ITelemetryConverter Traces => new TraceTelemetryConverter(); - public static ITelemetryConverter Events => new EventTelemetryConverter(); - } -} + public static ITelemetryConverter Events => new EventTelemetryConverter(); +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs index 305ab18..2ba6acc 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/ApplicationInsightsTest.cs @@ -1,42 +1,40 @@ -using Microsoft.ApplicationInsights.Channel; +using System.Collections.Generic; +using System.Linq; +using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; -using System.Collections.Generic; -using System.Linq; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; -namespace Serilog.Sinks.ApplicationInsights.Tests -{ - public abstract class ApplicationInsightsTest - { - private readonly UnitTestTelemetryChannel _channel; +namespace Serilog.Sinks.ApplicationInsights.Tests; - protected ApplicationInsightsTest(ITelemetryConverter converter = null) - { - var tc = new TelemetryConfiguration("", _channel = new UnitTestTelemetryChannel()); +public abstract class ApplicationInsightsTest +{ + readonly UnitTestTelemetryChannel _channel; - Logger = new LoggerConfiguration() - .WriteTo.ApplicationInsights(tc, converter ?? TelemetryConverter.Traces) - .MinimumLevel.Debug() - .Enrich.FromLogContext() - .CreateLogger(); - } + protected ApplicationInsightsTest(ITelemetryConverter converter = null) + { + var tc = new TelemetryConfiguration("", _channel = new UnitTestTelemetryChannel()); - protected ILogger Logger { get; private set; } + Logger = new LoggerConfiguration() + .WriteTo.ApplicationInsights(tc, converter ?? TelemetryConverter.Traces) + .MinimumLevel.Debug() + .Enrich.FromLogContext() + .CreateLogger(); + } - protected List SubmittedTelemetry => _channel.SubmittedTelemetry; + protected ILogger Logger { get; } - protected ITelemetry LastSubmittedTelemetry => _channel.SubmittedTelemetry.LastOrDefault(); + protected List SubmittedTelemetry => _channel.SubmittedTelemetry; - protected TraceTelemetry LastSubmittedTraceTelemetry => - _channel.SubmittedTelemetry - .OfType() - .LastOrDefault(); + protected ITelemetry LastSubmittedTelemetry => _channel.SubmittedTelemetry.LastOrDefault(); - protected EventTelemetry LastSubmittedEventTelemetry => - _channel.SubmittedTelemetry - .OfType() - .LastOrDefault(); + protected TraceTelemetry LastSubmittedTraceTelemetry => + _channel.SubmittedTelemetry + .OfType() + .LastOrDefault(); - } -} + protected EventTelemetry LastSubmittedEventTelemetry => + _channel.SubmittedTelemetry + .OfType() + .LastOrDefault(); +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs index 2c2c06b..18cbfb5 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomTelemetryConversionTest.cs @@ -1,68 +1,58 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.DataContracts; -using Serilog.Events; -using System; +using System; using System.Collections.Generic; using System.Linq; +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.DataContracts; +using Serilog.Events; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class CustomTelemetryConversionTest : ApplicationInsightsTest { - public class CustomTelemetryConversionTest : ApplicationInsightsTest + public CustomTelemetryConversionTest() : base(new CustomConverter()) { - public CustomTelemetryConversionTest() : base(new CustomConverter()) - { - - } + } - [Fact] - public void LogCustom() - { - Logger.Information("test"); + [Fact] + public void LogCustom() + { + Logger.Information("test"); - Assert.Equal("test", LastSubmittedTraceTelemetry.Message); - } + Assert.Equal("test", LastSubmittedTraceTelemetry.Message); + } - private class CustomConverter : TraceTelemetryConverter + class CustomConverter : TraceTelemetryConverter + { + public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { - public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) + // first create a default TraceTelemetry using the sink's default logic + // .. but without the log level, and (rendered) message (template) included in the Properties + foreach (var telemetry in base.Convert(logEvent, formatProvider)) { - // first create a default TraceTelemetry using the sink's default logic - // .. but without the log level, and (rendered) message (template) included in the Properties - foreach (ITelemetry telemetry in base.Convert(logEvent, formatProvider)) - { - // then go ahead and post-process the telemetry's context to contain the user id as desired - if (logEvent.Properties.ContainsKey("UserId")) - { - telemetry.Context.User.Id = logEvent.Properties["UserId"].ToString(); - } - // post-process the telemetry's context to contain the operation id - if (logEvent.Properties.ContainsKey("operation_Id")) - { - telemetry.Context.Operation.Id = logEvent.Properties["operation_Id"].ToString(); - } - // post-process the telemetry's context to contain the operation parent id - if (logEvent.Properties.ContainsKey("operation_parentId")) - { - telemetry.Context.Operation.ParentId = logEvent.Properties["operation_parentId"].ToString(); - } - // typecast to ISupportProperties so you can manipulate the properties as desired - ISupportProperties propTelematry = (ISupportProperties)telemetry; - - // find redundent properties - var removeProps = new[] { "UserId", "operation_parentId", "operation_Id" }; - removeProps = removeProps.Where(prop => propTelematry.Properties.ContainsKey(prop)).ToArray(); - - foreach (var prop in removeProps) - { - // remove redundent properties - propTelematry.Properties.Remove(prop); - } - - yield return telemetry; - } + // then go ahead and post-process the telemetry's context to contain the user id as desired + if (logEvent.Properties.ContainsKey("UserId")) + telemetry.Context.User.Id = logEvent.Properties["UserId"].ToString(); + // post-process the telemetry's context to contain the operation id + if (logEvent.Properties.ContainsKey("operation_Id")) + telemetry.Context.Operation.Id = logEvent.Properties["operation_Id"].ToString(); + // post-process the telemetry's context to contain the operation parent id + if (logEvent.Properties.ContainsKey("operation_parentId")) + telemetry.Context.Operation.ParentId = logEvent.Properties["operation_parentId"].ToString(); + // typecast to ISupportProperties so you can manipulate the properties as desired + var propTelematry = (ISupportProperties)telemetry; + + // find redundent properties + var removeProps = new[] { "UserId", "operation_parentId", "operation_Id" }; + removeProps = removeProps.Where(prop => propTelematry.Properties.ContainsKey(prop)).ToArray(); + + foreach (var prop in removeProps) + // remove redundent properties + propTelematry.Properties.Remove(prop); + + yield return telemetry; } } } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs index a1625f3..b01cea4 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/CustomiseEventTelemetryConverterTest.cs @@ -3,24 +3,19 @@ using Serilog.Events; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class CustomiseEventTelemetryConverterTest : ApplicationInsightsTest { - public class CustomiseEventTelemetryConverterTest : ApplicationInsightsTest + class IncludeRenderedMessageConverter : EventTelemetryConverter { - public CustomiseEventTelemetryConverterTest() : base() - { - - } - - private class IncludeRenderedMessageConverter : EventTelemetryConverter + public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, + ISupportProperties telemetryProperties, IFormatProvider formatProvider) { - public override void ForwardPropertiesToTelemetryProperties(LogEvent logEvent, ISupportProperties telemetryProperties, IFormatProvider formatProvider) - { - base.ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, - includeLogLevel: false, - includeRenderedMessage: true, - includeMessageTemplate: false); - } + base.ForwardPropertiesToTelemetryProperties(logEvent, telemetryProperties, formatProvider, + includeLogLevel: false, + includeRenderedMessage: true, + includeMessageTemplate: false); } } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs index 2e7596a..a9494ab 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/DottedOutFormattingTest.cs @@ -2,37 +2,35 @@ using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class DottedOutFormattingTest : ApplicationInsightsTest { - public class DottedOutFormattingTest : ApplicationInsightsTest + public DottedOutFormattingTest() : base(new DottedOutTrace()) { - public DottedOutFormattingTest() : base(new DottedOutTrace()) - { - - } - - [Fact] - public void Json_parameter_is_dotted_out() - { - var position = new { Latitude = 25, Longitude = 134 }; - var elapsedMs = 34; - var numbers = new int[] { 1, 2, 3, 4 }; + } - Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", position, elapsedMs, "test", numbers); + [Fact] + public void Json_parameter_is_dotted_out() + { + var position = new { Latitude = 25, Longitude = 134 }; + var elapsedMs = 34; + var numbers = new[] { 1, 2, 3, 4 }; - Assert.Equal("34", LastSubmittedTraceTelemetry.Properties["Elapsed"]); - Assert.Equal("25", LastSubmittedTraceTelemetry.Properties["Position.Latitude"]); - Assert.Equal("134", LastSubmittedTraceTelemetry.Properties["Position.Longitude"]); - Assert.Equal("1", LastSubmittedTraceTelemetry.Properties["numbers.0"]); - Assert.Equal("2", LastSubmittedTraceTelemetry.Properties["numbers.1"]); - Assert.Equal("3", LastSubmittedTraceTelemetry.Properties["numbers.2"]); - Assert.Equal("4", LastSubmittedTraceTelemetry.Properties["numbers.3"]); - } + Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", position, + elapsedMs, "test", numbers); - private class DottedOutTrace : TraceTelemetryConverter - { - public override IValueFormatter ValueFormatter => new ApplicationInsightsDottedValueFormatter(); - } + Assert.Equal("34", LastSubmittedTraceTelemetry.Properties["Elapsed"]); + Assert.Equal("25", LastSubmittedTraceTelemetry.Properties["Position.Latitude"]); + Assert.Equal("134", LastSubmittedTraceTelemetry.Properties["Position.Longitude"]); + Assert.Equal("1", LastSubmittedTraceTelemetry.Properties["numbers.0"]); + Assert.Equal("2", LastSubmittedTraceTelemetry.Properties["numbers.1"]); + Assert.Equal("3", LastSubmittedTraceTelemetry.Properties["numbers.2"]); + Assert.Equal("4", LastSubmittedTraceTelemetry.Properties["numbers.3"]); + } + class DottedOutTrace : TraceTelemetryConverter + { + public override IValueFormatter ValueFormatter => new ApplicationInsightsDottedValueFormatter(); } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index 8d40f3e..b436acf 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -1,33 +1,32 @@ using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class EventTelemetryConverterTest : ApplicationInsightsTest { - public class EventTelemetryConverterTest : ApplicationInsightsTest + public EventTelemetryConverterTest() : base(new EventTelemetryConverter()) { - public EventTelemetryConverterTest() : base(new EventTelemetryConverter()) - { - } + } - [Fact] - public void MessagesAreFormattedWithoutQuotedStrings() - { - Logger.Information("Hello, {Name}!", "world"); - Assert.Equal("Hello, world!", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); - } + [Fact] + public void MessagesAreFormattedWithoutQuotedStrings() + { + Logger.Information("Hello, {Name}!", "world"); + Assert.Equal("Hello, world!", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); + } - [Fact] - public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() - { - Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); - Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); - } + [Fact] + public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() + { + Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); + } - [Fact] - public void MessageQuotesAreNotEscaped() - { - Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("Data: This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); - } + [Fact] + public void MessageQuotesAreNotEscaped() + { + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("Data: This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index 1b06ac0..142daa6 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -1,72 +1,72 @@ -using Microsoft.ApplicationInsights.Channel; using Serilog.Context; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class FormattingTests : ApplicationInsightsTest { - public class FormattingTests : ApplicationInsightsTest + [Fact] + public void Log_level_is_not_in_trace_custom_property() { - [Fact] - public void Log_level_is_not_in_trace_custom_property() - { - Logger.Information("test"); + Logger.Information("test"); - Assert.False(LastSubmittedTraceTelemetry.Properties.ContainsKey("LogLevel")); - } + Assert.False(LastSubmittedTraceTelemetry.Properties.ContainsKey("LogLevel")); + } - [Fact] - public void Message_template_is_in_trace_custom_property() - { - Logger.Information("test"); + [Fact] + public void Message_template_is_in_trace_custom_property() + { + Logger.Information("test"); - Assert.True(LastSubmittedTraceTelemetry.Properties.ContainsKey("MessageTemplate")); - } + Assert.True(LastSubmittedTraceTelemetry.Properties.ContainsKey("MessageTemplate")); + } - [Fact] - public void Message_properies_include_log_context() + [Fact] + public void Message_properties_include_log_context() + { + using (LogContext.PushProperty("custom1", "value1")) { - using (LogContext.PushProperty("custom1", "value1")) - { - Logger.Information("test context"); + Logger.Information("test context"); - Assert.True(LastSubmittedTraceTelemetry.Properties.TryGetValue("custom1", out string value1) && value1 == "value1"); - } + Assert.True(LastSubmittedTraceTelemetry.Properties.TryGetValue("custom1", out var value1) && + value1 == "value1"); } + } - [Fact] - public void Json_parameter_is_compact() - { - var position = new { Latitude = 25, Longitude = 134 }; - var elapsedMs = 34; - var numbers = new int[] { 1, 2, 3, 4 }; + [Fact] + public void Json_parameter_is_compact() + { + var position = new { Latitude = 25, Longitude = 134 }; + var elapsedMs = 34; + var numbers = new[] { 1, 2, 3, 4 }; - Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", position, elapsedMs, "test", numbers); + Logger.Information("Processed {@Position} in {Elapsed:000} ms., str {str}, numbers: {numbers}", position, + elapsedMs, "test", numbers); - Assert.Equal("34", LastSubmittedTraceTelemetry.Properties["Elapsed"]); - Assert.Equal("{\"Latitude\":25,\"Longitude\":134}", LastSubmittedTraceTelemetry.Properties["Position"]); - Assert.Equal("[1,2,3,4]", LastSubmittedTraceTelemetry.Properties["numbers"]); - } + Assert.Equal("34", LastSubmittedTraceTelemetry.Properties["Elapsed"]); + Assert.Equal("{\"Latitude\":25,\"Longitude\":134}", LastSubmittedTraceTelemetry.Properties["Position"]); + Assert.Equal("[1,2,3,4]", LastSubmittedTraceTelemetry.Properties["numbers"]); + } - [Fact] - public void OperationId_from_logContext_is_included() + [Fact] + public void OperationId_from_logContext_is_included() + { + using (LogContext.PushProperty("operationId", "myId1")) { - using (LogContext.PushProperty("operationId", "myId1")) - { - Logger.Information("capture id?"); + Logger.Information("capture id?"); - Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Operation.Id); - } + Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Operation.Id); } + } - [Fact] - public void Version_from_logContext_is_included() + [Fact] + public void Version_from_logContext_is_included() + { + using (LogContext.PushProperty("version", "myId1")) { - using (LogContext.PushProperty("version", "myId1")) - { - Logger.Information("capture id?"); + Logger.Information("capture id?"); - Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Component.Version); - } + Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Component.Version); } } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj index e03b2f8..6028933 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/Serilog.Sinks.ApplicationInsights.Tests.csproj @@ -3,11 +3,13 @@ net6.0;net4.8 false + latest + true - - + + all runtime; build; native; contentfiles; analyzers @@ -15,7 +17,7 @@ - + diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs index 9f035ee..0445881 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TelemetryConversionTest.cs @@ -1,53 +1,51 @@ -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.DataContracts; -using Serilog.Events; -using System; +using System; using System.Collections.Generic; using System.Linq; +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.DataContracts; +using Serilog.Events; using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class TelemetryConversionTest : ApplicationInsightsTest { - public class TelemetryConversionTest : ApplicationInsightsTest + public TelemetryConversionTest() : base(new CustomConverter()) { - public TelemetryConversionTest() : base(new CustomConverter()) - { - - } + } - [Fact] - public void Converter_triggers() - { - Logger.Information("test"); + [Fact] + public void Converter_triggers() + { + Logger.Information("test"); - Assert.Single(SubmittedTelemetry); - } + Assert.Single(SubmittedTelemetry); + } - [Fact] - public void Convert_to_two_traces() - { - Logger.Information("two"); + [Fact] + public void Convert_to_two_traces() + { + Logger.Information("two"); - Assert.Equal(2, SubmittedTelemetry.Count(t => t is TraceTelemetry tt && tt.Message == "two")); - } + Assert.Equal(expected: 2, SubmittedTelemetry.Count(t => t is TraceTelemetry tt && tt.Message == "two")); + } - private class CustomConverter : TraceTelemetryConverter + class CustomConverter : TraceTelemetryConverter + { + public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { - public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) + var tt = base.Convert(logEvent, formatProvider); + + if (logEvent.MessageTemplate.Text == "two") + { + yield return tt.First(); + yield return tt.First(); + } + else { - IEnumerable tt = base.Convert(logEvent, formatProvider); - - if (logEvent.MessageTemplate.Text == "two") - { - yield return tt.First(); - yield return tt.First(); - } - else - { - yield return tt.First(); - } + yield return tt.First(); } } } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs index ba1360e..83ce720 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -1,33 +1,32 @@ using Serilog.Sinks.ApplicationInsights.TelemetryConverters; using Xunit; -namespace Serilog.Sinks.ApplicationInsights.Tests +namespace Serilog.Sinks.ApplicationInsights.Tests; + +public class TraceTelemetryConverterTest : ApplicationInsightsTest { - public class TraceTelemetryConverterTest : ApplicationInsightsTest + public TraceTelemetryConverterTest() : base(new TraceTelemetryConverter()) { - public TraceTelemetryConverterTest() : base(new TraceTelemetryConverter()) - { - } + } - [Fact] - public void MessagesAreFormattedWithoutQuotedStrings() - { - Logger.Information("Hello, {Name}!", "world"); - Assert.Equal("Hello, world!", LastSubmittedTraceTelemetry.Message); - } + [Fact] + public void MessagesAreFormattedWithoutQuotedStrings() + { + Logger.Information("Hello, {Name}!", "world"); + Assert.Equal("Hello, world!", LastSubmittedTraceTelemetry.Message); + } - [Fact] - public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() - { - Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); - Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedTraceTelemetry.Message); - } + [Fact] + public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() + { + Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedTraceTelemetry.Message); + } - [Fact] - public void MessageQuotesAreNotEscaped() - { - Logger.Information("Data: {MyData}", "This string is \"quoted\""); - Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); - } + [Fact] + public void MessageQuotesAreNotEscaped() + { + Logger.Information("Data: {MyData}", "This string is \"quoted\""); + Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); } -} +} \ No newline at end of file diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs index 4fc2d51..c2c3bc7 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/UnitTestTelemetryChannel.cs @@ -1,39 +1,34 @@ -using Microsoft.ApplicationInsights.Channel; -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; +using Microsoft.ApplicationInsights.Channel; -namespace Serilog.Sinks.ApplicationInsights.Tests -{ - class UnitTestTelemetryChannel : ITelemetryChannel - { - public List SubmittedTelemetry { get; } = new List(); +namespace Serilog.Sinks.ApplicationInsights.Tests; - public bool? DeveloperMode - { - get => false; - set { } - } - - public string EndpointAddress - { - get => null; - set { } - } +class UnitTestTelemetryChannel : ITelemetryChannel +{ + public List SubmittedTelemetry { get; } = new(); - public void Dispose() - { + public bool? DeveloperMode + { + get => false; + set { } + } - } + public string EndpointAddress + { + get => null; + set { } + } - public void Flush() - { + public void Dispose() + { + } - } + public void Flush() + { + } - public void Send(ITelemetry item) - { - SubmittedTelemetry.Add(item); - } + public void Send(ITelemetry item) + { + SubmittedTelemetry.Add(item); } -} +} \ No newline at end of file From b9e2e431070f181c396ce213f9ef93788e5d25e6 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 09:50:38 +1000 Subject: [PATCH 33/42] Remove unmaintained CHANGES.md --- serilog-sinks-applicationinsights.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/serilog-sinks-applicationinsights.sln b/serilog-sinks-applicationinsights.sln index 527e542..a752a23 100644 --- a/serilog-sinks-applicationinsights.sln +++ b/serilog-sinks-applicationinsights.sln @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{E9D1B5E1 README.md = README.md .gitattributes = .gitattributes .gitignore = .gitignore - CHANGES.md = CHANGES.md LICENSE = LICENSE EndProjectSection EndProject From 380120b4c29a7a11f940a4dde2ea68b358100eb7 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 09:53:59 +1000 Subject: [PATCH 34/42] Actually remove CHANGES.md --- CHANGES.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md deleted file mode 100644 index e58c098..0000000 --- a/CHANGES.md +++ /dev/null @@ -1,3 +0,0 @@ -2.0.0 - -* Moved the Application Insights sink from its [original location](https://github.com/serilog/serilog) From 78f79cb616e58e16d4ec05cc2caa185f2743ac6e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 10:03:51 +1000 Subject: [PATCH 35/42] Use JSON formatting for embedded structured data --- .../TelemetryConverters/TelemetryConverterBase.cs | 2 +- .../TelemetryConverters/TraceTelemetryConverter.cs | 2 +- .../EventTelemetryConverterTest.cs | 2 +- .../TraceTelemetryConverterTest.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs index a0aaeed..2fc0c0c 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TelemetryConverterBase.cs @@ -43,7 +43,7 @@ public abstract class TelemetryConverterBase : ITelemetryConverter /// public const string VersionProperty = "version"; - static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:l}"); + static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:lj}"); /// /// Creates an instance of using default value formatter ( diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs index 1c82650..61277fd 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/TelemetryConverters/TraceTelemetryConverter.cs @@ -12,7 +12,7 @@ namespace Serilog.Sinks.ApplicationInsights.TelemetryConverters; public class TraceTelemetryConverter : TelemetryConverterBase { - static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:l}"); + static readonly MessageTemplateTextFormatter MessageTemplateTextFormatter = new("{Message:lj}"); public override IEnumerable Convert(LogEvent logEvent, IFormatProvider formatProvider) { diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index b436acf..7ea9a8c 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -20,7 +20,7 @@ public void MessagesAreFormattedWithoutQuotedStrings() public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() { Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); - Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); + Assert.Equal("Hello, {\"Foo\":\"foo\",\"Bar\":123}", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } [Fact] diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs index 83ce720..37df0ac 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -20,7 +20,7 @@ public void MessagesAreFormattedWithoutQuotedStrings() public void MessagesAreFormattedWithoutQuotedStringsWhenDestructuring() { Logger.Information("Hello, {@Name}", new { Foo = "foo", Bar = 123 }); - Assert.Equal("Hello, { Foo: \"foo\", Bar: 123 }", LastSubmittedTraceTelemetry.Message); + Assert.Equal("Hello, {\"Foo\":\"foo\",\"Bar\":123}", LastSubmittedTraceTelemetry.Message); } [Fact] From 6e51af6b529a6f9ae5a34f1e522e827c1a01ffe0 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 10:28:34 +1000 Subject: [PATCH 36/42] Consistently log strings as literals, and structured values as JSON --- ...ApplicationInsightsDottedValueFormatter.cs | 5 ++- .../ApplicationInsightsJsonValueFormatter.cs | 33 +++++++++++-------- .../Formatters/IValueFormatter.cs | 14 ++++++-- .../FormattingTests.cs | 8 +++++ 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs index 3957ba1..f5c4b93 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsDottedValueFormatter.cs @@ -59,10 +59,9 @@ static void WriteSequenceValue(string key, SequenceValue sequenceValue, IDiction AppendProperty(properties, key + ".Count", index.ToString()); } - public static void WriteValue(string key, object value, IDictionary properties) + static void WriteValue(string key, object value, IDictionary properties) { - Action> writer; - if (value == null || !LiteralWriters.TryGetValue(value.GetType(), out writer)) + if (value == null || !LiteralWriters.TryGetValue(value.GetType(), out var writer)) { AppendProperty(properties, key, value?.ToString()); return; diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs index 20da43d..f1835e9 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs @@ -6,32 +6,39 @@ namespace Serilog.Sinks.ApplicationInsights.Formatters; -#pragma warning disable CS1591 - +/// +/// Formats properties containing structured data as JSON. +/// public class ApplicationInsightsJsonValueFormatter : IValueFormatter { - static readonly char[] TrimChars = { '\"' }; - readonly JsonValueFormatter _formatter = new(); + readonly JsonValueFormatter _formatter = new("$type"); - public void Format(string propertyName, LogEventPropertyValue propertyValue, + /// + public void Format( + string propertyName, + LogEventPropertyValue propertyValue, IDictionary properties) { - string value; - using (var sw = new StringWriter()) + string formattedValue; + + if (propertyValue is ScalarValue { Value: string literal }) { + formattedValue = literal; + } + else + { + using var sw = new StringWriter(); _formatter.Format(propertyValue, sw); - value = sw.ToString(); + formattedValue = sw.ToString(); } - value = value.Trim(TrimChars); - if (properties.ContainsKey(propertyName)) { SelfLog.WriteLine("The key {0} is not unique after simplification. Ignoring new value {1}", - propertyName, value); + propertyName, formattedValue); return; } - properties.Add(propertyName, value); + properties.Add(propertyName, formattedValue); } -} \ No newline at end of file +} diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs index 168c96e..3be8de3 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/IValueFormatter.cs @@ -3,9 +3,17 @@ namespace Serilog.Sinks.ApplicationInsights.Formatters; -#pragma warning disable CS1591 - +/// +/// Convert Serilog log event properties into flat (string, string) pairs to send to Application Insights. +/// public interface IValueFormatter { + /// + /// Convert the log event property with value + /// into one or more key-value properties to send to Application Insights, adding these to . + /// + /// The Serilog log event's name for the property. + /// The log event's property value. + /// The collection of string properties being built to send to Application Insights. void Format(string propertyName, LogEventPropertyValue propertyValue, IDictionary properties); -} \ No newline at end of file +} diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index 142daa6..d8f84d7 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -69,4 +69,12 @@ public void Version_from_logContext_is_included() Assert.Equal("myId1", LastSubmittedTraceTelemetry.Context.Component.Version); } } + + [Fact] + public void Literal_string_properties_are_not_encoded() + { + const string literal = "\"some \\ \"literal string \""; + Logger.Information("Literal is {Literal}", literal); + Assert.Equal(literal, LastSubmittedTraceTelemetry.Properties["Literal"]); + } } \ No newline at end of file From 1f31979b2edd306e22b9894116e3247c009e743d Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Thu, 5 May 2022 10:49:37 +1000 Subject: [PATCH 37/42] Improve handling of more scalar types --- .../ApplicationInsightsJsonValueFormatter.cs | 34 +++++++++++++++---- .../FormattingTests.cs | 20 +++++++---- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs index f1835e9..24308a1 100644 --- a/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs +++ b/src/Serilog.Sinks.ApplicationInsights/Sinks/ApplicationInsights/Formatters/ApplicationInsightsJsonValueFormatter.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; using Serilog.Debugging; using Serilog.Events; @@ -6,6 +8,8 @@ namespace Serilog.Sinks.ApplicationInsights.Formatters; +#nullable enable + /// /// Formats properties containing structured data as JSON. /// @@ -20,16 +24,25 @@ public void Format( IDictionary properties) { string formattedValue; - - if (propertyValue is ScalarValue { Value: string literal }) + if (propertyValue is ScalarValue sv) { - formattedValue = literal; + formattedValue = sv.Value switch + { + // In logs, being able to distinguish null from an empty string is often important. + null => "null", + // ISO-8601 is most accurate to parse. + DateTime or DateTimeOffset => ((IFormattable)sv.Value).ToString("o", CultureInfo.InvariantCulture), + char c => c.ToString(), + // Serilog's JSON representation of these values is unquoted and generally more suitable for + // parsing/processing than their default culture-dependent `ToString()` representations. + int or uint or long or ulong or decimal or byte or sbyte or short or ushort or double or + float or bool => FormatAsJson(sv), + _ => sv.Value.ToString() ?? "(null)" + }; } else { - using var sw = new StringWriter(); - _formatter.Format(propertyValue, sw); - formattedValue = sw.ToString(); + formattedValue = FormatAsJson(propertyValue); } if (properties.ContainsKey(propertyName)) @@ -41,4 +54,11 @@ public void Format( properties.Add(propertyName, formattedValue); } + + string FormatAsJson(LogEventPropertyValue propertyValue) + { + using var sw = new StringWriter(); + _formatter.Format(propertyValue, sw); + return sw.ToString(); + } } diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs index d8f84d7..0eb84f7 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/FormattingTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Runtime.InteropServices; using Serilog.Context; using Xunit; @@ -70,11 +72,17 @@ public void Version_from_logContext_is_included() } } - [Fact] - public void Literal_string_properties_are_not_encoded() + [Theory] + [InlineData("\"some \\ \"literal string \"", "\"some \\ \"literal string \"")] + [InlineData(true, "true")] + [InlineData(false, "false")] + [InlineData(null, "null")] + [InlineData(123.45, "123.45")] + [InlineData(6789, "6789")] + [InlineData('a', "a")] + public void Scalar_values_are_encoded_as_expected(object scalar, string expected) { - const string literal = "\"some \\ \"literal string \""; - Logger.Information("Literal is {Literal}", literal); - Assert.Equal(literal, LastSubmittedTraceTelemetry.Properties["Literal"]); + Logger.Information("Value is {Scalar}", scalar); + Assert.Equal(expected, LastSubmittedTraceTelemetry.Properties["Scalar"]); } -} \ No newline at end of file +} From 6211f0a5733cdd7ef0e5a18ecf31d3ac63001a28 Mon Sep 17 00:00:00 2001 From: Christer van der Meeren Date: Thu, 5 May 2022 09:40:27 +0200 Subject: [PATCH 38/42] Add tests verifying destructured property format --- .../EventTelemetryConverterTest.cs | 9 ++++++++- .../TraceTelemetryConverterTest.cs | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs index 7ea9a8c..d8cdbe5 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/EventTelemetryConverterTest.cs @@ -29,4 +29,11 @@ public void MessageQuotesAreNotEscaped() Logger.Information("Data: {MyData}", "This string is \"quoted\""); Assert.Equal("Data: This string is \"quoted\"", LastSubmittedEventTelemetry.Properties["RenderedMessage"]); } -} \ No newline at end of file + + [Fact] + public void DestructuredPropertyIsFormattedCorrectly() + { + Logger.Information("Hello, {@MyData}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("{\"Foo\":\"foo\",\"Bar\":123}", LastSubmittedEventTelemetry.Properties["MyData"]); + } +} diff --git a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs index 37df0ac..b0d67e9 100644 --- a/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs +++ b/test/Serilog.Sinks.ApplicationInsights.Tests/TraceTelemetryConverterTest.cs @@ -29,4 +29,11 @@ public void MessageQuotesAreNotEscaped() Logger.Information("Data: {MyData}", "This string is \"quoted\""); Assert.Equal("Data: This string is \"quoted\"", LastSubmittedTraceTelemetry.Message); } -} \ No newline at end of file + + [Fact] + public void DestructuredPropertyIsFormattedCorrectly() + { + Logger.Information("Hello, {@MyData}", new { Foo = "foo", Bar = 123 }); + Assert.Equal("{\"Foo\":\"foo\",\"Bar\":123}", LastSubmittedTraceTelemetry.Properties["MyData"]); + } +} From b3ed775929cbfade7b8d791f31ca292869205d50 Mon Sep 17 00:00:00 2001 From: Mike Mao Date: Thu, 12 May 2022 11:58:25 +1200 Subject: [PATCH 39/42] updated readme.md to clarify Azure Functions usage --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d121a38..a5504bc 100644 --- a/README.md +++ b/README.md @@ -363,23 +363,28 @@ the [original documenation](https://docs.microsoft.com/en-us/azure/azure-functio enable it. This sink can enrich AI messages, preserving *operation_Id* and other context information which is *already provided by -functions runtime*. The easiest way to configure Serilog in this case is to use **TelemetryConfiguration.Active** which -is already properly configured. You can, for instance, initialise logging in the static constructor: +functions runtime*. The easiest way to configure Serilog in this case is to use the injected **TelemetryClient** which +should be automatically configured by the environment through the **APPLICATIONINSIGHTS_CONNECTION_STRING** appsetting. +You can, for instance, initialise logging in the static constructor: ```csharp -public static class MyFunctions +[assembly: FunctionsStartup(typeof(MyFunctions.Startup))] +namespace MyFunctions { - static MyFunctions() + public class Startup : FunctionsStartup + { + public override void Configure(IFunctionsHostBuilder builder) { - var config = TelemetryConfiguration.Active; - if (config != null) + builder.Services.AddSingleton((sp) => { Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() - .WriteTo.ApplicationInsights(config, TelemetryConverter.Traces) + .WriteTo.ApplicationInsights(sp.GetRequiredService(), TelemetryConverter.Traces) .CreateLogger(); - } + return new SerilogLoggerProvider(Log.Logger, true); + }); } + } } ``` From 2c88e585daac218dec211fa5e0d22d64b0cece4f Mon Sep 17 00:00:00 2001 From: Mike Mao Date: Thu, 12 May 2022 11:59:59 +1200 Subject: [PATCH 40/42] updated copyright disclaimer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5504bc..73ab900 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,7 @@ namespace MyFunctions } ``` -Copyright © 2021 Serilog Contributors - Provided under +Copyright © 2022 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html). See also: [Serilog Documentation](https://github.com/serilog/serilog/wiki) From 575b344a610610079276ab10974b3f1e0c31154e Mon Sep 17 00:00:00 2001 From: Mike Mao Date: Thu, 12 May 2022 12:04:03 +1200 Subject: [PATCH 41/42] switch to using connection string --- ...LoggerConfigurationApplicationInsightsExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs index c4dacb1..0cdf1c7 100644 --- a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs +++ b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs @@ -105,24 +105,24 @@ public static LoggerConfiguration ApplicationInsights( /// have already constructed AI telemetry configuration, which is extremely rare. /// /// The logger configuration. - /// Required Application Insights key. + /// Required Application Insights connection string. /// Required telemetry converter. /// The minimum log event level required in order to write an event to the sink. /// Logging level switch for this sink /// public static LoggerConfiguration ApplicationInsights( this LoggerSinkConfiguration loggerConfiguration, - string instrumentationKey, + string connectionString, ITelemetryConverter telemetryConverter, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, LoggingLevelSwitch levelSwitch = null) { + var config = TelemetryConfiguration.CreateDefault(); + if (!string.IsNullOrWhiteSpace(connectionString)) config.ConnectionString = connectionString; #pragma warning disable CS0618 - var client = new TelemetryClient(); + var client = new TelemetryClient(config); #pragma warning restore CS0618 - if (!string.IsNullOrWhiteSpace(instrumentationKey)) client.InstrumentationKey = instrumentationKey; - return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch); } From 1432667a4bf468e91cf43c615effa0d0b2c139ad Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Sun, 19 Jun 2022 17:06:58 +1000 Subject: [PATCH 42/42] README updates --- README.md | 18 ++++++++++-------- ...nfigurationApplicationInsightsExtensions.cs | 12 +++++++----- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 73ab900..85f08ff 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,10 @@ public static class Program ### Configuring with `ReadFrom.Configuration()` +Configuring in code, as shown above, is recommended because the existing `TelemetryClient` can be injected. + The following configuration shows how to create an ApplicationInsights sink -with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration) - the telemetry -converter has to be specified with the full type name and the assembly name: +with [ReadFrom.Configuration(configuration)](https://github.com/serilog/serilog-settings-configuration). ```json { @@ -107,16 +108,17 @@ converter has to be specified with the full type name and the assembly name: "Serilog.Sinks.ApplicationInsights" ], "MinimumLevel": { - "Default": "Debug", + "Default": "Information", "Override": { - "Microsoft": "Information" + "Microsoft": "Warning", + "System": "Warning" } }, "WriteTo": [ { "Name": "ApplicationInsights", "Args": { - "restrictedToMinimumLevel": "Information", + "connectionString": "[your connection string here]", "telemetryConverter": "Serilog.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights" } @@ -130,11 +132,11 @@ converter has to be specified with the full type name and the assembly name: } ``` -> As mentioned above you can also pass an *instrumentation key* but it's actively discouraged +The `telemetryConverter` has to be specified with the full type name and the assembly name. -**Note**: restrictedToMinimumLevel can be omitted since it is defaulted to LevelAlias.Minimum. +A `connectionString` can be omitted if it's [supplied in the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable](https://docs.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings). -## What do we submit? +## What does the sink submit? By default, trace telemetry submits: diff --git a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs index 0cdf1c7..0c6745c 100644 --- a/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs +++ b/src/Serilog.Sinks.ApplicationInsights/LoggerConfigurationApplicationInsightsExtensions.cs @@ -101,8 +101,7 @@ public static LoggerConfiguration ApplicationInsights( /// /// Adds a Serilog sink that writes log events to Microsoft Application Insights /// using a custom converter / constructor. Only use in rare cases when your application - /// doesn't - /// have already constructed AI telemetry configuration, which is extremely rare. + /// doesn't have an already-constructed AI telemetry configuration, which is extremely rare. /// /// The logger configuration. /// Required Application Insights connection string. @@ -118,10 +117,13 @@ public static LoggerConfiguration ApplicationInsights( LoggingLevelSwitch levelSwitch = null) { var config = TelemetryConfiguration.CreateDefault(); - if (!string.IsNullOrWhiteSpace(connectionString)) config.ConnectionString = connectionString; -#pragma warning disable CS0618 + + if (!string.IsNullOrWhiteSpace(connectionString)) + { + config.ConnectionString = connectionString; + } + var client = new TelemetryClient(config); -#pragma warning restore CS0618 return loggerConfiguration.Sink(new ApplicationInsightsSink(client, telemetryConverter), restrictedToMinimumLevel, levelSwitch);