wtf*_*512 9 c# log4net asp.net-web-api .net-core asp.net-core
我想自动记录每个请求.在之前的.Net Framwork WebAPI项目中,我曾经注册了一个delegateHandler来执行此操作.
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new AutoLogDelegateHandler());
}
Run Code Online (Sandbox Code Playgroud)
AutoLogDelegateHandler.cs
public class AutoLogDelegateHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var requestBody = request.Content.ReadAsStringAsync().Result;
return await base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
HttpResponseMessage response = task.Result;
//Log use log4net
_LogHandle(request, requestBody, response);
return response;
});
}
}
Run Code Online (Sandbox Code Playgroud)
日志内容的示例:
------------------------------------------------------
2017-08-02 19:34:58,840
uri: /emp/register
body: {
"timeStamp": 1481013427,
"id": "0322654451",
"type": "t3",
"remark": "system auto reg"
}
response: {"msg":"c556f652fc52f94af081a130dc627433","success":"true"}
------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
但是在.NET Core WebAPI项目中,WebApiConfig
Global.asax中没有或没有注册函数GlobalConfiguration.Configure(WebApiConfig.Register);
那么有没有办法在.NET Core WebAPI中实现这一目标?
Set*_*Set 19
ActionFilter
将工作,直到您只需要记录由MVC中间件处理的请求(作为控制器操作).
如果需要记录所有传入请求,则需要使用中间件方法.
好的视觉解释:
请注意,中间件顺序很重要,如果您的日志记录应该在管道执行开始时完成,那么您的中间件应该是第一个.
来自docs的简单示例:
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do loging
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
Run Code Online (Sandbox Code Playgroud)
您可以创建自己的过滤器属性...
public class InterceptionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var x = "This is my custom line of code I need executed before any of the controller actions, for example log stuff";
base.OnActionExecuting(actionContext);
}
}
Run Code Online (Sandbox Code Playgroud)
...并且你会将它注册到GlobalFilters,但是既然你说你正在使用.NET Core,那么你就可以尝试继续...
您可以通过将其添加到收藏MvcOptions.Filters在启动类的ConfigureServices方法注册全局过滤器(所有控制器和动作):
让我们知道它是否有效.
PS这是一个关于使用WebAPI拦截请求的完整教程,以防有人需要更多细节.
演示:
AutologArribute.cs(新文件)
/// <summary>
/// <see cref="https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#Dependency injection"/>
/// </summary>
public class AutoLogAttribute : TypeFilterAttribute
{
public AutoLogAttribute() : base(typeof(AutoLogActionFilterImpl))
{
}
private class AutoLogActionFilterImpl : IActionFilter
{
private readonly ILogger _logger;
public AutoLogActionFilterImpl(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<AutoLogAttribute>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
// perform some business logic work
}
public void OnActionExecuted(ActionExecutedContext context)
{
//TODO: log body content and response as well
_logger.LogDebug($"path: {context.HttpContext.Request.Path}");
}
}
}
Run Code Online (Sandbox Code Playgroud)
启动文件
public void ConfigureServices(IServiceCollection services)
{
//....
// https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#filter-scopes-and-order-of-execution
services.AddMvc(opts=> {
opts.Filters.Add(new AutoLogAttribute());
});
//....
}
Run Code Online (Sandbox Code Playgroud)
对于想要快速且(非常)肮脏的解决方案用于调试目的的人(适用于 .Net Core 3),这里是这个答案的扩展,这就是你所需要的......
app.Use(async (context, next) =>
{
var initialBody = context.Request.Body;
using (var bodyReader = new StreamReader(context.Request.Body))
{
string body = await bodyReader.ReadToEndAsync();
Console.WriteLine(body);
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
await next.Invoke();
context.Request.Body = initialBody;
}
});
Run Code Online (Sandbox Code Playgroud)
这是 .NET Core 2.2 Web API 的完整日志组件。它将记录请求和响应,包括标头和正文。只要确保您有一个“Logs”文件夹即可。
AutoLogMiddleWare.cs(新文件)
public class AutoLogMiddleWare
{
private readonly RequestDelegate _next;
public AutoLogMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
string route = context.Request.Path.Value;
string httpStatus = "0";
// Log Request
var originalRequestBody = context.Request.Body;
originalRequestBody.Seek(0, SeekOrigin.Begin);
string requestBody = new StreamReader(originalRequestBody).ReadToEnd();
originalRequestBody.Seek(0, SeekOrigin.Begin);
// Log Response
string responseBody = string.Empty;
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream.CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
httpStatus = context.Response.StatusCode.ToString();
}
// Clean route
string cleanRoute = route;
foreach (var c in Path.GetInvalidFileNameChars())
{
cleanRoute = cleanRoute.Replace(c, '-');
}
StringBuilder sbRequestHeaders = new StringBuilder();
foreach (var item in context.Request.Headers)
{
sbRequestHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
}
StringBuilder sbResponseHeaders = new StringBuilder();
foreach (var item in context.Response.Headers)
{
sbResponseHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
}
string filename = DateTime.Now.ToString("yyyyMMdd.HHmmss.fff") + "_" + httpStatus + "_" + cleanRoute + ".log";
StringBuilder sbLog = new StringBuilder();
sbLog.AppendLine("Status: " + httpStatus + " - Route: " + route);
sbLog.AppendLine("=============");
sbLog.AppendLine("Request Headers:");
sbLog.AppendLine(sbRequestHeaders.ToString());
sbLog.AppendLine("=============");
sbLog.AppendLine("Request Body:");
sbLog.AppendLine(requestBody);
sbLog.AppendLine("=============");
sbLog.AppendLine("Response Headers:");
sbLog.AppendLine(sbResponseHeaders.ToString());
sbLog.AppendLine("=============");
sbLog.AppendLine("Response Body:");
sbLog.AppendLine(responseBody);
sbLog.AppendLine("=============");
var path = Directory.GetCurrentDirectory();
string filepath = ($"{path}\\Logs\\{filename}");
File.WriteAllText(filepath, sbLog.ToString());
}
catch (Exception ex)
{
// It cannot cause errors no matter what
}
}
}
public class EnableRequestRewindMiddleware
{
private readonly RequestDelegate _next;
public EnableRequestRewindMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Request.EnableRewind();
await _next(context);
}
}
public static class EnableRequestRewindExtension
{
public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
{
return builder.UseMiddleware<EnableRequestRewindMiddleware>();
}
}
Run Code Online (Sandbox Code Playgroud)
Startup.cs(现有文件)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMapper mapper, ILoggerFactory loggerFactory)
{
bool isLogEnabled = true; // Replace it by some setting, Idk
if(isLogEnabled)
app.UseEnableRequestRewind(); // Add this first
(...)
if(isLogEnabled)
app.UseMiddleware<AutoLogMiddleWare>(); // Add this just above UseMvc()
app.UseMvc();
}
Run Code Online (Sandbox Code Playgroud)
从 ASP.NET Core 6 开始,您可以使用默认中间件来实现此类行为(源代码):
app.UseHttpLogging();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
10271 次 |
最近记录: |