.Net Core中的日志用法和源代码分析

电脑杂谈  发布时间:2020-06-03 13:14:44  来源:网络整理

asp.net mvc 记录日志_net日志分类记录源码_日志分类

在.Net Core中,该系统带有集成的日志记录系统. 让我们看看如何使用它:

第一步是添加LogProvider. 这是告诉容器我们日志输出的来源. LogProvider的目的是创建一个Logger. 在Asp.Net Core中,默认情况下添加了三个提供程序:

            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
                logging.AddEventSourceLogger();
            })

此处添加的LoggerProvider最终将注入到LoggerFactory中以创建ILogger.

在上面的代码中也可以看到

AddConfiguration,它是通过配置添加日志过滤条件的. 我待会儿解释.

添加提供程序后,您可以从容器中获取日志对象. 当您获得它时,有两种类型:

asp.net mvc 记录日志_net日志分类记录源码_日志分类

这是最简单的示例. 实际记录日志的位置通常不在以下结构中:

    public class HomeController : Controller
    {
        public HomeController(ILogger<HomeController> loggerT, ILoggerFactory loggerFactory)
        {
            var testLog = loggerFactory.CreateLogger("Test");
            loggerT.LogInformation("Info");
            testLog.LogInformation("Info");
        }
}

谈到这一点,您可能会有一些问题: 我添加了多个LogProvider,自然会生成多个ILogger. 那么,这里的工厂类生成的ILogger是哪一个?答案是: 没有一个!工厂在此处生成的ILogger是Microsoft.Extensions.Logging.Logger系统的默认实现. 在Logger的默认实现中,有一个LoggerInformation数组. LoggerInformation包含由这些LogProvider生成的ILogger和一些过滤条件. 在不添加任何过滤条件的情况下,以上代码将同时记录到3个不同的位置.

添加了多少个LogProvider,这个LoggerInformation数组的长度就为多少。LoggerInformation和LogProvider一一对应。

此外,如果要通过注入获取ILogger,它将无法工作,因为在初始化期间,仅在ILoggerFactory和ILogger 中,不会在容器中注册ILogger实现. 值得一提的是,每个ILogger(默认情况下实现的Logger)将根据CategoryName进行缓存.

在谈论日志过滤时,我们必须首先弄清日志过滤的过滤条件:

这些参数和ILogger(由LogProvider生成)将一起保存在LoggerInformation对象中. LoggerInformation中还有另一个属性,该属性也用于日志过滤:

net日志分类记录源码_asp.net mvc 记录日志_日志分类

public Func<string, string, LogLevel, bool> Filter { get; set; }

这是一个委托,它需要三个参数,分别代表Category,ProviderType和LogLevel,并返回一个布尔值,指示是否启用日志类型. 有了这些信息,每当使用ILogger(系统的Logger的默认实现)进行日志写入时,它将循环遍历LoggerInformation数组对象,以便可以独立控制每个LogProvider生成的ILogger对象. LogLevel确定是否需要输出日志.

下一个问题是,如何配置过滤规则?配置过滤规则的唯一方法是配置LoggerFilterOptions对象. 有关如何自行配置Option对象的信息,请参阅本系列的上一篇文章.

系统提供了一系列扩展方法来帮助我们配置此对象. 有关详细信息,请参阅: 还有基于配置的扩展. 核心代码是: 这是什么LoggerFilterOptions?看一下源代码: <

    public class LoggerFilterOptions
    {
        /// <summary>
        /// Gets or sets the minimum level of log messages if none of the rules match.
        /// </summary>
        public LogLevel MinLevel { get; set; }
        /// <summary>
        /// Gets the collection of <see cref="LoggerFilterRule"/> used for filtering log messages.
        /// </summary>
        public IList<LoggerFilterRule> Rules { get; } = new List<LoggerFilterRule>();
    }

系统提供的所有扩展方法是将Rule添加到该对象的Rule中,然后在创建ILogger(由LogProvider创建)规则时根据某些规则匹配属于该ILogger(由LogProvider创建)的扩展方法. ,分配给LoggerInformation的Filter属性. 也就是说,即使有很多规则,最终也只会找到最符合条件的规则. 如果未找到,则使用MinLevel. 默认情况下,MinLevel为Information. LoggerFilterRule中包含的属性是上述过滤条件:

    public class LoggerFilterRule
    {
        public LoggerFilterRule(string providerName, string categoryName, LogLevel? logLevel, Func<string, string, LogLevel, bool> filter)
        {
            ProviderName = providerName;
            CategoryName = categoryName;
            LogLevel = logLevel;
            Filter = filter;
        }
        /// <summary>
        /// Gets the logger provider type or alias this rule applies to.
        /// </summary>
        public string ProviderName { get; }
        /// <summary>
        /// Gets the logger category this rule applies to.
        /// </summary>
        public string CategoryName { get; }
        /// <summary>
        /// Gets the minimum <see cref="LogLevel"/> of messages.
        /// </summary>
        public LogLevel? LogLevel { get; }
        /// <summary>
        /// Gets the filter delegate that would be applied to messages that passed the <see cref="LogLevel"/>.
        /// </summary>
        public Func<string, string, LogLevel, bool> Filter { get; }
        public override string ToString()
        {
            return $"{nameof(ProviderName)}: '{ProviderName}', {nameof(CategoryName)}: '{CategoryName}', {nameof(LogLevel)}: '{LogLevel}', {nameof(Filter)}: '{Filter}'";
        }
    }

net日志分类记录源码_日志分类_asp.net mvc 记录日志

查找规则有哪些匹配规则?核心代码如下(稍后可以直接查看说明):

    internal class LoggerRuleSelector
    {
        public void Select(LoggerFilterOptions options, Type providerType, string category, out LogLevel? minLevel, out Func<string, string, LogLevel, bool> filter)
        {
            filter = null;
            minLevel = options.MinLevel;
            // Filter rule selection:
            // 1. Select rules for current logger type, if there is none, select ones without logger type specified
            // 2. Select rules with longest matching categories
            // 3. If there nothing matched by category take all rules without category
            // 3. If there is only one rule use it's level and filter
            // 4. If there are multiple rules use last
            // 5. If there are no applicable rules use global minimal level
            var providerAlias = ProviderAliasUtilities.GetAlias(providerType);
            LoggerFilterRule current = null;
            foreach (var rule in options.Rules)
            {
                if (IsBetter(rule, current, providerType.FullName, category)
                    || (!string.IsNullOrEmpty(providerAlias) && IsBetter(rule, current, providerAlias, category)))
                {
                    current = rule;
                }
            }
            if (current != null)
            {
                filter = current.Filter;
                minLevel = current.LogLevel;
            }
        }

        private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, string logger, string category)
        {
            // Skip rules with inapplicable type or category
            if (rule.ProviderName != null && rule.ProviderName != logger)
            {
                return false;
            }
            if (rule.CategoryName != null && !category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase))
            {
                return false;
            }
            if (current?.ProviderName != null)
            {
                if (rule.ProviderName == null)
                {
                    return false;
                }
            }
            else
            {
                // We want to skip category check when going from no provider to having provider
                if (rule.ProviderName != null)
                {
                    return true;
                }
            }
            if (current?.CategoryName != null)
            {
                if (rule.CategoryName == null)
                {
                    return false;
                }
                if (current.CategoryName.Length > rule.CategoryName.Length)
                {
                    return false;
                }
            }
            return true;
        }
    }

在配置规则时,默认情况下ProviderName应该是该类的全名,而上面提到的ProviderAlias是指该类的昵称. 将ProviderAliasAttribute添加到该类中,以为其定义昵称,以方便配置.

让我们再次看看IsBetter方法:

如果传递的规则应用于特定的提供程序,并且与当前的日志提供程序不一致,则它会直接返回False,因为它不匹配. 如果传递的规则应用于特定的类别(默认为类)全名,并且与当前Log的类别不匹配,则返回False,因为它不匹配如果将当前规则应用于特定的Provider,并且传入的规则未应用于特定的Provider,它返回False,因为当前优先级更高. 如果当前规则未应用于特定的提供者,而传入规则被应用于特定的提供者(由于存在第一点,则传入规则提供者必须为,并且由于存在第二点,如果传入的规则包含“类别”,则它必须与当前Log相同),然后返回Truenet日志分类记录源码,因为如果将当前规则应用于特定的“特定类别”,则传入的优先级更高,而传递的规则不应用于特定类别,它会返回False,因为当前优先级更高. 如果当前规则应用于特定类别,并且当前传递的规则也应用于特定类别,但是当前匹配度更高(字符串长度越长),则返回Falsenet日志分类记录源码,因为当前优先级更高,并且其余的返回True

然后最终的规则匹配度从低到高应该是:

ProviderName: 空,CategoryName: nullProviderName: 空,CategoryName: 匹配(匹配长度越长,匹配越多)ProviderName: 已匹配,CategoryName: nullProviderName: 匹配,CategoryName: 匹配(匹配长度越长,匹配越多)

日志分类_asp.net mvc 记录日志_net日志分类记录源码

CategoryName的匹配程度是指字符串的长度. 如果未找到规则,则使用MinLevel.

使用配置文件配置日志过滤条件. 注意: 使用配置文件时,只能设置特定提供程序和类别的LogLevel,并且不能指定委托类型Filter. 如果要使用过滤器,则只能通过编码进行配置. 让我们看一个日志配置示例:

  "Logging": {
    "LogLevel": {
      "Default": "Debug"
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning",
        "CoreLogging.Controllers": "Error",
        "CoreLogging.Controllers.HomeController": "Info"
      }
    }
  }

在LogLevel下配置,LogLevel下的直接子节点表示Proivder为空,子节点的名称表示Category,值表示LogLevel.

对于“记录”下的其他直接子节点,节点名称表示ProviderName. LogLevel子节点下面的子节点,名称代表类别,值代表LogLevel.

此外,Category的值为Default,并且在最终解析时它将被解析为null. 它不区分大小写.

此处的控制台可以用Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider替换,因为ConsoleLoggerProvider具有特殊的性别名称Console,所以您不能写全名. [ProviderAlias(“控制台”)]

最后,此配置将解释为:

对于非控制台日志记录,仅记录日志级别高于DebugConsole的日志. 如果类别不是以CoreLogging.Controllers开头,则仅记录控制台级别高于“警告”的日志,如果类别以CoreLogging.Controllers开头但以CoreLogging.Controllers.HomeController开头,则仅记录级别高于“错误”的控制台日志. 如果Category以CoreLogging.Controllers.HomeController开头,则仅记录级别高于Info的日志

本文提到的源代码全在

转载于:


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-232408-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 季美汐
        季美汐

        这家公司演变成三鹿

      热点图片
      拼命载入中...