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); }
ReadFrom的AppSettings方法,下面代码里的settingConfiguration就是上面的ReadFrom。
代码里面new AppSettingsSettings(settingPrefix, filePath)作为参数传递,后续方法直接调用了它的Configure方法
需要注意的是 class AppSettingsSettings : ILoggerSettings
/// <summary> /// Reads the <appSettings> 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)); }
代码里面的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; }
readonly LoggerConfiguration _loggerConfiguration; internal LoggerSettingsConfiguration(LoggerConfiguration loggerConfiguration) { _loggerConfiguration = loggerConfiguration ?? throw new ArgumentNullException(nameof(loggerConfiguration)); }
readonly string _filePath; readonly string _settingPrefix; public AppSettingsSettings(string settingPrefix = null, string filePath = null) { _filePath = filePath; _settingPrefix = settingPrefix == null ? "serilog:" : $"{settingPrefix}:serilog:"; }
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);
需要注意的是,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实现的
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 = 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); } } }
var methods = CallableConfigurationMethodFinder.FindConfigurationMethods(configurationAssemblies, receiverGroup.Key);
这个应该就是用反射,来找方法