Serilog settings appsetting 配置的加载

Posted Chuck Lu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
    }

 

 

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 = 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);
                }
            }
        }

 

以上是关于Serilog settings appsetting 配置的加载的主要内容,如果未能解决你的问题,请参考以下文章

译Serilog 配置基础知识

如何使用 Serilog 以有效的 json 格式记录复杂对象?

向 Serilog 添加自定义属性

使用serilog和Castle winsdor日志时,Logger.InfoFormat没有记录第一个参数。

Serilog高级玩法之用Serilog记录所选终结点附加属性

Serilog高级玩法之用Serilog记录所选终结点附加属性