日志记录提供程序

  日志记录提供程序存储日志,但显示日志的 Console 提供程序除外。 例如,Azure Application Insights 提供程序将日志存储在 Azure Application Insights 中。 可以启用多个提供程序。

  默认 ASP.NET Core Web 应用模板调用 WebApplication.CreateBuilder,该操作将添加以下日志记录提供程序:

  • Console
  • Debug
  • EventSource
  • EventLog (仅限Windows)

  下面的代码将重写由 WebApplication.CreateBuilder 添加的一组默认的日志记录提供程序:

1
2
3
4
5
var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole();

1
2
3
4
5
6
var builder = WebApplication.CreateBuilder();
builder.Host.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
});

创建日志

  若要创建日志,请使用依赖项注入 (DI) 中的 ILogger对象。

  如下示例中:

  • 创建一个记录器 ILogger<AboutModel>,该记录器使用类型为 AboutModel 的完全限定名称的日志类别。 日志类别是与每个日志关联的字符串。
  • 调用 LogInformation 以在 Information 级别登录。 日志“级别”代表所记录事件的严重程度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AboutModel : PageModel
{
private readonly ILogger _logger;

public AboutModel(ILogger<AboutModel> logger)
{
_logger = logger;
}

public void OnGet()
{
_logger.LogInformation("About page visited at {DT}",
DateTime.UtcNow.ToLongTimeString());
}
}

配置日志记录

  日志记录配置通常由 appsettings.{ENVIRONMENT}.json 文件的 Logging 部分提供,其中 {ENVIRONMENT} 占位符是环境。 以下 appsettings.Development.json 文件由 ASP.NET Core Web 应用模板生成:

1
2
3
4
5
6
7
8
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

  在上述JSON中:

  • 指定了 “Default” 和 “Microsoft.AspNetCore” 类别。
  • “Microsoft.AspNetCore” 类别适用于以 “Microsoft.AspNetCore” 开头的所有类别。 例如,此设置适用于 “Microsoft.AspNetCore.Routing.EndpointMiddleware” 类别。
  • “Microsoft.AspNetCore” 类别在日志级别 Warning 或更高级别记录。
  • 未指定特定的日志提供程序,因此 LogLevel 适用于所有启用的日志记录提供程序,但 Windows EventLog 除外。

  Logging 属性可以具有 LogLevel 和日志提供程序属性。 LogLevel 指定要针对所选类别进行记录的最低级别。 在前面的 JSON 中,指定了 Information 和 Warning 日志级别。 LogLevel 表示日志的严重性,范围为 0 到 6:

  • Trace = 0
  • Debug = 1
  • Information = 2
  • Warning = 3
  • Error = 4
  • Critical = 5
  • None = 6

  提供程序属性可以指定 LogLevel 属性。 提供程序下的 LogLevel 指定要为该提供程序记录的级别,并替代非提供程序日志设置。 请考虑以下 appsettings.json 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"Logging": {
"LogLevel": { // All providers, LogLevel applies to all the enabled providers.
"Default": "Error", // Default logging, Error and higher.
"Microsoft": "Warning" // All Microsoft* categories, Warning and higher.
},
"Debug": { // Debug provider.
"LogLevel": {
"Default": "Information", // Overrides preceding LogLevel:Default setting.
"Microsoft.Hosting": "Trace" // Debug:Microsoft.Hosting category.
}
},
"EventSource": { // EventSource provider
"LogLevel": {
"Default": "Warning" // All categories of EventSource provider.
}
}
}
}

  Logging.{PROVIDER NAME}.LogLevel 中的设置会覆盖 Logging.LogLevel 中的设置,其中 *{PROVIDER NAME}*占位符是提供程序名称。

Program.cs中的日志

  以下示例调用 Program.cs 中的 Builder.WebApplication.Logger 并记录信息性消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var builder = WebApplication.CreateBuilder(args);

// 可禁用日志输出的颜色
builder.Logging.AddSimpleConsole(i => i.ColorBehavior = LoggerColorBehavior.Disabled);

var app = builder.Build();

app.Logger.LogInformation("Adding Routes");

app.MapGet("/", () => "Hello World!");

app.Logger.LogInformation("Starting the app");

app.MapGet("/Test", async (ILogger<Program> logger, HttpResponse response) =>
{
logger.LogInformation("Testing logging in Program.cs");
await response.WriteAsync("Testing");
});

app.Run();

不同方式设置日志级别

  日志级别可以由任何配置提供程序设置。

  所有平台上的环境变量分层键都不支持 : 分隔符。 __(双下划线):

  • 受所有平台支持。 例如,Bash 不支持 : 分隔符,但支持 __
  • 自动替换为 :

命令行方式

1
2
set Logging__LogLevel__Microsoft=Information
dotnet run
  • 在 Windows 上,将环境密钥 Logging:LogLevel:Microsoft 设置为值 Information。
  • 使用通过 ASP.NET Core Web 应用模板创建的应用时,请测试设置。 使用 set 之后,必须在项目目录中运行 dotnet run 命令。

环境变量

1
setx Logging__Console__LogLevel__Microsoft.Hosting.Lifetime Trace /M

  与 set 不同,setx 设置是持久的。 /M 开关在系统环境中设置变量。 如果未使用 /M,则设置用户环境变量。

  等价于以下配置:

1
2
3
4
5
6
7
"Logging": {
"Console": {
"LogLevel": {
"Microsoft.Hosting.Lifetime": "Trace"
}
}
}

日志级别

  下表列出了 LogLevel 值、方便的 Log{LogLevel} 扩展方法以及建议的用法:

LogLevel “值” 方法 说明
Trace 0 LogTrace 包含最详细的消息。 这些消息可能包含敏感的应用数据。 这些消息默认情况下处于禁用状态,并且不应在生产中启用。
Debug 1 LogDebug 用于调试和开发。 由于量大,请在生产中小心使用。
Information 2 LogInformation 跟踪应用的常规流。 可能具有长期值。
Warning 3 LogWarning 对于异常事件或意外事件。 通常包括不会导致应用失败的错误或情况。
Error 4 LogError 表示无法处理的错误和异常。 这些消息表示当前操作或请求失败,而不是整个应用失败。
Critical 5 LogCritical 需要立即关注的失败。 例如数据丢失、磁盘空间不足。
None 6 指定日志记录类别不应写入消息。

  在上表中,LogLevel 按严重性由低到高的顺序列出。

  Log 方法的第一个参数 LogLevel 指示日志的严重性。 大多数开发人员调用 Log{LOG LEVEL} 扩展方法,而不调用 Log(LogLevel, …),其中 {LOG LEVEL} 占位符是日志级别。 例如,以下两个日志记录调用功能相同,并生成相同的日志:

1
2
3
4
5
6
7
8
9
10
11
[HttpGet]
public IActionResult Test1(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);

_logger.Log(LogLevel.Information, MyLogEvents.TestItem, routeInfo);

_logger.LogInformation(MyLogEvents.TestItem, routeInfo);

return ControllerContext.MyDisplayRouteInfo();
}

日志事件ID

  每个日志都可指定一个事件 ID 。 示例应用使用 MyLogEvents 类来定义事件 ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyLogEvents
{
public const int GenerateItems = 1000;
public const int ListItems = 1001;
public const int GetItem = 1002;
public const int InsertItem = 1003;
public const int UpdateItem = 1004;
public const int DeleteItem = 1005;

public const int TestItem = 3000;

public const int GetItemNotFound = 4000;
public const int UpdateItemNotFound = 4001;
}

  事件 ID 与一组事件相关联。 例如,与在页面上显示项列表相关的所有日志可能是 1001。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

var todoItem = await _context.TodoItems.FindAsync(id);

if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}

return ItemToDTO(todoItem);
}

  日志记录提供程序可将事件 ID 存储在 ID 字段中,存储在日志记录消息中,或者不进行存储。 调试提供程序不显示事件 ID。 控制台提供程序在类别后的括号中显示事件 ID:

1
2
3
4
info: TodoApi.Controllers.TodoItemsController[1002]
Getting item 1
warn: TodoApi.Controllers.TodoItemsController[4000]
Get(1) NOT FOUND

日志消息模板

  每个日志 API 都使用一个消息模板。 消息模板可包含要填写参数的占位符。 请在占位符中使用名称而不是数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

var todoItem = await _context.TodoItems.FindAsync(id);

if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound, "Get({Id}) NOT FOUND", id);
return NotFound();
}

return ItemToDTO(todoItem);
}

  参数的顺序(而不是它们的占位符名称)决定了用于在日志消息中提供占位符值的参数。 在以下代码中,消息模板的占位符中的参数名称失序:

1
2
3
4
5
var apples = 1;
var pears = 2;
var bananas = 3;

_logger.LogInformation("Parameters: {Pears}, {Bananas}, {Apples}", apples, pears, bananas);

记录异常

  记录器方法的重载采用异常参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[HttpGet("{id}")]
public IActionResult TestExp(int id)
{
var routeInfo = ControllerContext.ToCtxString(id);
_logger.LogInformation(MyLogEvents.TestItem, routeInfo);

try
{
if (id == 3)
{
throw new Exception("Test exception");
}
}
catch (Exception ex)
{
// 记录异常
_logger.LogWarning(MyLogEvents.GetItemNotFound, ex, "TestExp({Id})", id);
return NotFound();
}

return ControllerContext.MyDisplayRouteInfo();
}

默认日志级别

  如果未设置默认日志级别,则默认的日志级别值为 Information。

  例如,考虑以下 Web 应用:

  • 使用 ASP.NET Web 应用模板创建的应用。
  • 已删除或重命名 appsettings.json 和 appsettings.Development.json。

  如果未在配置中设置默认日志级别,以下代码会设置默认日志级别:

1
2
var builder = WebApplication.CreateBuilder();
builder.Logging.SetMinimumLevel(LogLevel.Warning);

筛选器函数

  对配置或代码没有向其分配规则的所有提供程序和类别调用筛选器函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var builder = WebApplication.CreateBuilder();
builder.Logging.AddFilter((provider, category, logLevel) =>
{
if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Controller")
&& logLevel >= LogLevel.Information)
{
return true;
}
else if (provider.Contains("ConsoleLoggerProvider")
&& category.Contains("Microsoft")
&& logLevel >= LogLevel.Information)
{
return true;
}
else
{
return false;
}
});

ASP.NETCORE类别

  下表包含 ASP.NET Core 使用的一些类别。

类别 说明
Microsoft.AspNetCore 常规 ASP.NET Core 诊断。
Microsoft.AspNetCore.DataProtection 考虑、找到并使用了哪些密钥。
Microsoft.AspNetCore.HostFiltering 所允许的主机。
Microsoft.AspNetCore.Hosting HTTP 请求完成的时间和启动时间。 加载了哪些承载启动程序集。
Microsoft.AspNetCore.Mvc MVC 和 Razor 诊断。 模型绑定、筛选器执行、视图编译和操作选择。
Microsoft.AspNetCore.Routing 路由匹配信息。
Microsoft.AspNetCore.Server 连接启动、停止和保持活动响应。 HTTP 证书信息。
Microsoft.AspNetCore.StaticFiles 提供的文件。

  若要在控制台窗口中查看更多类别,请将 appsettings.Development.json 设置为以下各项:

1
2
3
4
5
6
7
8
9
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Trace",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

日志作用域

  “作用域”可对一组逻辑操作分组 。 此分组可用于将相同的数据附加到作为集合的一部分而创建的每个日志。 例如,在处理事务期间创建的每个日志都可包括事务 ID。

  范围:

  • 是 BeginScope 方法返回的 IDisposable 类型。
  • 持续到处置完毕。

  以下提供程序支持范围:

  • Console
  • AzureAppServicesFile 和 AzureAppServicesBlob要使用作用域,请在 using 块中包装记录器调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
TodoItem todoItem;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(MyLogEvents.GetItem, "Getting item {Id}", id);

todoItem = await _context.TodoItems.FindAsync(id);

if (todoItem == null)
{
_logger.LogWarning(MyLogEvents.GetItemNotFound,
"Get({Id}) NOT FOUND", id);
return NotFound();
}
}

return ItemToDTO(todoItem);
}

  日志作用域的作用:

  1. 关联多个日志条目 :作用域使得一组日志条目可以被视为一个整体操作的组成部分。例如,当处理一个 HTTP 请求时,可能会记录多个日志条目,作用域可以将这些条目关联到同一个请求。
  2. 上下文信息传播 :作用域允许将与某个操作相关的上下文信息(如 TransactionId、用户ID等)自动添加到所有相关的日志中,这样每条日志信息都会包含该上下文信息,而不需要每次都显式传递。
  3. 提高日志的可读性和可跟踪性 :作用域中的日志条目都共享相同的上下文信息,因此在分析日志时,能够快速确定哪些日志条目是关联的,帮助你更快地找到问题或了解请求的处理流程。

HTTP日志记录

  HTTP 日志记录是一种中间件,用于记录传入 HTTP 请求和 HTTP 响应的相关信息。 HTTP 日志记录可以记录:

  • HTTP 请求信息
  • 公共属性
  • 标头
  • 正文
  • HTTP 响应信息

  HTTP 日志记录可以:

  • 记录所有请求和响应,或者仅记录满足特定条件的请求和响应。
  • 选择要记录请求和响应的哪些部分。
  • 支持修订日志中的敏感信息。

  HTTP 日志记录可能会降低应用的性能,尤其是在记录请求和响应正文时。 在选择要记录的字段时请考虑性能影响。 测试所选日志记录属性的性能影响。

HTTP 日志记录可能会记录个人身份信息 (PII)。 请考虑风险,并避免记录敏感信息。

启用HTTP日志记录

  HTTP 日志记录通过调用 AddHttpLoggingUseHttpLogging 来调用,如以下示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var builder = WebApplication.CreateBuilder(args);

// 添加HTTP日志记录服务
builder.Services.AddHttpLogging(o => { });

var app = builder.Build();

// 使用HTTP日志记录服务
app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

  上述调用 AddHttpLogging 的示例中的空 Lambda 添加具有默认配置的中间件。默认情况下,HTTP 日志记录会记录路径、状态代码、请求头和响应头等常用属性。

  将以下行添加到 “LogLevel”: {}级别的 appsettings.Development.json 文件,以便显示 HTTP 日志:

1
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

  如果使用默认配置,请求和响应将记录为一对消息,与以下示例类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
Request:
Protocol: HTTP/2
Method: GET
Scheme: https
PathBase:
Path: /
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Host: localhost:52941
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: [Redacted]
sec-ch-ua: [Redacted]
sec-ch-ua-mobile: [Redacted]
sec-ch-ua-platform: [Redacted]
sec-fetch-site: [Redacted]
sec-fetch-mode: [Redacted]
sec-fetch-user: [Redacted]
sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
Response:
StatusCode: 200
Content-Type: text/plain; charset=utf-8
Date: Tue, 24 Oct 2023 02:03:53 GMT
Server: Kestrel

HTTP日志记录选项

  若要配置 HTTP 日志记录中间件的全局选项,请调用 Program.cs 中的 AddHttpLogging,使用 Lambda 来配置 HttpLoggingOptions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
// 用于配置要记录的请求和响应的特定部分
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add("sec-ch-ua");
logging.ResponseHeaders.Add("MyResponseHeader");
// 用于特定媒体类型的编码的配置
logging.MediaTypeOptions.AddText("application/javascript");
// 请求体和响应体大小限制
logging.RequestBodyLogLimit = 4096;
logging.ResponseBodyLogLimit = 4096;
// 将请求和响应所有已启用的日志合并到一个日志中。 这包括请求、请求正文、响应、响应正文和持续时间。
logging.CombineLogs = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}

app.UseStaticFiles();

app.UseHttpLogging();

app.Use(async (context, next) =>
{
context.Response.Headers["MyResponseHeader"] =
new string[] { "My Response Header Value" };

await next();
});

app.MapGet("/", () => "Hello World!");

app.Run();

在上述示例和下面的示例中,UseHttpLogging 在 UseStaticFiles 之后调用,因此没有为静态文件启用 HTTP 日志记录。 要启用静态文件 HTTP 日志记录,请在 UseStaticFiles 之前调用 UseHttpLogging。

IHttpLoggingInterceptor

  IHttpLoggingInterceptor 是服务的接口,可实现它来处理每请求和每响应回调,从而自定义记录的详细信息。 首先应用任何特定于终结点的日志设置,然后可在这些回调中替代它们。 实现可以:

  • 检查请求或响应。
  • 启用或禁用任何 HttpLoggingFields。
  • 调整记录的请求或响应正文量。
  • 将自定义字段添加到日志。

  通过调用 Program.cs 中的 AddHttpLoggingInterceptor<T> 来注册 IHttpLoggingInterceptor 实现。 如果注册了多个 IHttpLoggingInterceptor 实例,会按注册的顺序运行这些实例。

1
2
3
4
5
6
7
8
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.Duration;
});

builder.Services.AddHttpLoggingInterceptor<SampleHttpLoggingInterceptor>();
  • 检查请求方法并禁用 POST 请求的日志记录。
  • 对于非 POST 请求:
    • 修订请求路径、请求头和响应头。
    • 将自定义字段和字段值添加到请求和响应日志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using Microsoft.AspNetCore.HttpLogging;

namespace HttpLoggingSample;

internal sealed class SampleHttpLoggingInterceptor : IHttpLoggingInterceptor
{
public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
{
if (logContext.HttpContext.Request.Method == "POST")
{
// Don't log anything if the request is a POST.
logContext.LoggingFields = HttpLoggingFields.None;
}

// Don't enrich if we're not going to log any part of the request.
if (!logContext.IsAnyEnabled(HttpLoggingFields.Request))
{
return default;
}

if (logContext.TryDisable(HttpLoggingFields.RequestPath))
{
RedactPath(logContext);
}

if (logContext.TryDisable(HttpLoggingFields.RequestHeaders))
{
RedactRequestHeaders(logContext);
}

EnrichRequest(logContext);

return default;
}

public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext)
{
// Don't enrich if we're not going to log any part of the response
if (!logContext.IsAnyEnabled(HttpLoggingFields.Response))
{
return default;
}

if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders))
{
RedactResponseHeaders(logContext);
}

EnrichResponse(logContext);

return default;
}

private void RedactPath(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter(nameof(logContext.HttpContext.Request.Path), "RedactedPath");
}

private void RedactRequestHeaders(HttpLoggingInterceptorContext logContext)
{
foreach (var header in logContext.HttpContext.Request.Headers)
{
logContext.AddParameter(header.Key, "RedactedHeader");
}
}

private void EnrichRequest(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter("RequestEnrichment", "Stuff");
}

private void RedactResponseHeaders(HttpLoggingInterceptorContext logContext)
{
foreach (var header in logContext.HttpContext.Response.Headers)
{
logContext.AddParameter(header.Key, "RedactedHeader");
}
}

private void EnrichResponse(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter("ResponseEnrichment", "Stuff");
}
}

  使用此侦听器时,即使 HTTP 日志记录配置为记录 HttpLoggingFields.All,POST 请求也不会生成任何日志。

日志记录配置优先级顺序

以下列表显示了日志记录配置的优先级顺序:

  1. HttpLoggingOptions 中的全局配置,通过调用 AddHttpLogging 来设置。
  2. [HttpLogging] 属性或 WithHttpLogging 扩展方法中特定于终结点的配置会替代全局配置。
  3. IHttpLoggingInterceptor 使用结果进行调用,并且可进一步修改每个请求的配置。

W3C记录器

  W3CLogger 是一个以 W3C 标准格式写入日志文件的中间件。 相关日志包含有关 HTTP 请求和 HTTP 响应的信息。 W3CLogger 提供以下内容的日志:

  • HTTP 请求信息
  • 公共属性
  • 标头
  • HTTP 响应信息
  • 有关请求/响应对的元数据(开始日期/时间,所用时间)

  在以下几种方案中,W3CLogger 很有价值:

  • 记录传入请求和响应的相关信息。
  • 筛选请求和响应的哪些部分被记录。
  • 筛选要记录的头。

  W3CLogger 可能会降低应用的性能。 在选择要记录的字段时考虑性能影响 - 记录的属性越多,性能降低越多。 测试所选日志记录属性的性能影响。

W3CLogger 可能会记录个人身份信息 (PII)。 请考虑风险,并避免记录敏感信息。 默认情况下,不会记录可能包含 PII 的字段。

启用W3CLogger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var builder = WebApplication.CreateBuilder(args);

// 添加W3CLogger服务
// builder.Services.AddW3CLogging(logging=>
// {

// });

var app = builder.Build();

// 使用W3CLogger服务
app.UseW3CLogging();

app.UseRouting();

app.Run();

  默认情况下,W3CLogger 记录路径、状态代码、日期、时间和协议等常见属性。 有关单个请求/响应对的所有信息都写入同一行。

1
2
3
4
5
6
#Version: 1.0
#Start-Date: 2021-09-29 22:18:28
#Fields: date time c-ip s-computername s-ip s-port cs-method cs-uri-stem cs-uri-query sc-status time-taken cs-version cs-host cs(User-Agent) cs(Referer)
2021-09-29 22:18:28 ::1 DESKTOP-LH3TLTA ::1 5000 GET / - 200 59.9171 HTTP/1.1 localhost:5000 Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/93.0.4577.82+Safari/537.36 -
2021-09-29 22:18:28 ::1 DESKTOP-LH3TLTA ::1 5000 GET / - 200 0.1802 HTTP/1.1 localhost:5000 Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/93.0.4577.82+Safari/537.36 -
2021-09-29 22:18:30 ::1 DESKTOP-LH3TLTA ::1 5000 GET / - 200 0.0966 HTTP/1.1 localhost:5000 Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/93.0.4577.82+Safari/537.36 -

配置W3CLogger选项

  若要配置 W3CLogger 中间件,请在 Program.cs 中调用 AddW3CLogging:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddW3CLogging(logging =>
{
// Log all W3C fields
logging.LoggingFields = W3CLoggingFields.All;

logging.AdditionalRequestHeaders.Add("x-forwarded-for");
logging.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
logging.FileSizeLimit = 5 * 1024 * 1024;
logging.RetainedFileCountLimit = 2;
logging.FileName = "MyLogFile";
logging.LogDirectory = @"C:\logs";
logging.FlushInterval = TimeSpan.FromSeconds(2);
});