ASP.NET Core 3中的自定义日志记录

根据我的经验,通常在API中记录请求和响应。这样做可以帮助开发人员调试问题并提供有价值的性能指标。在本教程中,我将介绍如何为ASP.NET Core 3 Web API创建基本的日志记录解决方案。在这篇文章的结尾,我们将有一个有效的日志记录解决方案,它将记录每个请求以及对控制台和文件系统的响应,并且日志将包括API处理每个请求所花费的时间。以下是概述:

1. 先决条件
2. 创建RequestLog和ResponseLog模型
3. 创建ILogForWebAPI
4. 创建WebAPIConsoleLogger
5. 创建WebAPIFileLogger
6. 创建CustomLoggingMiddleware
7. 在启动中添加自定义日志记录,然后进行测试


先决条件

您应该熟悉 ASP.NET Core Web API请求管道。

首先,创建一个ASP.NET Core 3 Web API项目。

创建RequestLog和ResponseLog模型

这些类将携带我们要记录的请求和响应数据。

1  public class RequestLog
2     {
3         public Guid Id { get; set; }
4         public string Action { get; set; }
5         public string URL { get; set; }
6         public string IPAddress { get; set; }
7         public DateTime TimeStampUtc { get; set; }
8     }

 

1  public class ResponseLog
2 {
3         public Guid Id { get; set; }
4         public string Action { get; set; }
5         public string URL { get; set; }
6         public int StatusCode { get; set; }
7         public long ResponseTimeInMilliseconds { get; set; }
8         public DateTime TimeStampUtc { get; set; }
9 }

 

创建ILogForWebAPI

在这里,我们创建了可以执行两个操作的日志记录抽象—日志记录请求和日志记录响应。

1  public interface ILogForWebAPIs
2     {
3         Task LogAsync(RequestLog requestLog);
4         Task LogAsync(ResponseLog responseLog);
5     }

 

创建WebAPIConsoleLogger

遵循单一职责原则(SRP),我们将创建ILogForWebAPI的两个实现。WebAPIConsoleLogger将负责登录到控制台,而WebAPIFileLogger将负责登录到文件系统。我们可以使用Decorator Pattern在单个ILogForWebAPI实例中提供两个记录器的功能。每个ILogForWebAPIs实现都将包含ILogForWebAPIs的嵌套实例,如果该实例不为null,则将其调用。

 1  public class WebAPIConsoleLogger : ILogForWebAPIs
 2     {
 3         private readonly ILogForWebAPIs _nextLogger;
 4         private readonly string _dateTimeFormat = "hh:mm:ss tt";
 5 
 6         public WebAPIConsoleLogger(ILogForWebAPIs nextLogger = null)
 7         {
 8             _nextLogger = nextLogger;
 9         }
10 
11         public async Task LogAsync(RequestLog requestLog)
12         {
13             Console.WriteLine($"Request received from {requestLog.IPAddress} @ {requestLog.TimeStampUtc.ToString(_dateTimeFormat)} (Utc)");
14             Console.WriteLine($"{requestLog.Action} {requestLog.URL}");
15             Console.WriteLine();
16 
17             if (_nextLogger != null)
18             {
19                 await _nextLogger.LogAsync(requestLog);
20             }
21         }
22 
23         public async Task LogAsync(ResponseLog responseLog)
24         {
25             Console.WriteLine($"Response sent @ {responseLog.TimeStampUtc.ToString(_dateTimeFormat)} (Utc)");
26             Console.WriteLine($"{responseLog.StatusCode}: {responseLog.Action} {responseLog.URL}");
27             Console.WriteLine($"Response time: {responseLog.ResponseTimeInMilliseconds} ms");
28             Console.WriteLine();
29 
30             if (_nextLogger != null)
31             {
32                 await _nextLogger.LogAsync(responseLog);
33             }
34         }
35     }

 

创建WebAPIFileLogger

WebAPIFileLogger将序列化模型并将其ID用作文件名,从而为每个请求和响应创建一个json文件。

 1  public class WebAPIFileLogger : ILogForWebAPIs
 2     {
 3         private readonly string _requestDirectory;
 4         private readonly string _responseDirectory;
 5         private readonly ILogForWebAPIs _nextLogger;
 6 
 7         public WebAPIFileLogger(string path, ILogForWebAPIs nextLogger = null)
 8         {
 9             if (string.IsNullOrWhiteSpace(path))
10             {
11                 throw new ArgumentNullException(nameof(path));
12             }
13 
14             _requestDirectory = Path.Combine(path, "requests");
15             _responseDirectory = Path.Combine(path, "responses");
16 
17             if (!Directory.Exists(_requestDirectory))
18             {
19                 Directory.CreateDirectory(_requestDirectory);
20             }
21 
22             if (!Directory.Exists(_responseDirectory))
23             {
24                 Directory.CreateDirectory(_responseDirectory);
25             }
26 
27             _nextLogger = nextLogger;
28         }
29 
30         public async Task LogAsync(RequestLog requestLog)
31         {
32             var serializedLog = JsonConvert.SerializeObject(requestLog, Formatting.Indented);
33             var filePath = Path.Combine(_requestDirectory, $"{requestLog.Id}.json");
34             await File.WriteAllTextAsync(filePath, serializedLog);
35 
36             if (_nextLogger != null)
37             {
38                 await _nextLogger.LogAsync(requestLog);
39             }
40         }
41 
42         public async Task LogAsync(ResponseLog responseLog)
43         {
44             var serializedLog = JsonConvert.SerializeObject(responseLog, Formatting.Indented);
45             var filePath = Path.Combine(_responseDirectory, $"{responseLog.Id}.json");
46             await File.WriteAllTextAsync(filePath, serializedLog);
47 
48             if (_nextLogger != null)
49             {
50                 await _nextLogger.LogAsync(responseLog);
51             }
52         }
53     }

 

创建CustomLoggingMiddleware

CustomLoggingMiddleware需要将自身附加到请求管道,然后使用ApplicationServices提供的记录器记录请求,最后执行请求管道并记录响应。

 1  public static class CustomLoggingMiddleware
 2     {
 3         public static void UseCustomLogging(this IApplicationBuilder app)
 4         {
 5             app.Use(async (context, next) =>
 6             {
 7                 var logger = app.ApplicationServices.GetService<ILogForWebAPIs>();
 8 
 9                 if (logger is null)
10                 {
11                     throw new Exception($"Add ILogForWebAPIs to your service provider in {nameof(Startup)}.{nameof(Startup.ConfigureServices)}");
12                 }
13 
14                 await LogRequestAsync(context, logger);
15                 var stopWatch = new Stopwatch();
16                 stopWatch.Start();
17 
18                 // execute request pipeline
19                 await next?.Invoke();
20 
21                 stopWatch.Stop();
22 
23                 await LogResponseAsync(context, stopWatch.ElapsedMilliseconds, logger);
24             });
25         }
26 
27         private static async Task LogRequestAsync(HttpContext context, ILogForWebAPIs logger)
28         {
29             var requestLog = new RequestLog
30             {
31                 Id = Guid.NewGuid(),
32                 Action = context.Request.Method,
33                 URL = context.Request.Path,
34                 IPAddress = context.Request.HttpContext.Connection.RemoteIpAddress.ToString(),
35                 TimeStampUtc = DateTime.UtcNow
36             };
37 
38             await logger.LogAsync(requestLog);
39         }
40 
41         private static async Task LogResponseAsync(HttpContext context, long responseTimeInMilliseconds, ILogForWebAPIs logger)
42         {
43             var responseLog = new ResponseLog
44             {
45                 Id = Guid.NewGuid(),
46                 Action = context.Request.Method,
47                 URL = context.Request.Path,
48                 StatusCode = context.Response.StatusCode,
49                 ResponseTimeInMilliseconds = responseTimeInMilliseconds,
50                 TimeStampUtc = DateTime.UtcNow
51             };
52 
53             await logger.LogAsync(responseLog);
54         }
55     }

 

在启动中添加自定义日志记录,然后进行测试

要获取我们的API日志记录,我们只需要做两件事:

  1. 将记录器添加到Startup.ConfigureServices中的IServiceCollection中
  2. 在Startup.Configure中调用UseCustomLogging

注意:如果像下面的示例那样使用https重定向,建议将自定义日志记录添加到请求管道中。这样,您可以确保不记录重定向。

 

 1  public class Startup
 2     {
 3         public Startup(IConfiguration configuration)
 4         {
 5             Configuration = configuration;
 6         }
 7 
 8         public IConfiguration Configuration { get; }
 9 
10         // This method gets called by the runtime. Use this method to add services to the container.
11         public void ConfigureServices(IServiceCollection services)
12         {
13             services.AddControllers();
14             services.AddTransient<ILogForWebAPIs>((serviceProvider) => new WebAPIConsoleLogger(new WebAPIFileLogger("APILogs")));
15         }
16 
17         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
18         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
19         {
20             if (env.IsDevelopment())
21             {
22                 app.UseDeveloperExceptionPage();
23             }
24 
25             app.UseHttpsRedirection();
26 
27             app.UseCustomLogging();
28 
29             app.UseRouting();
30 
31             app.UseAuthorization();
32 
33             app.UseEndpoints(endpoints =>
34             {
35                 endpoints.MapControllers();
36             });
37         }
38     }

 

要在Visual Studio中查看控制台输出,请使用项目配置文件运行应用程序并进行测试。

5

6

导航到日志目录以检查日志文件

7

{
“ Id”:“ 0c7ffe14-66c3-428c-bffe-0da1dccd9546”,
“ Action”:“ GET”,
“ URL”:“ / weatherforecast”,
“ IPAddress”:“ :: 1”,
“ TimeStampUtc”:“ 2020 -02-13T15:05:27.3373827Z”
}

 
 

 

posted @ 2020-02-19 13:55  TonysDad  阅读(879)  评论(0编辑  收藏  举报