c# - 请求和响应日志的自然链接 .NET Core 应用
问题描述
我正在尝试通过 GUID 链接我的请求和响应日志。要么我为每个请求和响应获得相同的 GUID,要么我为每个请求和响应获得一个完全不同的 GUID。我需要的是链接的请求和响应具有相同的 GUID,但与所有其他请求和响应不同。甚至可以用我目前的结构做到这一点吗?
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
public RequestResponseLoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<RequestResponseLoggingMiddleware>();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
await LogRequest(context);
await LogResponse(context);
}
private async Task LogRequest(HttpContext context)
{
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
var Request = new Dictionary<string, string>(){
{"Http Request Information", $"{DateTime.UtcNow}"},
{"ID", Guid.NewGuid().ToString()},
{"IP", $"{context.Request.HttpContext.Connection.RemoteIpAddress}" },
{"Schema", context.Request.Scheme},
{"Path", context.Request.Path},
{"QueryString", $"{context.Request.QueryString}"},
{"Request Body", ReadStreamInChunks(requestStream)}
};
var requestJson = JsonConvert.SerializeObject(Request, Formatting.None);
_logger.LogInformation(requestJson);
context.Request.Body.Position = 0;
}
private async Task LogResponse(HttpContext context)
{
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(context.Response.Body).ReadToEndAsync();
context.Response.Body.Seek(0, SeekOrigin.Begin);
var Response = new Dictionary<string, string>(){
{"Http Response Information", $"{DateTime.UtcNow}"},
{"ID", Guid.NewGuid().ToString()},
{"IP", $"{context.Request.HttpContext.Connection.RemoteIpAddress}" },
{"Schema", context.Request.Scheme},
{"Path", context.Request.Path },
{"QueryString", $"{context.Request.QueryString}"},
{"Response Body", text}
};
var responseJson = JsonConvert.SerializeObject(Response, Formatting.None);
_logger.LogInformation(responseJson);
await responseBody.CopyToAsync(originalBodyStream);
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
解决方案
我会扩展请求和响应 json 对象并在那里添加一些 ID 字段并使用相同的 Guid 填充:
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
public RequestResponseLoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<RequestResponseLoggingMiddleware>();
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
}
public async Task Invoke(HttpContext context)
{
var guid = Guid.NewGuid();
await LogRequest(context, guid);
await LogResponse(context, guid);
}
private async Task LogRequest(HttpContext context, Guid guid)
{
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
var Request = new Dictionary<string, string>(){
{"Request ID", guid.ToString()}
{"Http Request Information", $"{DateTime.UtcNow}"},
{"ID", Guid.NewGuid().ToString()},
{"IP", $"{context.Request.HttpContext.Connection.RemoteIpAddress}" },
{"Schema", context.Request.Scheme},
{"Path", context.Request.Path},
{"QueryString", $"{context.Request.QueryString}"},
{"Request Body", ReadStreamInChunks(requestStream)}
};
var requestJson = JsonConvert.SerializeObject(Request, Formatting.None);
_logger.LogInformation(requestJson);
context.Request.Body.Position = 0;
}
private async Task LogResponse(HttpContext context, Guid guid)
{
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(context.Response.Body).ReadToEndAsync();
context.Response.Body.Seek(0, SeekOrigin.Begin);
var Response = new Dictionary<string, string>(){
{"Request ID", guid.ToString()}
{"Http Response Information", $"{DateTime.UtcNow}"},
{"ID", Guid.NewGuid().ToString()},
{"IP", $"{context.Request.HttpContext.Connection.RemoteIpAddress}" },
{"Schema", context.Request.Scheme},
{"Path", context.Request.Path },
{"QueryString", $"{context.Request.QueryString}"},
{"Response Body", text}
};
var responseJson = JsonConvert.SerializeObject(Response, Formatting.None);
_logger.LogInformation(responseJson);
await responseBody.CopyToAsync(originalBodyStream);
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
推荐阅读
- c# - 更改对象类后无法反序列化 XML 文件
- cuda - 为 cuda 进行矢量化,一个将复数作为输入并以复数作为输出的函数在 numba 中失败
- python - 即使在使变量为 false 之后,Python bot 也不会停止 while 循环
- java - 如何在我的 Android Studio 项目中添加具有坐标搜索功能的 Google Maps API?
- css - 水平显示可滚动的列表
- reactjs - React Formik 中未选中复选框
- python - UnicodeEncodeError:“ascii”编解码器无法对位置 0-4 中的字符进行编码:序数不是范围(128)
- azure-data-factory - Azure 数据工厂将 Zip 文件上传到 SFTP
- java - 构造函数 Candidate(String, int[]) 是未定义的错误
- android - java.util.Objects#requireNonNull 需要 api 级别 19 错误