diff --git a/Samples.sln b/Samples.sln index 8e7c6297..1130b64c 100644 --- a/Samples.sln +++ b/Samples.sln @@ -55,6 +55,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compliance", "Compliance", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuditReports", "src\Compliance\AuditReports\AuditReports.csproj", "{9B5FB8C7-27BA-4397-B4B8-DD0B0D525CB2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LogSampling", "src\Telemetry\Logging\LogSampling\LogSampling.csproj", "{1A8652E7-EA1D-49AF-98B3-56D655F759B6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -93,6 +95,10 @@ Global {9B5FB8C7-27BA-4397-B4B8-DD0B0D525CB2}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B5FB8C7-27BA-4397-B4B8-DD0B0D525CB2}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B5FB8C7-27BA-4397-B4B8-DD0B0D525CB2}.Release|Any CPU.Build.0 = Release|Any CPU + {1A8652E7-EA1D-49AF-98B3-56D655F759B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A8652E7-EA1D-49AF-98B3-56D655F759B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A8652E7-EA1D-49AF-98B3-56D655F759B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A8652E7-EA1D-49AF-98B3-56D655F759B6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -116,6 +122,7 @@ Global {931A6585-1085-4185-AE12-78BBA87F2A73} = {175F98E5-AFE8-4978-A512-EB84AD660208} {C42F13CB-E8D9-4A37-BFCC-A50458509D69} = {301296EC-54FF-4ADC-B2D1-281E0C7F2867} {9B5FB8C7-27BA-4397-B4B8-DD0B0D525CB2} = {C42F13CB-E8D9-4A37-BFCC-A50458509D69} + {1A8652E7-EA1D-49AF-98B3-56D655F759B6} = {248CB37C-2412-4231-96C0-092413C10D4B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6083D400-BF26-4ACE-86A8-778D26A8FA6A} diff --git a/src/Telemetry/Logging/LogSampling/Log.cs b/src/Telemetry/Logging/LogSampling/Log.cs new file mode 100644 index 00000000..90c37a3b --- /dev/null +++ b/src/Telemetry/Logging/LogSampling/Log.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Logging; + +namespace LogSampling; + +internal static partial class Log +{ + [LoggerMessage(Level = LogLevel.Error, Message = "ERROR log message in my application.")] + public static partial void ErrorMessage(ILogger logger); + + [LoggerMessage(Level = LogLevel.Information, Message = "INFORMATION log message in my application.")] + public static partial void InformationMessage(ILogger logger); + +} diff --git a/src/Telemetry/Logging/LogSampling/LogSampling.csproj b/src/Telemetry/Logging/LogSampling/LogSampling.csproj new file mode 100644 index 00000000..143395db --- /dev/null +++ b/src/Telemetry/Logging/LogSampling/LogSampling.csproj @@ -0,0 +1,21 @@ + + + + Demonstrates how to use log sampling feature. + Exe + $(NoWarn);EXTEXP0003 + + + + + + + + + + + Always + + + + diff --git a/src/Telemetry/Logging/LogSampling/Program.cs b/src/Telemetry/Logging/LogSampling/Program.cs new file mode 100644 index 00000000..310b9f48 --- /dev/null +++ b/src/Telemetry/Logging/LogSampling/Program.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace LogSampling; + +internal static class Program +{ + public static void Main() + { + var hostBuilder = Host.CreateApplicationBuilder(); + hostBuilder.Logging.AddSimpleConsole(options => + { + options.SingleLine = true; + options.TimestampFormat = "hh:mm:ss"; + }); + + // Add the Random probabilistic sampler to the logging pipeline. + hostBuilder.Logging.AddRandomProbabilisticSampler(hostBuilder.Configuration); + + var app = hostBuilder.Build(); + var loggerFactory = app.Services.GetRequiredService(); + var logger = loggerFactory.CreateLogger("SamplingDemo"); + + // Simulate a prod application with many log messages generated: + while (true) + { + Log.ErrorMessage(logger); + + for (int i = 0; i < 10; i++) + { + Log.InformationMessage(logger); + Thread.Sleep(300); + } + } + } +} diff --git a/src/Telemetry/Logging/LogSampling/README.md b/src/Telemetry/Logging/LogSampling/README.md new file mode 100644 index 00000000..d6f82dde --- /dev/null +++ b/src/Telemetry/Logging/LogSampling/README.md @@ -0,0 +1,20 @@ +# Log Sampling + +This sample shows how to use +[log sampling](https://github.com/dotnet/extensions/blob/main/src/Libraries/Microsoft.Extensions.Telemetry/README.md). +Log Sampling allows logs to be sampled, e.g. only some share of all log messages will be emitted. + +The sample uses a typical `HostApplicationBuilder` pattern to configure a small console application with +logging. Log sampling is enabled by calling `.AddRandomProbabilisticSampler()` on the logging builder +and +providing a configuration via `appsettings.json`. + +The configuration in `appsettings.json` is flexible - you can configure specific sampling rates per +any combination of +- log level +- category name +- event id + +And, importantly, the configuration supports dynamic runtime updates via the `IOptionsMonitor` pattern. +So you can change the `appsettings.json` (in the `/artifacts/bin/LogSampling/Debug` folder) at runtime +and the changes will be picked up by the Log sampling. diff --git a/src/Telemetry/Logging/LogSampling/appsettings.json b/src/Telemetry/Logging/LogSampling/appsettings.json new file mode 100644 index 00000000..3467ad32 --- /dev/null +++ b/src/Telemetry/Logging/LogSampling/appsettings.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information" + } + }, + + "RandomProbabilisticSampler": { + "Rules": [ + { + "LogLevel": "Information", + "Probability": 1 + }, + { + "LogLevel": "Error", + "Probability": 1 + } + ] + } +}