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...");