feat(config): show non-default values on startup#11523
feat(config): show non-default values on startup#11523obchain wants to merge 5 commits intoNethermindEth:masterfrom
Conversation
|
@claude review |
|
Claude finished @LukaszRozmej's task in 3m 22s —— View job Code Review:
|
| List<string> nonDefaultLines = []; | ||
| foreach ((string category, string name, object? currentValue, object? _) in configProvider.GetNonDefaultValues()) | ||
| { | ||
| nonDefaultLines.Add($" {category}.{name} = {serializer.Serialize(currentValue)}"); | ||
| } | ||
|
|
||
| if (nonDefaultLines.Count == 0) | ||
| { | ||
| logger.Info("Configuration: all values at defaults."); | ||
| } | ||
| else | ||
| { | ||
| logger.Info($"Configuration: {nonDefaultLines.Count} non-default value(s):\n{string.Join('\n', nonDefaultLines)}"); | ||
| } |
There was a problem hiding this comment.
Because it is reflection based, it can slow down startup a bit, maybe worth it to run in Task.Run?
| { | ||
| continue; | ||
| } | ||
|
|
There was a problem hiding this comment.
[High] Sensitive data exposure
Properties like Seq.ApiKey and KeyStore.Passwords will be included here and logged at Info level in Program.cs. A Seq.ApiKey override would be logged into Seq itself; KeyStore.Passwords contains plaintext account unlock passwords.
Consider adding an IsSensitive flag to ConfigItemAttribute and skipping those properties:
| // Skip sensitive properties to avoid logging credentials | |
| foreach (PropertyInfo property in configInterface.GetProperties() | |
| .Where(static p => p.CanRead && p.GetCustomAttribute<ConfigItemAttribute>()?.IsSensitive != true)) |
Or at a minimum filter out properties where the attribute is not present or HiddenFromDocs is true.
| continue; | ||
| } | ||
|
|
||
| IConfig fresh; |
There was a problem hiding this comment.
[Medium] Incomplete exception handling
Activator.CreateInstance can also throw TargetInvocationException (if the constructor body throws), TypeLoadException, and MethodAccessException. Any of these would propagate and crash startup for what is a diagnostic-only feature. Broaden the catch:
| IConfig fresh; | |
| catch (Exception e) when (e is MissingMethodException or TargetInvocationException or TypeLoadException or MethodAccessException) |
| { | ||
| ArgumentNullException.ThrowIfNull(configProvider); | ||
|
|
||
| foreach (Type configInterface in TypeDiscovery |
There was a problem hiding this comment.
[Medium] LINQ where foreach suffices
Per repo coding style (coding-style.md): "No LINQ when a simple for/foreach works". Replace with an early-continue:
| foreach (Type configInterface in TypeDiscovery | |
| foreach (Type configInterface in TypeDiscovery.FindNethermindBasedTypes(typeof(IConfig))) | |
| { | |
| if (!configInterface.IsInterface) continue; |
- Add IsSensitive flag to ConfigItemAttribute and mark Seq.ApiKey, KeyStore.Passwords, KeyStore.TestNodeKey, EthStats.Secret so their values never reach startup logs via GetNonDefaultValues. - Catch TargetInvocationException, TypeLoadException, and MethodAccessException alongside MissingMethodException so a single reflection-construction failure cannot crash startup. - Replace LINQ Where filter with an early-continue foreach per repo coding style. - Skip the redundant GetDirectInterfaceImplementation TypeDiscovery rescan; reuse the populated instance's concrete type for the fresh default. - Run the non-default scan via Task.Run so the reflection cost no longer blocks the rest of startup; warn (not crash) on failure. - Add a regression test covering sensitive-property suppression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Run the non-default scan synchronously before the runner can mutate config (was a fire-and-forget Task.Run that raced ResolveDataDirectory, BlockTree.Initializer, plugin load, etc., serialised mutating graphs, and emitted resolved absolute paths as "non-default" overrides). - Make the helper resilient: a single failing config interface (provider lookup, ctor, or property getter) now skips that one interface only via an Action<Type, Exception>? callback the caller uses for logging. Previously a single throw would short-circuit the yield iterator and silently drop every later config. - Replace the hand-rolled IEnumerator-based ValuesEqual with StructuralComparisons.StructuralEqualityComparer.Equals. Configs only use scalars / strings / arrays — all handled correctly — and the recursion-cycle hazard goes away. - Mark KeyStore identity fields sensitive (UnlockAccounts, BlockAuthorAccount, EnodeAccount, EnodeKeyFile) so account addresses and key-file paths never reach startup logs. - Add a regression test where one IConfigProvider.GetConfig call throws: enumeration must continue and surface other categories' overrides while the callback receives the failing type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes #1462
Changes
IConfigProvider.GetNonDefaultValues()extension that yields(category, name, current, default)tuples for every config property whose runtime value differs from a freshly constructed implementation instance.Infolevel on startup inNethermind.Runner/Program.cs, alongside the existingDebug-level full dump. Operators see exactly which knobs they overrode.ConfigProviderTests.cscover the empty case, single override, and multi-category overrides.Types of changes
Testing
Requires testing
If yes, did you write tests?
Documentation
Requires documentation update
Requires explanation in Release Notes
Startup logs now include a
Configuration: N non-default value(s):summary listing each overridden setting; operators no longer need to enable Debug logging to see which values they have changed.