---
layout:     post
title:      "ASP.NET Core基本原理(7)-日志"
subtitle:   "ASP.NET Core Fundamentals-Logging"
tags: [netcore, fundamentals, logging]
---

ASP.NET Core内置了对日志的支持。通过依赖注入请求`ILoggerFactory`或者`ILogger<T>`可以为应用程序添加日志功能。

## 在应用程序中实现日志

如果请求了`ILoggerFactory`,那么日志记录器(Logger)就必须通过它的`CreateLogger`方法来创建：

```csharp
var logger = loggerFactory.CreateLogger("Catch all Endpoint");
logger.LogInformation("No endpoint found for request {path}", context.Request.Path);
```

当一个Logger被创建时，必须提供一个类别名称。类别名称指定了日志事件的根源。

![](http://charlie-blog.oss-cn-shanghai.aliyuncs.com/netcore-fundamentals-logging-1.png)

你可以会发现每次你通过浏览器发起网络请求都会产生多条记录，因为浏览器在尝试加载一个页面的时候会发起多个请求。在控制台记录器显示的日志级别（`info`,`fail`等），类别(`[Catch all Endpoint]`)，最后是日志消息。

在真实的应用中，你会希望基于应用程序级别添加日志，而不是基于框架级别或者事件。例如，之前创建的应用[创建ASP.NET Core Web API](http://zhuchenglin.me/my-first-web-api-on-mac)。我们来要为其中的各种操作添加日志记录。

API的逻辑被包含在`UserController`中，在它的构造函数通过依赖注入的方式来请求它所需要的服务。理想情况下，类应当像这个例子一样使用它们的构造函数来[显式地定义它们的依赖项](http://deviq.com/explicit-dependencies-principle/)并作为参数传入，而不是请求一个*ILoggerFactory*并显式创建`ILogger`实例。

在`UserController`中演示了另一种在应用中使用Logger的方式 - 通过请求*ILogger\<T>*(其中*T*是请求Logger的类)。

```csharp
[Route("api/[controller]")]
public class UserController : Controller
{
    //public IUserRepository UserItems { get; set; }
    private readonly IUserRepository _userRepository;
    private readonly ILogger<UserController> _logger;

    public UserController(IUserRepository userRepository,ILogger<UserController> logger)
    {
        _userRepository = userRepository;
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<UserItem> GetAll()
    {
        _logger.LogInformation("LoggingEvents.LIST_ITEMS","Listing all items");
        return _userRepository.GetAll();
    }
}
```

在每个控制器动作方法内，通过本地字段*_logger*来记录日志。这种技术并仅限于控制器内，通过依赖注入它能应用于应用程序内的所有服务中。

## 使用ILogger

如您所见，应用程序可以在类的构造函数中通过请求到`ILogger<T>`的实例，其中`T`是执行日志记录的类型。`UserController`就演示了这个方法。当使用这种技术时，Logger会自动使用该类型的名称作为其类别的名称。通过请求`ILogger<T>`的实例，类自己不需要通过`ILoggerFactory`来创建一个Logger实例。这种方法可以用在任何不需要用到`ILoggerFactory`所提供的额外功能的任何地方。

## 日志级别

当添加一个日志语句到的应用程序时，你必须指定一个`LogLevel`。日志级别(LogLevel)可以控制应用程序的日志记录输出的详细程序，以及管理不同类型的日志消息到不同的日志记录器的能力。你可能希望记录调试信息到一个本地文件，且把错误消息记录到计算机的事件日志或者数据库中。

ASP.NET Core定义了日志的6种级别，按重要性和严重程序排序如下：

* Trace

用于定义最详细的日志消息，通常只对开发人员调试一个问题有价值。这些信息可能包含敏感的应用程序数据，因此不应该用于生产环境。*默认为禁用*。比如：`Credentials: {"User":"someuser", "Password":"P@ssword"}`

* Debug

这种信息在开发阶段的短期内比较有用。它包含了一些可能对调试来说有用的信息，但没有长期的价值。默认情况下这是最详细的日志。比如：`Entering method Configure with flag set to true`

* Information

这种信息用于跟踪应用程序的一般流程。与`Debug`级别的信息相反，这些日志应该有一定的长期价值，比如：`Request received for path /foo`。

* Warning

这种信息用于程序流中的异常和意外事件。可能包含错误或者其它不会导致程序停止但可能要在日后调查的流程。比如：`Login failed for IP 127.0.0.1`或者`FileNotFoundException for file foo.txt`

* Error

当应用程序流程由于某些没有被处理到的异常等停止则需要记录错误日志。这些信息应当指定当前活动或者操作(比如当前的HTTP请求)，而不是应用程序范围的故障。比如：`Cannot insert record due to duplicate key violation`

* Critical

这种信息用于不可恢复的应用程序错误或系统错误，或者需要立即被关注的灾难性错误。比如:数据丢失、磁盘空间不够等。

`Logging`包为每一个`LogLevel`值提供了Helper扩展方法，允许你去调用。例如，`LogInformation`，而不是更详细的`Log(LogLevel.Information, ...)`。每个`LogLevel`- 扩展方法有多个重载，允许你传递下面的一些或者所有的参数：

* string data

记录信息

* EventId eventId

使用数字类型的id来标记日志，这样可将一系列的日志事件彼此相互关联。事件ID应该是静态的，并且特定于正在记录的特定类型的事件。例如，你可能把添加商品到购物车的事件ID标记为1000,然后把完成一次购买的事件ID标记为1001。这样允许你智能过滤并处理日志记录。`EventId`类型可以隐式转换成`int`，所以，你可以传递一个`int`参数。

* string format

日志信息的格式字符串

* object[] args

用于格式化的一组对象。

* Exception error

用于记录的异常实例。

## 在应用程序中配置日志

为了在ASP.NET Core应用程序中配置日志，你必须在`Startup`类的`Configure`方法中解析`ILoggerFactory`。ASP.NET Core会使用依赖注入自动提供一个`ILoggerFactory`实例。

```csharp
public void Configure(IApplicationBuilder app,
    IHostingEnvironment env,
    ILoggerFactory loggerFactory)
```

一旦你添加一个`ILoggerFactory`作为参数，你就可以在`Configure`方法中通过在Logger Factory上调用方法来配置了一个日志记录器(Logger)。比如调用`loggerFactory.AddConsole`添加控制台日志记录。

## 原文链接

[Logging](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging)

## 个人博客

[我的个人博客](http://zhuchenglin.me/)
