详解.Net Core中的日志组件(Logging)

1、介绍

Logging组件是微软实现的日志记录组件包括控制台(Console)、调试(Debug)、事件日志(EventLog)和TraceSource,但是没有实现最常用用的文件记录日志功能(可以用其他第三方的如NLog、Log4Net。之前写过NLog使用的文章)。

2、默认配置

新建.Net Core Web Api项目,添加下面代码。

  [Route("api/[controller]")]
  public class ValuesController : Controller
  {
    ILogger<ValuesController> logger;
     //构造函数注入Logger
    public ValuesController(ILogger<ValuesController> logger)
    {
      this.logger = logger;
    }
    [HttpGet]
    public IEnumerable<string> Get()
    {
      logger.LogWarning("Warning");
      return new string[] { "value1", "value2" };
    }
  }

运行结果如下:

我刚开始接触的时候,我就有一个疑问我根本没有配置关于Logger的任何代码,仅仅写了注入,为什么会起作用呢?最后我发现其实是在Program类中使用了微软默认的配置。

public class Program
  {
    public static void Main(string[] args)
    {
      BuildWebHost(args).Run();
    }
    public static IWebHost BuildWebHost(string[] args) =>
      WebHost.CreateDefaultBuilder(args)//在这里使用了默认配置
        .UseStartup<Startup>()
        .Build();
  }

下面为CreateDefaultBuilder方法的部分源码,整个源码在 https://github.com/aspnet/MetaPackages ,可以看出在使用模板创建项目的时候,默认添加了控制台和调试日志组件,并从appsettings.json中读取配置。

        builder.UseKestrel((builderContext, options) =>
        {
          options.Configure(builderContext.Configuration.GetSection("Kestrel"));
        })
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
          var env = hostingContext.HostingEnvironment;
            //加载appsettings.json文件 使用模板创建的项目,会生成一个配置文件,配置文件中包含Logging的配置项
          config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
             .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
            .......
        })
        .ConfigureLogging((hostingContext, logging) =>
        { 
            //从appsettings.json中获取Logging的配置
          logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            //添加控制台输出
          logging.AddConsole();
            //添加调试输出
          logging.AddDebug();
        })

3、建立自己的Logging配置

首先修改Program类

public class Program
  {
    public static void Main(string[] args)
    {
      //指定配置文件路径
      var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())//设置基础路径
                .AddJsonFile($"appsettings.json", true, true)//加载配置文件
                .AddJsonFile($"appsettings.{EnvironmentName.Development}.json", true, true)
                .Build();

      var host = new WebHostBuilder()
            .UseKestrel()
            .UseStartup<Startup>()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseConfiguration(config)//使用配置
            .UseUrls(config["AppSettings:Url"])//从配置中读取 程序监听的端口号
            .UseEnvironment(EnvironmentName.Development)//如果加载了多个环境配置,可以设置使用哪个配置 一般有测试环境、正式环境

              //.ConfigureLogging((hostingCotext, logging) => //第一种配置方法 直接在webHostBuilder建立时配置 不需要修改下面的Startup代码
                          //{
                          //     logging.AddConfiguration(hostingCotext.Configuration.GetSection("Logging"));
                          //     logging.AddConsole();
                          //})
            .Build();
      host.Run();
    }
  }

修改Startup类如下面,此类的执行顺序为 Startup构造函数 > ConfigureServices > Configure

public class Startup
  {
    public IConfiguration Configuration { get; private set; }
    public IHostingEnvironment HostingEnvironment { get; private set; }
    //在构造函数中注入 IHostingEnvironment和IConfiguration,配置已经在Program中设置了,注入后就可以获取配置文件的数据
    public Startup(IHostingEnvironment env, IConfiguration config)
    {
      HostingEnvironment = env;
      Configuration = config;
    }
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        //第二种配置 也可以这样加上日志功能,不用下面的注入
              //services.AddLogging(builder =>
              //{
                //  builder.AddConfiguration(Configuration.GetSection("Logging"))
                //    .AddConsole();
              //});
    }
     //注入ILoggerFactory
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }
       //第三种配置 注入ILogggerFactory,然后配置参数
      //添加控制台输出
      loggerFactory.AddConsole(Configuration.GetSection("Logging"));
       //添加调试输出
      loggerFactory.AddDebug();
      app.UseMvc();
    }
  }

这种结构就比较清晰明了。

4、Logging源码解析

三种配置其实都是为了注入日志相关的服务,但是调用的方法稍有不同。现在我们以第二种配置来详细看看其注入过程。首先调用AddLogging方法,其实现源码如下:

public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
    {
      services.AddOptions();//这里会注入最基础的5个服务 option相关服务只要是跟配置文件相关,通过Option服务获取相关配置文件参数参数 

      services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
      services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
      services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<LoggerFilterOptions>>(new DefaultLoggerLevelConfigureOptions(LogLevel.Information)));

      configure(new LoggingBuilder(services));
      return services;
    }

接着会调用AddConfiguration

 public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration)
    {
      builder.AddConfiguration();
       //下面为AddConfiguration的实现

        public static void AddConfiguration(this ILoggingBuilder builder)
            {
                builder.Services.TryAddSingleton<ILoggerProviderConfigurationFactory, LoggerProviderConfigurationFactory>();
                builder.Services.TryAddSingleton(typeof(ILoggerProviderConfiguration<>), typeof(LoggerProviderConfiguration<>));
            }
      builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration));
      builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration));
      builder.Services.AddSingleton(new LoggingConfiguration(configuration));

      return builder;
    }

下面来看打印日志的具体实现:

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
       var loggers = Loggers;
      List<Exception> exceptions = null;
       //loggers为LoggerInformation数组,如果你在Startup中添加了Console、Deubg日志功能了,那loggers数组值有2个,就是它俩。
      foreach (var loggerInfo in loggers)
      {  //循环遍历每一种日志打印,如果满足些日子的条件,才执行打印log方法。比如某一个日志等级为Info,
          //但是Console配置的最低打印等级为Warning,Debug配置的最低打印等级为Debug
          //则Console中不会打印,Debug中会被打印
        if (!loggerInfo.IsEnabled(logLevel))
        {
          continue;
        }
        try
        {
            //每一种类型的日志,对应的打印方法不同。执行对应的打印方法
          loggerInfo.Logger.Log(logLevel, eventId, state, exception, formatter);
        }
        catch (Exception ex)
        {
          if (exceptions == null)
          {
            exceptions = new List<Exception>();
          }

          exceptions.Add(ex);
        }
      }
    }

下面具体看一下Console的打印实现:

首先ConsoleLogger实现了ILogger的Log方法,并在方法中调用WriteMessage方法

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
       //代码太多 我就省略一些判空代码
      var message = formatter(state, exception);

      if (!string.IsNullOrEmpty(message) || exception != null)
      {
        WriteMessage(logLevel, Name, eventId.Id, message, exception);
      }
    }

    public virtual void WriteMessage(LogLevel logLevel, string logName, int eventId, string message, Exception exception)
    {
       .......
      if (logBuilder.Length > 0)
      {
        var hasLevel = !string.IsNullOrEmpty(logLevelString);
        //这里是主要的代码实现,可以看到,并没有写日志的代码,而是将日志打入到一个BlockingCollection<LogMessageEntry>队列中
        _queueProcessor.EnqueueMessage(new LogMessageEntry()
        {
          Message = logBuilder.ToString(),
          MessageColor = DefaultConsoleColor,
          LevelString = hasLevel ? logLevelString : null,
          LevelBackground = hasLevel ? logLevelColors.Background : null,
          LevelForeground = hasLevel ? logLevelColors.Foreground : null
        });
      }
       ......
    }

下面看日志被放入队列后的具体实现:

public class ConsoleLoggerProcessor : IDisposable
  {
        private const int _maxQueuedMessages = 1024;
        private readonly BlockingCollection<LogMessageEntry> _messageQueue = new BlockingCollection<LogMessageEntry>(_maxQueuedMessages);
        private readonly Thread _outputThread;
   public IConsole Console;

    public ConsoleLoggerProcessor()
    {
      //在构造函数中启动一个线程,执行ProcessLogQueue方法
       //从下面ProcessLogQueue方法可以看出,是循环遍历集合,将集合中的数据打印

      _outputThread = new Thread(ProcessLogQueue)
      {
        IsBackground = true,
        Name = "Console logger queue processing thread"public virtual void EnqueueMessage(LogMessageEntry message)
    {
      if (!_messageQueue.IsAddingCompleted)
      {
        try
        {
          _messageQueue.Add(message);
          return;
        }
        catch (InvalidOperationException) { }
      }

      WriteMessage(message);
    }

    internal virtual void WriteMessage(LogMessageEntry message)
    {
      if (message.LevelString != null)
      {
        Console.Write(message.LevelString, message.LevelBackground, message.LevelForeground);
      }

      Console.Write(message.Message, message.MessageColor, message.MessageColor);
      Console.Flush();
    }

    private void ProcessLogQueue()
    {
    
      try
        {
          //GetConsumingEnumerable()方法比较特殊,当集合中没有值时,会阻塞自己,一但有值了,知道集合中又有元素继续遍历
          foreach (var message in _messageQueue.GetConsumingEnumerable())
        {
          WriteMessage(message);
        }
      }
      catch
      {
        try
        {
          _messageQueue.CompleteAdding();
        }
        catch { }
      }
    }
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • .NET Core开发日志之OData(Open Data Protocol)

    简述 OData,即Open Data Protocol,是由微软在2007年推出的一款开放协议,旨在通过简单.标准的方式创建和使用查询式及交互式RESTful API. 类库 在.NET Core中想要使用OData功能的话需要添加Microsoft.AspNetCore.OData包. dotnet add package Microsoft.AspNetCore.OData 准备模型类 public class Address { public string City { get; set

  • 详解ASP.NET Core应用中如何记录和查看日志

    日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.LoggerFactory和LoggerProvider这三个核心对象组成.我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加. 一. 配置LoggerFactory 我们在上面一节演示了一个展示ASP.NET Core默认注册服务的实例,细心的读者一定会看到显

  • .NET Core日志配置的方法

    熟悉ASP.NET的开发者一定对web.config文件不陌生.在ASP.NET环境中,要想添加配置参数,一般也都会在此文件中操作.其中最常用的莫过于AppSettings与ConnectionStrings两项.而要在代码中获得文件中的配置信息,ConfigurationManager则是必不可少需要引入的程序集. 然而到了ASP.NET Core时代,存储与读取配置的方式都发生了改变. 如果对ASP.NET Core项目有所了解的话,应该会看到过appsettings.json这个文件.这里

  • 详解.Net core2.0日志组件Log4net、Nlog简单性能测试

    .Net core之Log4net.Nlog简单性能测试 比较log4net.nlog的文件写入性能(.netcore环境),涉及代码和配置如有不正确的地方,还请批评指正. 测试环境 开发工具: Vsual Studio 2017 15.3 框架版本: .net core 2.0 操作系统:window10 Enterprise 1703 硬件配置:CPU I3-4170 3.7GHz,内存 8G,固态硬盘 日志组件 log4net 2.0.8 nlog 5.0.0-beta10 测试用例 1.

  • .Net Core项目如何添加日志功能详解

    一.微软内置的日志组件 在.Net Core中使用模板新建的Web Api项目时,会自动加入日志功能.只需要在控制器中注入ILogger就可以了.命名空间为:Microsoft.Extensions.Logging. 会发现只有Error被打印到了控制台,Trace没有被打印.那是因为在appsetting.json中配置了Logging>Console>Default的等级为Debug,日志的等级大于等于Debug才会输出到控制台.在这里说一下LogLevel:Trace<Debug&

  • ASP.NET Core开发教程之Logging利用NLog写日志文件

    前言 本文主要介绍了ASP.NET Core 开发-Logging 使用NLog 写日志文件的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 NLog 可以适用于 .NET Core 和 ASP.NET Core . ASP.NET Core已经内置了日志支持,可以轻松输出到控制台. 学习Logging 组件的相关使用,使用NLog 将日志写入到文件记录. Logging 使用 新建一个 ASP.NET Core 项目,为了方便,我选择Web 应用程序,改身份验证 改为

  • Asp.Net Core轻松学之利用日志监视进行服务遥测详解

    前言 在 Net Core 2.2 中,官方文档表示,对 EventListener 这个日志监视类的内容进行了扩充,同时赋予了跟踪 CoreCLR 事件的权限:通过跟踪 CoreCLR 事件,比如通过跟踪 CoreCLR 事件,可以了解和收集到比如 GC,JIT,ThreadPool,intreop 这些运行时服务的行为:通过使用配置注入,我们将获得一种动态跟踪事件的能力. 1. EventListener 介绍 1.1 EventListener 中文直译为:事件侦听器 EventListe

  • 详解.Net Core中的日志组件(Logging)

    1.介绍 Logging组件是微软实现的日志记录组件包括控制台(Console).调试(Debug).事件日志(EventLog)和TraceSource,但是没有实现最常用用的文件记录日志功能(可以用其他第三方的如NLog.Log4Net.之前写过NLog使用的文章). 2.默认配置 新建.Net Core Web Api项目,添加下面代码. [Route("api/[controller]")] public class ValuesController : Controller

  • 详解.NET Core中的数据保护组件

    背景介绍 在 OWASP(开放式 Web 应用程序安全项目) 2013 年发布的报告中,将不安全的直接对象引用(Insecure Direct Object Reference)标记为 十大 Web 应用程序风险之一, 其表现形式是对象的引用(例如数据库主键)被各种恶意攻击利用, 所以对于Api返回的各种主键外键ID, 我们需要进行加密. .NET Core 的数据保护组件 .NET Core 中内置了一个IDataProtectionProvider接口和一个IDataProtector接口.

  • 详解.NET Core中的Worker Service

    当你想到ASP.NET Core时,可能会想到Web应用程序后端代码,包括MVC和WebAPI.MVC视图和Razor页面还允许使用后端代码生成带有HTML元素的前端UI.全新的Blazor更进一步,允许使用WebAssembly在Web浏览器中运行客户端.NET代码.最后,我们现在有了一个Worker Service应用程序的模板. 这是在ASP.NET Core早期预览中引入的.虽然项目模板最初列在Web模板下,但此后在向导中重新定位了一个级别.这是在.NET Core中创建长时间运行的跨平

  • 详解ABP框架中的日志管理和设置管理的基本配置

    日志管理 Server side(服务器端) ASP.NET Boilerplate使用Castle Windsor's logging facility日志记录工具,并且可以使用不同的日志类库,比如:Log4Net, NLog, Serilog... 等等.对于所有的日志类库,Castle提供了一个通用的接口来实现,我们可以很方便的处理各种特殊的日志库,而且当业务需要的时候,很容易替换日志组件. 译者注释:Castle是什么:Castle是针对.NET平台的一个开源项目,从数据访问框架ORM到

  • 详解Android App中创建ViewPager组件的方法

    现在很多app一打开就是一个ViewPager,然后可以用手指滑,每滑一次就换一张图,底下还会有圈圈表示说现在滑到第几章~ 通常这些图片都是放功能简介或是使用教学之类的,我的需求很简单,就是上面提到的那样而已. 有两种做法,一种是找现有套件,查了一堆资料每个都跟我推荐ViewPagerIndicator这套,我之前也看过这套,只是看起来需要有fragment再加上google play范例好像载不到了,所以只好自己实做一个. Viewpager的实作可参考Android ViewPager使用详

  • 详解iOS App中UISwitch开关组件的基本创建及使用方法

    一.第一种创建UISwitch组件的方法,在代码中动态创建. 1.打开Xcode, 新建项目Switch,选择Single View Application. 2.打开ViewController.m文件在viewDidLoad方法里添加代码: 复制代码 代码如下: (void)viewDidLoad  {      [super viewDidLoad];      UISwitch *switchButton = [[UISwitch alloc] initWithFrame:CGRectM

  • 详解Nginx服务器中的日志相关配置

    nginx 日志相关指令主要有两条, log_format,用来设置日志格式, access_log,用来指定日志文件的存放路径.格式和缓存大小 log_format 格式 log_format name( 格式名字) 格式样式(即想要得到什么样的日志内容) 默认的示例: log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_s ent "$ht

  • 详解Android应用中使用TabHost组件进行布局的基本方法

    TabHost布局文件 我们先来了解一下布局文件的基本内容: 1. 根标签及id 设置Android自带id : XML布局文件中, 可以使用 标签设置, 其中的id 需要引用 android的自带id : android:id=@android:id/tabhost ; getHost()获取前提 : 设置了该id之后, 在Activity界面可以使用 getHost(), 获取这个TabHost 视图对象; 示例 : 复制代码 代码如下: <tabhost android:id="@a

  • ASP.Net Core中的日志与分布式链路追踪

    目录 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory ILoggerProvider ILogger Logging Providers 怎么使用 日志等级 Trace.Debug 链路跟踪 OpenTracing 上下文和跟踪功能 跟踪单个功能 将多个跨度合并到一条轨迹中 传播过程中的上下文 分布式链路跟踪 在不同进程中跟踪 在 ASP.NET Core 中跟踪 OpenTracing API 和

  • 详解Go语言中配置文件使用与日志配置

    目录 项目结构调整 配置文件使用 日志配置 小结 接着上一篇的文章构建的项目:Go语学习笔记 - 环境安装.接口测试 只是简单的把GET和POST接口的使用测试了一下. 我还是想按照正常的项目结构调整一下,这篇笔记主要是三个部分:调整项目目录结构.增加配置文件使用.增加日志配置,很常规而且也是每个项目都需要用到的. 项目地址:github地址 项目结构调整 说先对项目目录结构调整一下,按照我自己的开发习惯,增加了几个目录. 项目结构如下图: 解释一下目录结构 app/constants:主要放置

随机推荐