Skip to content

Instantly share code, notes, and snippets.

@amoerie
Created March 24, 2021 07:39
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amoerie/22a1fbfd0196b85b2cc15af0349af0f4 to your computer and use it in GitHub Desktop.
Save amoerie/22a1fbfd0196b85b2cc15af0349af0f4 to your computer and use it in GitHub Desktop.
LogConfigOnStartup.cs
public class LogConfigOnStartup : IHostedService
{
private readonly ILogger<LogConfigOnStartup> _logger;
private readonly IConfigurationRoot _configuration;
public LogConfigOnStartup(ILogger<LogConfigOnStartup> logger, IConfiguration configuration)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_configuration = configuration as IConfigurationRoot ?? throw new ArgumentNullException(nameof(configuration));
}
[SuppressMessage("System", "CA1031", Justification = "Logging the config should not halt the startup")]
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
var debugView = GetDebugView(_configuration);
_logger.LogInformation("Configuration:\n{ConfigurationDebugView:l}", debugView);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to build a human readable summary of the configuration");
}
return Task.CompletedTask;
}
private static string GetDebugView(IConfigurationRoot configurationRoot)
{
var builder = new StringBuilder();
var providers = GetProviders(configurationRoot).ToList();
builder.AppendLine("Configuration providers in order of preference: (values from providers that come first override values from providers that come later)");
for (var index = 0; index < providers.Count; index++)
{
var provider = providers[index];
builder.Append("Provider ");
builder.Append(index);
builder.Append(" = ");
builder.Append(ProviderToString(provider));
builder.AppendLine();
}
builder.AppendLine();
builder.AppendLine("Configuration values: ");
BuildDebugView(providers, builder, configurationRoot.GetChildren(), "");
return builder.ToString();
}
private static void BuildDebugView(
List<IConfigurationProvider> providers,
StringBuilder stringBuilder,
IEnumerable<IConfigurationSection> children,
string indent)
{
foreach (var child in children)
{
(string? value, IConfigurationProvider? provider) = GetValueAndProvider(providers, child.Path);
if (provider != null)
{
stringBuilder
.Append(indent)
.Append(child.Key)
.Append('=')
.Append(value)
.Append(" (")
.Append(ProviderToString(provider))
.AppendLine(")");
}
BuildDebugView(providers, stringBuilder, child.GetChildren(), indent + child.Key + ".");
}
}
[SuppressMessage("System", "CA1031")]
private static IEnumerable<IConfigurationProvider> GetProviders(IConfigurationRoot configurationRoot)
{
foreach (var provider in configurationRoot.Providers.Reverse())
{
switch (provider)
{
case EnvironmentVariablesConfigurationProvider environmentVariablesConfigurationProvider:
bool yieldProvider;
try
{
var prefix = typeof(EnvironmentVariablesConfigurationProvider)
.GetField("_prefix", BindingFlags.Instance | BindingFlags.NonPublic)!
.GetValue(environmentVariablesConfigurationProvider)
as string;
yieldProvider = !string.IsNullOrWhiteSpace(prefix);
}
catch
{
yieldProvider = false;
}
if (yieldProvider)
yield return environmentVariablesConfigurationProvider;
break;
case ChainedConfigurationProvider chainedConfigurationProvider:
var innerProviders = new List<IConfigurationProvider>();
try
{
var configField = typeof(ChainedConfigurationProvider).GetField("_config", BindingFlags.Instance | BindingFlags.NonPublic);
var innerConfigurationRoot = configField!.GetValue(chainedConfigurationProvider) as IConfigurationRoot;
foreach (var innerProvider in GetProviders(innerConfigurationRoot!))
{
innerProviders.Add(innerProvider);
}
}
catch
{
innerProviders.Add(chainedConfigurationProvider);
}
foreach (var innerProvider in innerProviders)
yield return innerProvider;
break;
default:
yield return provider;
break;
}
}
}
private static string ProviderToString(IConfigurationProvider provider)
{
switch (provider)
{
case JsonConfigurationProvider json:
return $"JSON file {json.Source.Path} (reload on change = {json.Source.ReloadOnChange})";
case EnvironmentVariablesConfigurationProvider env:
try
{
var prefix = typeof(EnvironmentVariablesConfigurationProvider)
.GetField("_prefix", BindingFlags.Instance | BindingFlags.NonPublic)!
.GetValue(env);
return $"System environment variables that start with '{prefix}'";
}
catch
{
return $"System environment variables";
}
case CommandLineConfigurationProvider cmd:
return $"Command line arguments";
case MemoryConfigurationProvider mem:
return $"In-memory configuration";
default:
return $"Custom provider: {provider.GetType()}";
}
}
private static (string? Value, IConfigurationProvider? Provider) GetValueAndProvider(
IEnumerable<IConfigurationProvider> providers,
string key)
{
foreach (var provider in providers)
{
if (provider.TryGet(key, out string? value))
{
return (value, provider);
}
}
return (null, null);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
@amoerie
Copy link
Author

amoerie commented Mar 24, 2021

Sample output:

Configuration providers in order of preference: (values from providers that come first override values from providers that come later)
Provider 0 = System environment variables that start with 'MYAPP_'
Provider 1 = Command line arguments
Provider 2 = JSON file appsettings.Development.json (reload on change = True)
Provider 3 = JSON file appsettings.json (reload on change = True)
Provider 4 = System environment variables that start with 'ASPNETCORE_'
Provider 5 = In-memory configuration
Provider 6 = Command line arguments
Provider 7 = System environment variables that start with 'DOTNET_'
Provider 8 = In-memory configuration
Provider 9 = In-memory configuration
Configuration values: 
applicationName=MyApp (System environment variables that start with 'ASPNETCORE_')
CleanupSettings.CronSchedule=0 0 1 ? * * (JSON file appsettings.json (reload on change = True))
CleanupSettings.MaximumDiskSpaceUsageInGigaBytes=15 (JSON file appsettings.json (reload on change = True))
CompressionSettings.OnTheFlyCompressionLevel=BestSpeed (JSON file appsettings.json (reload on change = True))
CompressionSettings.PreRenderCompressionLevel=BestCompression (JSON file appsettings.json (reload on change = True))
CONNECTIONSTRINGS.MYAPPDB=Data Source=;Initial Catalog=MyApp;Integrated Security=true;Persist Security Info=True;MultipleActiveResultSets=True;App=EntityFramework; (System environment variables that start with 'MYAPP_')
CONNECTIONSTRINGS.MINIPROFILER=Data Source=;Initial Catalog=MiniProfiler;Integrated Security=true;MultipleActiveResultSets=True;App=EntityFramework (System environment variables that start with 'MYAPP_')
contentRoot=C:\git\MyApp\MyApp (In-memory configuration)
....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment