当前位置:首页 > 日记 > 正文

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

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

日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性。我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger、LoggerFactory和LoggerProvider这三个核心对象组成。我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加。

一、 配置LoggerFactory

我们在上面一节演示了一个展示ASP.NET Core默认注册服务的实例,细心的读者一定会看到显示的列表中就包含了针对LoggerFactory的服务。如果这个默认的LoggerFactory服务不能满足我们的需求,我们完全可以配置任何一个需要的LoggerFactory,针对LoggerFactory的设置可以直接调用WebHostBuilder的UseLoggerFactory方法来实现。

public interface IWebHostBuilder  {    IWebHostBuilder UseLoggerFactory(ILoggerFactory loggerFactory);    IWebHostBuilder ConfigureLogging(Action<ILoggerFactory> configureLogging);    ...  }

不过针对日志的配置更多地还是体现在针对某种LoggerProvider的添加,而这可以通过调用WebHostBuilder的ConfigureLogging方法来完成。我们在上面演示的实例中就曾经采用如下的方式将一个ConsoleLoggerProvider注册到LoggerFactory之上,这样我们可以直接在宿主应用的扩展台上看到记录的日志信息。

 new WebHostBuilder()    .ConfigureLogging(factory=>factory.AddConsole())    ...

既然LoggerFactory已经作为一个服务进行了注册,那么我们完全按照依赖注入的来获取这个对象,并利用它创建对应的Logger对象来写日志。如果我们需要在一个定义的中间件中写入某种类型的日志,就可以按照如下的方式在Invoke方法中定义ILoggerFactory类型的参数注入这个LoggerFactory。

 public class FoobarMiddleware  {    private RequestDelegate _next;       public FoobarMiddleware(RequestDelegate next)    {      _next = next;    }       public async Task Invoke(HttpContext context, ILoggerFactory loggerFactory)    {      ILogger<FoobarMiddleware> logger = loggerFactory.CreateLogger<FoobarMiddleware>();      logger.LogInformation("...");      await _next(context);    }  }

不仅仅我们开发的应用或者中间件可以利用注册的LoggerFactory来创建进行日志记录的Logger对象,ASP.NET Core管道本身也会在处理请求过程中采用相同的方式记录一些日志。比如管道每次处理请求的开始和结束时候分别会写入两条Information等级的日志,我们现在就来通过一个简单的实例看看这两条日志信息具有怎样的内容。

public class Program  {    public static void Main()    {      new WebHostBuilder()        .ConfigureLogging(factory=>factory.AddConsole())        .UseKestrel()        .UseStartup<Startup>()                .Build()        .Run();    }  }    public class Startup  {    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)    {      app.Run(async context =>      {        loggerFactory.CreateLogger("App").LogInformation("Log entry for test...");        await context.Response.WriteAsync("Hello world!");      });    }  }

如上所示的代码有两处与日志有关,第一个地方是调用WebHostBuilder的ConfigureLogging方法通过调用扩展方法AddConsole将一个ConsoleProvider添加到当前LoggerFactory之上,另一个地方就是启动类的Configure方法注册的中间件在执行过程中会利用注入的LoggerFactory创建一个Logger对象,我们利用后者写入了一条Information等级的日志。我们运行程序之后利用浏览器访问目标地址后,宿主控制台上会出现如下图所示的三条日志。除了第二条日志是由我们自己编写的代码写入的之外,其余两条都是ASP.NET Core框架自己写入的。第一条日志包含不仅仅包含请求的目标地址,还包括请求采用的协议(HTTP/1.1)和HTTP方法(GET),第三条则反映了整个请求处理过程所花的时间。

由于ASP.NET Core管道对请求的处理总是在一个由HttpApplication创建的执行上下文中进行,所以上下文的创建和回收释放可以视为 整个请求处理流程开始和结束的标识。对于上述的这两条分别在处理请求开始和结束时写入的日志,实际上是在HostingApplication的CreateContext和DisposeContext方法分别被调用的时候被记录下来的。之所以在结束的时候能够计算出整个请求处理过程所花的时间,是因为创建的这个上下文对象保存了开始处理请求的时间戳,该时间戳对应着Context结构的StartTimestamp属性。

 public class HostingApplication IHttpApplication<HostingApplication.Context>  {    public struct Context    {      public HttpContext   HttpContext { get; set; }      public IDisposable   Scope { get; set; }      public long     StartTimestamp { get; set; }    }  }

二、以当前请求作为日志范围

我们知道日志系统有一个叫做“日志范围”的概念,它的目的在于为多次相关的日志记录创建一个上下文范围,并为这个范围提供一个唯一标识,这个标识会作为日志内容的一部分被写入。当我们在进行日志分析的时候,可以根据日志范围标识将一组原本独立的日志关联起来。这个概念对于Web应用尤为重要,因为很多情况下我们所做的日志分析都是针对某一个请求,这就要求我们必须明确地分辨出被记录下来的日志隶属于哪一个请求,只有这样才能将针对同一请求的所有日志提取出来做综合的分析以得出一个准确的结论。

从上个实例最终写入的三条日志来看,它们并不携带当前请求的标识信息。但是这不是ASP.NET Core的问题,而是我们在调用LoggerFactory的扩展方法AddConsole注册ConsoleLoggerProvider的时候并未显式开启针对日志范围的支持。为了让注册的ConsoleLoggerProvider创建的Logger能够支持日志范围,我们只需按照如下的方式在调用AddConsole方法的时候添加一个额外的参数(true)即可。

 new WebHostBuilder()    .ConfigureLogging(factory=>factory.AddConsole(true))    .UseKestrel()    .UseStartup<Startup>()            .Build()    .Run();

我们再次请求应用并利用浏览器对目标地址发送两次请求,六条写入的日志将会以如下图所示的形式输出到控制台上。不同于上面的输出结果,本次输出的日志包含请求的ID(Request Id),在同一个请求下被记录下来的日志具有相同的ID。除了请求ID,记录的日志还携带了请求的路径(Request Path)。

日志范围携带的用于唯一标识当前请求的ID,同时也可以视为当前HttpContext的唯一标识,它对应着HttpContext的TranceIdentifier属性。对于DefaultHttpContext来说,针对这个属性的读写是借助一个名为HttpRequestIdentifierFeature的特性实现的,下面的代码提供了该对象对应的接口IHttpRequestIdentifierFeature和默认实现类HttpRequestIdentifierFeature的定义。

public abstract class HttpContext  {    //省略其他成员    public abstract string TraceIdentifier { get; set; }  }     public interface IHttpRequestIdentifierFeature  {    string TraceIdentifier { get; set; }  }    public class HttpRequestIdentifierFeature IHttpRequestIdentifierFeature  {    private string _id;    private static long _requestId = DateTime.UtcNow.Ticks;    private static unsafe string GenerateRequestId(long id);    public string TraceIdentifier    {      get      {        return _id??(id= GenerateRequestId(Interlocked.Increment(ref _requestId)));      }      set      {        this._id = value;      }    }  }

HttpRequestIdentifierFeature生成TraceIdentifier的逻辑很简单。如上面的代码片断所示,它具有一个静态长整型字段_requestId,其初始值为当前时间戳。对于某个具体的HttpRequestIdentifierFeature对象来说,它的TraceIdentifier属性的默认值返回的是这个字段_requestId加1之后转换的字符串。具体的转换逻辑定义在GenerateRequestId方法中,它会采用相应的算法 将指定的整数转换一个长度为13的字符串。

和开始请求处理的时间戳一样,被创建出来的日志范围实际被保存在HostingApplication的上下文对象中,它对应着Context结构的Scope属性。当HostingApplication创建这个Context对象的时候,它会从当前HttpContext中提取出请求的ID和路径,创建出这个日志范围并赋值给这个属性。整个请求的处理其实就在这个日志范围中进行,请求处理结束,当前日志范文也被回收释放。

 public class HostingApplication IHttpApplication<HostingApplication.Context>  {    public struct Context    {      public HttpContext   HttpContext { get; set; }      public IDisposable   Scope { get; set; }      public long      StartTimestamp { get; set; }    }  }

三、记录异常日志

由于ASP.NET Core在处理请求过程中导致的异常并不会导致应用终止,考虑到安全,抛出的异常的详细信息也不应该直接返回到客户端。所以在很多情况下我们根本感知不到应用发生了异常,即使感知到了,也不知道导致异常的根源在何处。在这种情况下,我们就需要使用记录的日志进行差错和纠错,因为ASP.NET Core在处理请求遇到的异常都会记录到日志中。

比如针对如下这段程序,毫无疑问它针对任何一个请求的处理都会抛出一个DivideByZeroException的异常。如果我们利用浏览器来访问站点地址,它只会得到一个状态为500的响应,并简单的提示服务端出现错误。对于宿主程序来说,我们根本就是感知不到任何异常发生。

 new WebHostBuilder()    .UseKestrel()    .Configure(app=>app.Run(async context=> {      int x = 1;      int y = 0;      await context.Response.WriteAsync((x / y).ToString());    }))    .Build()  .Run();

在这种情况下我们可以通过查看日志得到异常的详细信息,不过在这之前必须为LoggerFactory注册相应的LoggerProvider。如果我们采用控制台应用作为宿主,在开发或者调试的时候最简单的莫过于按照如下的方式注册一个ConsoleLoggerProvider让日志可以直接写入宿主程序的控制台。

 new WebHostBuilder()    .ConfigureLogging(factory=>factory.AddConsole (true))    .UseKestrel()    .Configure(app=>app.Run(async context=> {      int x = 1;      int y = 0;      await context.Response.WriteAsync((x / y).ToString());    }))    .Build()    .Run();

一旦为LoggerFactory注册了这么一个ConsoleLoggerProvider,对于服务端出现的未处理的任何异常,我们都可以直接在宿主控制台上看到错误的详细信息,下图就是上面这个例子抛出的DivideByZeroException异常的详细信息。

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

相关文章

photoshop图层样式制作漂亮的琥珀

photoshop图层样式制作漂亮的琥珀

立体字,图层,样式,漂亮,电脑软件,今天为大家分享photoshop制作琥珀文字方法,教程比较基础,适合新手来学习,推荐到,喜欢的朋友可以参考本文!最终效果图:1、先建一个800x800画布,填充背景颜色为黑色,输入文字(IOU)2、为文字图层添加图层样式3、在&ldquo…

word2013表格怎么设置跨页换行word

word2013表格怎么设置跨页换行word

设置,换行,方法,步骤,表格,  Word文档中Word表格如果一切按照默认设置,如果表格的单元格中有很多文字时并超出一个页面的大小时,Word表格是会自动跨页延伸到下一页的。这里小编教你word2013表格设置跨页换行的方法步骤,希望对你有帮助!word20…

ps怎么将图标背景透明化?

ps怎么将图标背景透明化?

图标,透明化,背景,电脑软件,ps,我们在日常的工作或者学习中,需要使用背景透明的图标,这样的图标能够更好地嵌入到页面或者代码中。很多我们从网上下载下来的图标,背景虽然看上去是白色透明的,但真正嵌入到窗口或者页面上之后,才发现背景不是透明…

AI怎么使用透视网格工具绘制三维?

AI怎么使用透视网格工具绘制三维?

工具,绘制,网格,透视,电脑软件,ai中绘制三维立体图形使用透视网格工具绘制就会简单很多,下面我们就来看看详细的教程。软件名称:Adobe Illustrator CS6 (AI cs6) 精简绿色中文版(32位+64位)软件大小:205MB更新时间:2014-05-111、首先,在 AI 中点…

ppt2007中文如何去除模板水印

ppt2007中文如何去除模板水印

模板,中文,方法,水印,电脑软件,  一般在网上下载的ppt模板都会有水印图片,我们在使用模板之前通常都需要将水印去除,那么,怎样在ppt中去除水印呢?下面就让小编告诉你ppt怎样去除模板水印 的方法,希望看完本教程的朋友都能学会并运用起来。ppt…

如何解决vue与传统jquery插件冲突

如何解决vue与传统jquery插件冲突

冲突,插件,如何解决,与传统,电脑软件,本篇文章主要介绍了如何解决vue与传统jquery插件冲突,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧比如基于jquery的select2插件,在vue下单独用有很多问题,其实对于这类插件,…

vue中配置mint-ui报css错误问题的

vue中配置mint-ui报css错误问题的

错误问题,解决方法,配置,电脑软件,vue,在vue2.0中引入了mint-ui后总是报一个css的错误 但是package.json中已经配置了css-loader style-loader ,webpack.config中也已经配置了css,还是报这个错误,相反,如果把webpack.config中css的配置注释掉就…

PS怎么设计一个圆圆可爱的冰球表情

PS怎么设计一个圆圆可爱的冰球表情

冰球,表情,可爱,电脑软件,PS,想要设计一个圆形的冰球表情,夏天看冰冰的东西比较有食欲,该怎么设计呢?下面我们就来看看详细的教程,很简单,需要的朋友可以参考下。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB更新时间:2015-11…

win10系统中CDRX6菜单变白色不显示

win10系统中CDRX6菜单变白色不显示

系统,显示,菜单,白色,电脑软件,相信很多同学安装了win10系统之后,你的 CDR 菜单栏就变了,变得没有任何内容,以白色显示,win10系统与CorelDRAW早期的版本兼容方面存在问题,(CorelDRAW x7/X8无此问题出现)。如果你还在使用CorelDRAW X6或更早版本的…

jquery+css实现简单的轮播效果

jquery+css实现简单的轮播效果

效果,简单,电脑软件,jquery,css,开发过程中需要用到图片轮播的插件,在网上找了几个插件之后还是决定自己码一个,比较简洁的功能,以后说不定还会有用。ps: 功能比较简单,整个框并不能根据图片的大小自动调节,这里所用的图片是1170*500的,如果需要改…

Word中进行2013版更改页面颜色的操

Word中进行2013版更改页面颜色的操

操作方法,颜色,页面,操作步骤,电脑软件,  word如何更改页面颜色是可以更改的,怎么该呢?今天,小编就教大家在Word中进行2013版更改页面颜色的操作方法。Word中进行2013版更改页面颜色的操作步骤打开word文档:选择设计菜单:找到页面颜色:选择你…

PHP从数组中删除元素的四种方法实

PHP从数组中删除元素的四种方法实

方法,删除元素,四种,组中,实例,茴香豆的“茴”字有四种写法,PHP从数组中删除元素也有四种方法 ^_^。删除一个元素,且保持原有索引不变使用 unset 函数,示例如下:<?php $array = array(0 => "a", 1 => "b", 2 => "c"); unset($array[1])…