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中查看控制台输出,请使用项目配置文件运行应用程序并进行测试。

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

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

原文地址:https://www.cnblogs.com/bisslot/p/12331025.html

时间: 2024-10-14 12:16:12

ASP.NET Core 3中的自定义日志记录的相关文章

ASP.NET Core 集成测试中通过 Serilog 向控制台输出日志

日志是程序员的雷达,不仅在生产环境中需要,在集成测试环境中也需要,可以在持续集成失败后帮助定位问题.与生产环境不同,在集成测试环境中使用控制台输出日志更方便,这样可以通过持续集成 runner 执行 job 时的输出看到日志. 这篇博文简单记录一下我们在  asp.net core 集成测试中通过 serilog 向控制台输出日志的实现代码 var outputTemplate = "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {SourceContext}{Ne

通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成,前者负责监听请求并将接收的请求传递给给HttpApplication对象处理,后者则将请求处理任务委托给注册的中间件来完成.中间件的注册是通过ApplicationBuilder对象来完成的,所以我们先来了解一下这究竟是个怎样的对象.[本文已经同步到<ASP.NET Core框架揭秘>之中] [

asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

原文:asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionless的学习做下笔记! Exceptionless是什么?能做什么呢? “Exceptionless”这个词的定义是:没有异常.Exceptionless可以为您的ASP.NET.We

ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

.NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对Redis和SQL Server的原生支持.除了这个独立的缓存系统之外,ASP.NET Core还借助一个中间件实现了“响应缓存”,它会按照HTTP缓存规范对整个响应实施缓存.不过按照惯例,在对缓存进行系统介绍之前,我们还是先通过一些简单的实例演示感知一下如果在一个ASP.NET Core应用中如何

如何在ASP.NET Core应用中实现与第三方IoC/DI框架的整合?

我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合.对此比较了解的读者朋友应该很清楚,针对第三方DI框架的整合可以通过在定义Startup类型的ConfigureServices方法返回一个ServiceProvider来实现.但是真的有这么简单吗? 一.ConfigureServices方法返回的ServiceProvider貌似没有用!? 我们可以通过一个简单的

asp.net core mvc中如何把二级域名绑定到特定的控制器上

由于公司的工作安排,一直在研究其他技术,所以一直没时间更新博客,今天终于可以停下手头的事情,写一些新内容了. 应用场景:企业门户网站会根据内容不同,设置不同的板块,如新浪有体育,娱乐频道,等等.有的情况下需要给不同的板块设置不同的二级域名,如新浪体育sports.sina.com.cn. 在asp.net core mvc中,如果要实现板块的效果,可能会给不同的板块建立不同的控制器(当然也有其他的技术,这里不讨论实现方式的好坏),在这种情况下,如何给控制器绑定上独有的二级域名,比如体育频道对应的

007.Adding a view to an ASP.NET Core MVC app -- 【在asp.net core mvc中添加视图】

Adding a view to an ASP.NET Core MVC app 在asp.net core mvc中添加视图 2017-3-4 7 分钟阅读时长 本文内容 1.Changing views and layout pages 修改视图和布局页 2.Change the title and menu link in the layout file 在布局文件中修改标题与菜单 3.Passing Data from the Controller to the View 从控制器向视图

在 ASP.NET Core 项目中实现小写的路由URL

在 ASP.NET MVC 早期版本中,我们可以通过在应用的 RegisterRoutes 方法中设置 routes.LowercaseUrls = true ; 来将页面的 URL 链接转小写.在 ASP.NET Core MVC 中,路由的配置类似与下面的代码: app.UseMvc(configureRoutes => { configureRoutes.MapRoute("Default", "{controller=App}/{action=Index}/{i

如何在 ASP.NET Core 测试中操纵时间?

有时候,我们会遇到一些跟系统当前时间相关的需求,例如: 只有开学季才允许录入学生信息 只有到了晚上或者周六才允许备份博客 注册满 3 天的用户才允许进行一些操作 某用户在 24 小时内被禁止发言 很显然,要实现这些功能的代码多多少少要用到 DateTime.Now 这个静态属性,然而要使用单元测试或者集成测试对上述需求进行验证,往往需要采用一些曲线救国的方法甚至是直接跳过这些测试,这是因为在 .Net 中,DateTime.Now 通常难以被 Mock .这时候我就要夸一夸 Angular 的测