ASP.NET Core 中文文档 第三章 原理(8)日志

ASP.NET Core 内建支持日志,也允许开发人员轻松切换为他们想用的其他日志框架。尽量用最少的代码来实现应用程序日志,只要做到这点,就能想在哪里加就能在那里加日志记录。

章节:

在应用程序中实现日志
ILoggerFactoryILoggerILoggerFactoryCreateLogger
var logger = loggerFactory.CreateLogger("Catchall Endpoint");
logger.LogInformation("No endpoint found for request {path}", context.Request.Path);
.在应用程序中配置日志dotnet runlocalhost:5000
info[Catchall Endpoint]
{path}
logger.LogInformation("No endpoint found for request {path}", context.Request.Path);

在实际的应用程序中,你会希望基于应用程序级别来添加日志,而不是基于框架级别或事件。例如,你创建了一个 Web API 应用程序来管理 To-Do 条目(参见 用 Visual Studio 和 ASP.NET Core MVC 创建首个 Web API ),你可能会为这些条目的各种操作添加日志记录。

[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger<TodoController> _logger; public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
} [HttpGet]
public IEnumerable<TodoItem> GetAll()
{
_logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all items");
EnsureItems();
return _todoRepository.GetAll();
}
_logger

使用 ILogger

ILoggerTTodoControllerILoggerILoggerFactoryILoggerFactory

日志记录级别

当应用程序添加一条日志记录时,必须指定 日志级别 。日志级别允许你控制应用程序输出日志的详细程度,以及把不同类型的日志传送给不同的日志记录器。比方说,你可能会希望调试消息在一个本地文件,而把错误消息记录到计算机的事件日志或数据库中。

ASP.NET Core 详尽地定义了六个日志级别,通过增加重要性或严重程度排序:

Credentials: {"User":"someuser", "Password":"P@ssword"}
Entering method Configure with flag set to true
VerboseRequest received for path /foo
Login failed for IP 127.0.0.1FileNotFoundException for file foo.txt
Cannot insert record due to duplicate key violation

Critical

当应用程序或系统崩溃、遇到灾难性故障,需要立即被关注时,应当记录关键级别的日志。举例:数据丢失、磁盘空间不够等。

LoggingLogLevelLogInformationLog(LogLevel.Information, ...)LogLevel

string data

记录消息。

EventId eventId

使用数字类型的 id 来标记日志,这样可以将一系列的事件彼此相互关联。被记录的事件 ID 应该是静态的、特定于指定类型时间的。比如,你可能会把添加商品到购物车的事件 ID 标记为 1000,然后把结单的事件 ID 标记为 1001,以便能智能过滤并处理这些日志记录。

string format

日志消息的格式字符串。

object[] args

用于格式化的一组对象。

Exception error

用于记录的异常实例。

EventIdintint
ConsoleLoggereventId
TodoControllerInformationWarning
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
_logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all items");
EnsureItems();
return _todoRepository.GetAll();
} [HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
{
_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {0}", id);
var item = _todoRepository.Find(id);
if (item == null)
{
_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({0}) NOT FOUND", id);
return NotFound();
}
return new ObjectResult(item);
}

注解

建议在应用程序及其 API 上执行应用程序日志记录,而不是在框架级别上记录。框架已经有了一个内建的能够简单通过设置启用相应日志级别的日志记录器了。

DebugTraceConfigureAddConsoleLogLevel.Trace

控制台记录器输出时使用前缀「dbug: 」,默认的框架没有追踪的记录器,每一个日志级别都有使用对应的四个字符的前缀,使得日志信息始终一致。

Log Level Prefix
Critical crit
Error fail
Warning warn
Information info
Debug dbug
Trace trce

作用域

IDisposableILogger.BeginScopeTraceSource

作用域不是必须的,而且需要谨慎使用。它们适合用于具有比较明显的开始和结束的操作,比如在一个事务中调用多个资源。

在应用程序中配置日志
StartupConfigureILoggerFactorydependency-injectionConfigureILoggerFactoryILoggerFactoryConfigureloggerFactory.AddConsoleMinimumLevelVerbose
    public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
ILoggerFactoryloggerFactory.AddConsole

注解

你可以选择配置日志记录,当设置 hosting 时,而不是在启动时。

ILoggerFactorylogLevel => logLevel >= LogLevel.Warning(category, loglevel) => category.Contains("MyController") && loglevel >= LogLevel.TraceEventLogSettingsFilterSourceSwitchTraceListener
FilterLoggerSettingsWithFilterILoggerFactoryILoggerFactoryILoggerFactory
loggerFactory
.WithFilter(new FilterLoggerSettings
{
{ "Microsoft", LogLevel.Warning },
{ "System", LogLevel.Warning },
{ "ToDoApi", LogLevel.Debug }
})
.AddConsole();

配置 TraceSource 日志

TraceSource
Microsoft.Extensions.Logging.TraceSourceTextWriterTraceListener
  "Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Filter": "1.0.0",
"Microsoft.Extensions.Logging.TraceSource": "1.0.0"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
TraceSourceLoggerWarningAddTraceSourceTraceListenerTextWriterTraceListener
// add Trace Source logging
var testSwitch = new SourceSwitch("sourceSwitch", "Logging Sample");
testSwitch.Level = SourceLevels.Warning;
loggerFactory.AddTraceSource(testSwitch,
new TextWriterTraceListener(writer: Console.Out));
sourceSwitchSourceLevels.WarningWarningTraceListener
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using TodoApi.Core;
using TodoApi.Core.Interfaces;
using TodoApi.Core.Model; namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger<TodoController> _logger; public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
} [HttpGet]
public IEnumerable<TodoItem> GetAll()
{
_logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all items");
EnsureItems();
return _todoRepository.GetAll();
} [HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
{
_logger.LogInformation(LoggingEvents.GET_ITEM, "Getting item {0}", id);
var item = _todoRepository.Find(id);
if (item == null)
{
_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "GetById({0}) NOT FOUND", id);
return NotFound();
}
return new ObjectResult(item);
} [HttpPost]
public IActionResult Create([FromBody] TodoItem item)
{
if (item == null)
{
return BadRequest();
}
_todoRepository.Add(item);
_logger.LogInformation(LoggingEvents.INSERT_ITEM, "Item {0} Created", item.Key);
return CreatedAtRoute("GetTodo", new { controller = "Todo", id = item.Key }, item);
} [HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] TodoItem item)
{
if (item == null || item.Key != id)
{
return BadRequest();
} var todo = _todoRepository.Find(id);
if (todo == null)
{
_logger.LogWarning(LoggingEvents.GET_ITEM_NOTFOUND, "Update({0}) NOT FOUND", id);
return NotFound();
} _todoRepository.Update(item);
_logger.LogInformation(LoggingEvents.UPDATE_ITEM, "Item {0} Updated", item.Key);
return new NoContentResult();
} [HttpDelete("{id}")]
public void Delete(string id)
{
_todoRepository.Remove(id);
_logger.LogInformation(LoggingEvents.DELETE_ITEM, "Item {0} Deleted", id);
} private void EnsureItems()
{
if (!_todoRepository.GetAll().Any())
{
_logger.LogInformation(LoggingEvents.GENERATE_ITEMS, "Generating sample items.");
for (int i = 1; i < 11; i++)
{
_todoRepository.Add(new TodoItem() { Name = "Item " + i });
}
}
}
}
}
http://localhost:5000/api/Todo/0
ConsoleLoggerTextWriterTraceListenerTextWriter

配置其它提供程序

ILoggerFactory

你也可以创建自己定制的提供程序来支持其他的日志框架或自己内部的日志需求。

日志记录建议

当你在 ASP.NET Core 应用程序中实现日志时可以参考以下有用建议:

LogLevelCreate
总结
Startup