Serilog settings appsetting 配置的加载

宝剑锋从磨砺出,梅花香自苦寒来。这篇文章主要讲述Serilog settings appsetting 配置的加载相关的知识,希望能为你提供帮助。
 
 

var loggerConfiguration = new LoggerConfiguration(); loggerConfiguration = loggerConfiguration.ReadFrom.AppSettings(filePath: path); var logger = loggerConfiguration.CreateLogger();

 
 
https://github.com/serilog/serilog/blob/dev/src/Serilog/LoggerConfiguration.cs#L133
ReadFrom的类型是LoggerSettingsConfiguration,构造的时候,把LoggerConfiguration作为参数传递过去。会对应到下文出现的_loggerConfiguration
/// < summary> /// Apply external settings to the logger configuration. /// < /summary> public LoggerSettingsConfiguration ReadFrom => new LoggerSettingsConfiguration(this);

  LoggerSettingsConfiguration类的构造函数
readonly LoggerConfiguration _loggerConfiguration; internal LoggerSettingsConfiguration(LoggerConfiguration loggerConfiguration) { _loggerConfiguration = loggerConfiguration ?? throw new ArgumentNullException(nameof(loggerConfiguration)); }

 
https://github.com/serilog/serilog/blob/dev/src/Serilog/Configuration/ILoggerSettings.cs#L20
/// < summary> /// Implemented on types that apply settings to a logger configuration. /// < /summary> public interface ILoggerSettings { /// < summary> /// Apply the settings to the logger configuration. /// < /summary> /// < param name="loggerConfiguration"> The logger configuration to apply settings to.< /param> void Configure(LoggerConfiguration loggerConfiguration); }

 
【Serilog settings appsetting 配置的加载】 
https://github.com/serilog/serilog-settings-appsettings/blob/dev/src/Serilog.Settings.AppSettings/AppSettingsLoggerConfigurationExtensions.cs#L62
ReadFrom的AppSettings方法,下面代码里的settingConfiguration就是上面的ReadFrom。
代码里面new AppSettingsSettings(settingPrefix, filePath)作为参数传递,后续方法直接调用了它的Configure方法
需要注意的是  class AppSettingsSettings : ILoggerSettings
/// < summary> /// Reads the & lt; appSettings& gt; element of App.config or Web.config, searching for for keys /// that look like: < code> serilog:*< /code> , which are used to configure /// the logger. To add a sink, use a key like < code> serilog:write-to:File.path< /code> for /// each parameter to the sink‘s configuration method. To add an additional assembly /// containing sinks, use < code> serilog:using< /code> . To set the level use /// < code> serilog:minimum-level< /code> . /// < /summary> /// < param name="settingConfiguration"> Logger setting configuration.< /param> /// < param name="settingPrefix"> Prefix to use when reading keys in appSettings. If specified the value /// will be prepended to the setting keys and followed by :, for example "myapp" will use "myapp:serilog:minumum-level. If null /// no prefix is applied.< /param> /// < param name="filePath"> Specify the path to an alternative .config file location. If the file does not exist it will be ignored. /// By default, the current application‘s configuration file will be used.< /param> /// < returns> An object allowing configuration to continue.< /returns> public static LoggerConfiguration AppSettings( this LoggerSettingsConfiguration settingConfiguration, string settingPrefix = null, string filePath = null) { if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration)); if (settingPrefix != null) { if (settingPrefix.Contains(":")) throw new ArgumentException("Custom setting prefixes cannot contain the colon (:) character."); if (settingPrefix == "serilog") throw new ArgumentException("The value "serilog" is not a permitted setting prefix. To use the default, do not specify a custom prefix at all."); if (string.IsNullOrWhiteSpace(settingPrefix)) throw new ArgumentException("To use the default setting prefix, do not supply the settingPrefix parameter, instead pass the default null."); }return settingConfiguration.Settings(new AppSettingsSettings(settingPrefix, filePath)); }

 
https://github.com/serilog/serilog/blob/dev/src/Serilog/Configuration/LoggerSettingsConfiguration.cs#L38
代码里面的settings是上面new AppSettingsSettings(settingPrefix, filePath)的结果。
/// < summary> /// Apply external settings to the logger configuration. /// < /summary> /// < returns> Configuration object allowing method chaining.< /returns> /// < exception cref="ArgumentNullException"> When < paramref name="settings"/> is < code> null< /code> < /exception> public LoggerConfiguration Settings(ILoggerSettings settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); settings.Configure(_loggerConfiguration); return _loggerConfiguration; }

 
https://github.com/serilog/serilog/blob/dev/src/Serilog/Configuration/LoggerSettingsConfiguration.cs#L26
readonly LoggerConfiguration _loggerConfiguration; internal LoggerSettingsConfiguration(LoggerConfiguration loggerConfiguration) { _loggerConfiguration = loggerConfiguration ?? throw new ArgumentNullException(nameof(loggerConfiguration)); }

 
https://github.com/serilog/serilog-settings-appsettings/blob/dev/src/Serilog.Settings.AppSettings/Settings/AppSettings/AppSettingsSettings.cs#L30
readonly string _filePath; readonly string _settingPrefix; public AppSettingsSettings(string settingPrefix = null, string filePath = null) { _filePath = filePath; _settingPrefix = settingPrefix == null ? "serilog:" : $"{settingPrefix}:serilog:"; }

 
https://github.com/serilog/serilog-settings-appsettings/blob/dev/src/Serilog.Settings.AppSettings/Settings/AppSettings/AppSettingsSettings.cs#L36
  class AppSettingsSettings : ILoggerSettings实现的接口方法
public void Configure(LoggerConfiguration loggerConfiguration) { if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration)); IEnumerable< KeyValuePair< string, string> > settings; if (!string.IsNullOrWhiteSpace(_filePath)) { if (!File.Exists(_filePath)) { SelfLog.WriteLine("The specified configuration file `{0}` does not exist and will be ignored.", _filePath); return; }var map = new ExeConfigurationFileMap {ExeConfigFilename = _filePath}; var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); settings = config.AppSettings.Settings .Cast< KeyValueConfigurationElement> () .Select(k => new KeyValuePair< string, string> (k.Key, k.Value)); } else { settings = ConfigurationManager.AppSettings.AllKeys .Select(k => new KeyValuePair< string, string> (k, ConfigurationManager.AppSettings[k])); }var pairs = settings .Where(k => k.Key.StartsWith(_settingPrefix)) .Select(k => new KeyValuePair< string, string> ( k.Key.Substring(_settingPrefix.Length), Environment.ExpandEnvironmentVariables(k.Value))); loggerConfiguration.ReadFrom.KeyValuePairs(pairs); }

  一次性拿到所有的key和value,然后用where过滤一下
settings = ConfigurationManager.AppSettings.AllKeys
.Select(k => new KeyValuePair< string, string> (k, ConfigurationManager.AppSettings[k]));
最后loggerConfiguration.ReadFrom.KeyValuePairs(pairs);
 
https://github.com/serilog/serilog/blob/dev/src/Serilog/Configuration/LoggerSettingsConfiguration.cs#L53
需要注意的是,class KeyValuePairSettings : ILoggerSettings
/// < summary> /// Apply settings specified in the Serilog key-value setting format to the logger configuration. /// < /summary> /// < param name="settings"> A list of key-value pairs describing logger settings.< /param> /// < returns> Configuration object allowing method chaining.< /returns> /// < remarks> In case of duplicate keys, the last value for the key is kept and the previous ones are ignored.< /remarks> /// < exception cref="ArgumentNullException"> When < paramref name="settings"/> is < code> null< /code> < /exception> public LoggerConfiguration KeyValuePairs(IEnumerable< KeyValuePair< string, string> > settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); var uniqueSettings = new Dictionary< string, string> (); foreach (var kvp in settings) { uniqueSettings[kvp.Key] = kvp.Value; } return KeyValuePairs(uniqueSettings); }LoggerConfiguration KeyValuePairs(IReadOnlyDictionary< string, string> settings) { return Settings(new KeyValuePairSettings(settings)); }

 
 
并且这里的setting是上面曾经提到过的,但是这次传给方法的参数是class KeyValuePairSettings : ILoggerSettings
/// < summary> /// Apply external settings to the logger configuration. /// < /summary> /// < returns> Configuration object allowing method chaining.< /returns> /// < exception cref="ArgumentNullException"> When < paramref name="settings"/> is < code> null< /code> < /exception> public LoggerConfiguration Settings(ILoggerSettings settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); settings.Configure(_loggerConfiguration); return _loggerConfiguration; }

 
然后这次触发的Configure方法是KeyValuePairSettings实现的
https://github.com/serilog/serilog/blob/dev/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs#L86
public void Configure(LoggerConfiguration loggerConfiguration) { if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration)); var directives = _settings .Where(kvp => _supportedDirectives.Any(kvp.Key.StartsWith)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); var declaredLevelSwitches = ParseNamedLevelSwitchDeclarationDirectives(directives); if (directives.TryGetValue(MinimumLevelDirective, out var minimumLevelDirective) & & Enum.TryParse(minimumLevelDirective, out LogEventLevel minimumLevel)) { loggerConfiguration.MinimumLevel.Is(minimumLevel); }foreach (var enrichPropertyDirective in directives.Where(dir => dir.Key.StartsWith(EnrichWithPropertyDirectivePrefix) & & dir.Key.Length > EnrichWithPropertyDirectivePrefix.Length)) { var name = enrichPropertyDirective.Key.Substring(EnrichWithPropertyDirectivePrefix.Length); loggerConfiguration.Enrich.WithProperty(name, enrichPropertyDirective.Value); }if (directives.TryGetValue(MinimumLevelControlledByDirective, out var minimumLevelControlledByLevelSwitchName)) { var globalMinimumLevelSwitch = LookUpSwitchByName(minimumLevelControlledByLevelSwitchName, declaredLevelSwitches); loggerConfiguration.MinimumLevel.ControlledBy(globalMinimumLevelSwitch); }foreach (var minimumLevelOverrideDirective in directives.Where(dir => dir.Key.StartsWith(MinimumLevelOverrideDirectivePrefix) & & dir.Key.Length > MinimumLevelOverrideDirectivePrefix.Length)) { var namespacePrefix = minimumLevelOverrideDirective.Key.Substring(MinimumLevelOverrideDirectivePrefix.Length); if (Enum.TryParse(minimumLevelOverrideDirective.Value, out LogEventLevel overriddenLevel)) { loggerConfiguration.MinimumLevel.Override(namespacePrefix, overriddenLevel); } else { var overrideSwitch = LookUpSwitchByName(minimumLevelOverrideDirective.Value, declaredLevelSwitches); loggerConfiguration.MinimumLevel.Override(namespacePrefix, overrideSwitch); } }var matchCallables = new Regex(CallableDirectiveRegex); var callableDirectives = (from wt in directives where matchCallables.IsMatch(wt.Key) let match = matchCallables.Match(wt.Key) select new { ReceiverType = CallableDirectiveReceiverTypes[match.Groups["directive"].Value], Call = new ConfigurationMethodCall { MethodName = match.Groups["method"].Value, ArgumentName = match.Groups["argument"].Value, Value = https://www.songbingjia.com/android/wt.Value } }).ToList(); if (callableDirectives.Any()) { var configurationAssemblies = LoadConfigurationAssemblies(directives).ToList(); foreach (var receiverGroup in callableDirectives.GroupBy(d => d.ReceiverType)) { var methods = CallableConfigurationMethodFinder.FindConfigurationMethods(configurationAssemblies, receiverGroup.Key); var calls = receiverGroup .Select(d => d.Call) .GroupBy(call => call.MethodName) .ToList(); ApplyDirectives(calls, methods, CallableDirectiveReceivers[receiverGroup.Key](loggerConfiguration), declaredLevelSwitches); } } }

 


    推荐阅读