From 8c5081d44290f1ca5724abbbfa6264fd4592ee31 Mon Sep 17 00:00:00 2001 From: xRain Date: Sat, 11 Jul 2026 16:15:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20SimApiRequestLogMiddleware?= =?UTF-8?q?=20=E8=AF=B7=E6=B1=82=E6=97=A5=E5=BF=97=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE=20ShowFu?= =?UTF-8?q?llHeader=20/=20ShowFullResponse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Configurations/SimApiOptions.cs | 13 +++ Configurations/SimApiRequestLogOptions.cs | 14 +++ Middlewares/SimApiRequestLogMiddleware.cs | 105 ++++++++++++++++++++++ SimApiExtensions.cs | 7 ++ 4 files changed, 139 insertions(+) create mode 100644 Configurations/SimApiRequestLogOptions.cs create mode 100644 Middlewares/SimApiRequestLogMiddleware.cs diff --git a/Configurations/SimApiOptions.cs b/Configurations/SimApiOptions.cs index b7611aa..c3641bd 100644 --- a/Configurations/SimApiOptions.cs +++ b/Configurations/SimApiOptions.cs @@ -56,6 +56,12 @@ public class SimApiOptions /// public bool EnableSimApiResponseFilter { get; set; } = true; + /// + /// 启用请求日志中间件 + /// default: false + /// + public bool EnableRequestLog { get; set; } + /// /// 开启ForwardHeaders,开启后可以透传负载均衡的Headers /// default: true @@ -104,6 +110,8 @@ public class SimApiOptions public SimApiRouteOptions SimApiRouteOptions { get; set; } = new(); + public SimApiRequestLogOptions SimApiRequestLogOptions { get; set; } = new(); + public void ConfigureSimApiRoute(Action? options = null) { options?.Invoke(SimApiRouteOptions); @@ -143,4 +151,9 @@ public class SimApiOptions { options?.Invoke(SimApiAuthCenterOptions); } + + public void ConfigureSimApiRequestLog(Action? options = null) + { + options?.Invoke(SimApiRequestLogOptions); + } } \ No newline at end of file diff --git a/Configurations/SimApiRequestLogOptions.cs b/Configurations/SimApiRequestLogOptions.cs new file mode 100644 index 0000000..b2916f7 --- /dev/null +++ b/Configurations/SimApiRequestLogOptions.cs @@ -0,0 +1,14 @@ +namespace SimApi.Configurations; + +public class SimApiRequestLogOptions +{ + /// + /// 是否打印完整的请求Header + /// + public bool ShowFullHeader { get; set; } + + /// + /// 是否打印完整的响应体 + /// + public bool ShowFullResponse { get; set; } +} diff --git a/Middlewares/SimApiRequestLogMiddleware.cs b/Middlewares/SimApiRequestLogMiddleware.cs new file mode 100644 index 0000000..fed2769 --- /dev/null +++ b/Middlewares/SimApiRequestLogMiddleware.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.ExceptionServices; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using SimApi.Configurations; + +namespace SimApi.Middlewares; + +/// +/// 请求日志中间件 +/// +public class SimApiRequestLogMiddleware( + RequestDelegate next, + ILogger log, + SimApiRequestLogOptions options) +{ + public async Task InvokeAsync(HttpContext context) + { + var sw = Stopwatch.StartNew(); + var fullUrl = + $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}"; + + var logMessage = new StringBuilder(); + logMessage.AppendLine($"[{context.Request.Method}] {fullUrl}"); + + if (options.ShowFullHeader) + { + logMessage.AppendLine("*( RequestHeaders [Full] ) =>"); + var headersDict = new Dictionary(); + foreach (var header in context.Request.Headers) + { + headersDict[header.Key] = header.Value.ToString(); + } + + logMessage.AppendLine(JsonSerializer.Serialize(headersDict)); + } + else + { + logMessage.AppendLine("*( RequestHeaders ) =>"); + var token = context.Request.Headers["Token"].FirstOrDefault() ?? ""; + var queryId = context.Request.Headers["Query-Id"].FirstOrDefault() ?? ""; + logMessage.AppendLine($"Token: {token} QueryId: {queryId}"); + } + + context.Request.EnableBuffering(); + var requestBodyText = await new StreamReader(context.Request.Body).ReadToEndAsync(); + context.Request.Body.Seek(0, SeekOrigin.Begin); + logMessage.AppendLine("*( RequestBody ) =>"); + logMessage.AppendLine(requestBodyText); + + var originalBodyStream = context.Response.Body; + using var responseBody = new MemoryStream(); + context.Response.Body = responseBody; + + ExceptionDispatchInfo? edi = null; + + try + { + await next(context); + } + catch (Exception ex) + { + edi = ExceptionDispatchInfo.Capture(ex); + } + finally + { + sw.Stop(); + + responseBody.Seek(0, SeekOrigin.Begin); + var responseText = await new StreamReader(responseBody).ReadToEndAsync(); + + logMessage.AppendLine($"*( Response [{context.Response.StatusCode}] ) =>"); + if (options.ShowFullResponse) + { + logMessage.Append(responseText); + } + else + { + var truncated = responseText.Length > 200 ? responseText[..200] : responseText; + logMessage.Append(truncated); + } + + if (edi != null) + { + logMessage.Append(Environment.NewLine); + logMessage.Append($"Exception: {edi.SourceException}"); + } + + responseBody.Seek(0, SeekOrigin.Begin); + await responseBody.CopyToAsync(originalBodyStream); + context.Response.Body = originalBodyStream; + + log.LogInformation(logMessage.ToString()); + } + + edi?.Throw(); + } +} diff --git a/SimApiExtensions.cs b/SimApiExtensions.cs index aad2d5c..70613e7 100644 --- a/SimApiExtensions.cs +++ b/SimApiExtensions.cs @@ -339,6 +339,7 @@ public static class SimApiExtensions } } + builder.AddSingleton(simApiOptions.SimApiRequestLogOptions); builder.AddSingleton(simApiOptions); return builder; } @@ -497,6 +498,12 @@ public static class SimApiExtensions }); } + if (options.EnableRequestLog) + { + logger.LogInformation("开始配置SimApiRequestLog..."); + builder.UseMiddleware(); + } + if (options.EnableSimApiException) { logger.LogInformation("开始配置SimApiException...");