FeatureFlags.AdaptiveLogging
1.0.8
See the version list below for details.
dotnet add package FeatureFlags.AdaptiveLogging --version 1.0.8
NuGet\Install-Package FeatureFlags.AdaptiveLogging -Version 1.0.8
<PackageReference Include="FeatureFlags.AdaptiveLogging" Version="1.0.8" />
<PackageVersion Include="FeatureFlags.AdaptiveLogging" Version="1.0.8" />
<PackageReference Include="FeatureFlags.AdaptiveLogging" />
paket add FeatureFlags.AdaptiveLogging --version 1.0.8
#r "nuget: FeatureFlags.AdaptiveLogging, 1.0.8"
#:package FeatureFlags.AdaptiveLogging@1.0.8
#addin nuget:?package=FeatureFlags.AdaptiveLogging&version=1.0.8
#tool nuget:?package=FeatureFlags.AdaptiveLogging&version=1.0.8
FeatureFlags.AdaptiveLogging
A lightweight helper for feature‑flag–driven, adaptive logging powered by Azure App Configuration. It:
- Wires Feature Flags from configuration via
AddFeatureFlagsFromConfiguration(). - Registers services via
AddFeatureFlagDrivenLogging(). - Uses a single refresh knob
AppConfig_RefreshInSecondsfor both the Feature Flags SDK refresh window (UseFeatureFlags().SetRefreshInterval(...)) and a local non‑blockingTryRefreshAsyncthrottle—keeping evaluation cadence consistent. - When the flag is ON, ensures
Debug/Tracediagnostics are visible. If the provider would drop them, the helper * elevates* them toInformationand prefixes"[FORCED-<LEVEL>]"so you can spot them in production. - Provides automatic correlation: if you don’t pass a
requestId, a GUID (formatN) is generated and all messages from thatFeatureLoggerinstance are prefixed with the same id.
1) Required settings
{
"AppConfig": {
"ConnectionString": "Endpoint=...;Id=...;Secret=...",
"Label": "dev|ProjectX",
"RefreshInSeconds": 5
},
"Logging": { "LogLevel": { "Default": "Information" } }
}
2) Create flags and labels
- Create your flag (e.g.,
LogEndpointA) in App Configuration Feature Manager. - Use Label for environment and/or project scoping (e.g.,
dev|WebApi,stg|WorkerA).
3) Cost & performance
- Lower AI costs via
host.jsonlevels and sampling;Debugappears only when the flag is ON. - Unified refresh avoids drift and excessive calls.
4) Install & wire
dotnet add package FeatureFlags.AdaptiveLogging
builder.Configuration.AddFeatureFlagsFromConfiguration();
builder.Services.AddFeatureFlagDrivenLogging();
5) From zero in App Configuration
- Create the App Configuration resource.
- Add
LogEndpointA(and label variants if needed). - Set
AppConfig_*in your app settings.
6) Quick recipes (pseudo‑code only)
6.1 .NET 8 Console
var cfg = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false, true)
.AddEnvironmentVariables()
.Build();
cfg.AddFeatureFlagsFromConfiguration();
var services = new ServiceCollection();
services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Information));
services.AddFeatureFlagDrivenLogging();
services.AddSingleton<IConfiguration>(cfg);
var sp = services.BuildServiceProvider();
var logger = sp.GetRequiredService<ILoggerFactory>().CreateLogger("Console");
var flogger = await logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("Console says hi");
flogger.LogDebug("More details (visible when flag is ON)");
6.2 .NET 8 Minimal API
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddFeatureFlagsFromConfiguration();
builder.Services.AddFeatureFlagDrivenLogging();
builder.Services.AddControllers();
var app = builder.Build();
public class WeatherController : ControllerBase
{
private readonly ILogger<WeatherController> _logger;
public WeatherController(ILogger<WeatherController> logger) => _logger = logger;
[HttpGet("/api/weather")]
public async Task<IActionResult> Get()
{
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("Weather endpoint hit");
flogger.LogDebug("Debug when flag is ON");
return Ok(new { ok = true });
}
}
6.3 .NET 10 Minimal API
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddFeatureFlagsFromConfiguration();
builder.Services.AddFeatureFlagDrivenLogging();
builder.Services.AddControllers();
var app = builder.Build();
app.MapGet("/api/info", async (ILogger<Program> logger) =>
{
var flogger = await logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation(".NET 10 endpoint");
flogger.LogDebug("Debug when flag is ON");
return Results.Ok(new { ok = true });
});
6.4 Azure Functions v4 – in‑process (.NET 8)
public override void ConfigureAppConfiguration(b)
=> b.ConfigurationBuilder.AddFeatureFlagsFromConfiguration();
public override void Configure(h)
=> h.Services.AddFeatureFlagDrivenLogging();
public class Function1
{
private readonly ILogger<Function1> _logger;
public Function1(ILogger<Function1> logger) => _logger = logger;
[FunctionName("Function1")]
public async Task Run([QueueTrigger("demo-queue")] string msg)
{
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("Processing {Msg}", msg);
flogger.LogDebug("Debug when flag is ON");
}
}
6.5 Azure Functions v4 – isolated (.NET 8)
var host = new HostBuilder()
.ConfigureAppConfiguration((ctx, cfg) => cfg.AddFeatureFlagsFromConfiguration())
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(s => s.AddFeatureFlagDrivenLogging())
.Build();
public class QueueProcessor
{
private readonly ILogger<QueueProcessor> _logger;
public QueueProcessor(ILogger<QueueProcessor> logger) => _logger = logger;
[Function("QueueProcessor")]
public async Task Run([QueueTrigger("demo-queue")] string msg)
{
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("[Isolated] Processing {Msg}", msg);
flogger.LogDebug("[Isolated] Debug when flag is ON");
}
}
6.6 .NET Framework 4.7.2 (OWIN)
var cfg = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", false, true)
.AddEnvironmentVariables()
.Build();
cfg.AddFeatureFlagsFromConfiguration();
var services = new ServiceCollection();
services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Information));
services.AddFeatureFlagDrivenLogging();
services.AddSingleton<IConfiguration>(cfg);
var sp = services.BuildServiceProvider();
public class SampleController : ApiController
{
private readonly ILogger _logger = Program.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger("Sample");
// Sync version – safe on .NET Framework (no SynchronizationContext deadlock)
[HttpGet, Route("api/sample")]
public IHttpActionResult Get()
{
var flogger = _logger.EnableDebug("LogEndpointA");
flogger.LogInformation("Framework endpoint");
flogger.LogDebug("Debug when flag is ON");
return Ok(new { ok = true });
}
}
6.7 Azure Functions – Queue trigger (Service Bus)
// In-process startup wiring (once at startup)
public override void ConfigureAppConfiguration(builder)
=> builder.ConfigurationBuilder.AddFeatureFlagsFromConfiguration();
public override void Configure(services)
=> services.Services.AddFeatureFlagDrivenLogging();
// Function class (Service Bus trigger)
public class QueueProcessor
{
private readonly ILogger<QueueProcessor> _logger;
public QueueProcessor(ILogger<QueueProcessor> logger) => _logger = logger;
[FunctionName("QueueProcessor")]
public async Task Run(
[ServiceBusTrigger("your-queue-name", Connection = "ServiceBus:Connection")] string message,
int deliveryCount)
{
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("SB message received. DeliveryCount={DeliveryCount}", deliveryCount);
flogger.LogDebug("Raw payload: {Message}", message);
// ... business logic ...
flogger.LogInformation("SB message processed at {UtcNow}", DateTime.UtcNow);
}
}
// appsettings.json (pseudo-config)
{
"ServiceBus": {
"Connection": "Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=...;SharedAccessKey=..."
},
"AppConfig": {
"ConnectionString": "Endpoint=...;Id=...;Secret=...",
"Label": "dev|ProjectX",
"RefreshInSeconds": 5
},
"Logging": { "LogLevel": { "Default": "Information" } }
}
6.8 Checking if a feature is enabled
Sometimes you only want to know if a flag is active without creating a new logger instance to log right away:
// Async – preferred on modern .NET:
bool isEnabled = await _logger.IsFeatureEnabledAsync("LogEndpointA");
if (isEnabled) { /* ... */ }
// Sync – safe on .NET Framework:
bool isEnabled = _logger.IsFeatureEnabledSync("LogEndpointA");
if (isEnabled) { /* ... */ }
// Check if a previously created FeatureLogger had its flag enabled:
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
if (flogger.IsFeatureEnabled()) { /* ... */ }
6.9 Sync vs Async – choosing the right API
| Environment | Recommended API | Why |
|---|---|---|
| .NET 6 / 8 / 10 (async context) | EnableDebugAsync, IsFeatureEnabledAsync |
Native async – best throughput, no thread-pool overhead |
| .NET Framework 4.x (ASP.NET, WCF, OWIN) | EnableDebug, IsFeatureEnabledSync |
Avoids SynchronizationContext deadlocks |
| Console apps (any runtime) | Either | No SynchronizationContext – both are safe |
Why does .NET Framework need the sync API?
ASP.NET on .NET Framework installs a
SynchronizationContextthat requires async continuations to run on the original request thread. Calling.GetAwaiter().GetResult()directly on an async method would deadlock because the continuation can never marshal back. The sync methods useTask.Run(...)internally, which offloads the work to a thread-pool thread that has noSynchronizationContext, making the blocking wait safe.
// .NET Framework – use the sync API:
var flogger = _logger.EnableDebug("LogEndpointA");
flogger.LogInformation("Safe on .NET Framework");
flogger.LogDebug("Debug when flag is ON");
// .NET 6+ – prefer async:
var flogger = await _logger.EnableDebugAsync("LogEndpointA");
flogger.LogInformation("Modern .NET");
flogger.LogDebug("Debug when flag is ON");
7) Application Insights Kusto (KQL) cheat‑sheet
- Latest logs:
traces | where timestamp > ago(30m) | order by timestamp desc
- Elevated debug lines:
traces | where timestamp > ago(2h) | where message has "[FORCED-DEBUG]" or message has "[FORCED-Trace]"
- Filter by your request/correlation id:
let rid = "a3f2e6d9f81e4e9e9f6b2d3f4c5a6b7c";
traces | where timestamp > ago(1d) | where message startswith strcat("[", rid, "] ")
- Volume/severity distribution:
traces | where timestamp > ago(24h) | summarize count() by severityLevel | order by severityLevel asc
8) Changelog (high‑level)
[1.0.7]
- Added synchronous API:
EnableDebug,IsFeatureEnabledSync, and a privateRefreshConfigurationhelper. - The sync methods use
Task.Runinternally to avoidSynchronizationContextdeadlocks on .NET Framework 4.x (ASP.NET, WCF, OWIN). - All existing async methods remain unchanged and are still the recommended choice on modern .NET.
[1.0.6]
- Added
SetRefreshIntervalfor Feature Flags SDK refresh window fromAppConfig_RefreshInSeconds.
[1.0.5]
- Fixed an initialization order bug where
InvalidOperationExceptionwas thrown prematurely if the Azure App Config builder was called beforeappsettings.jsonwas fully loaded into the host configuration. - Added explicit null handling in
EnableDebugAsync.
[< 1.0.4]
- Automatic correlation id when not provided.
- Unified refresh interval for SDK and local throttle.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.AzureAppConfiguration (>= 8.5.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.FeatureManagement (>= 4.4.0)
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.AzureAppConfiguration (>= 8.5.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.FeatureManagement (>= 4.4.0)
-
net6.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.AzureAppConfiguration (>= 8.5.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.FeatureManagement (>= 4.4.0)
-
net8.0
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.AzureAppConfiguration (>= 8.5.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.FeatureManagement (>= 4.4.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.