[英]Natural Linking of Request and Response logs .NET Core app
I am trying to link my request and response logs by GUID.我正在尝试通过 GUID 链接我的请求和响应日志。 Either i get the same GUID for every request and response, or i get a completely different one fore every request and response.
要么我为每个请求和响应获得相同的 GUID,要么在每个请求和响应之前获得一个完全不同的 GUID。 What i need is for the linked Requests and responses to have the Same GUID, but be different to all other requests an responses.
我需要的是链接的请求和响应具有相同的 GUID,但与所有其他请求和响应不同。 Is it even possible to do this with my current structure?
甚至可以用我目前的结构来做到这一点吗?
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();
}
I'd extend request and response json objects and add there some ID fields and fill the with the same Guid:我会扩展请求和响应 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();
}
I am not sure if there's a specific principle you "link" your requests and responses, but assuming it's stock standard asp.net core app and you work within user request, I believe it is possible to inject scoped services into your middleware.我不确定您是否有特定的原则来“链接”您的请求和响应,但假设它是标准的 asp.net 核心应用程序并且您在用户请求中工作,我相信可以将范围服务注入您的中间件。 So, suppose you create a service with something these lines:
因此,假设您使用以下几行创建了一个服务:
class GuidProvider {
public string CorrelationId {get; private set;}
public GuidProvider() {
CorrelationId = Guid.NewGuid().ToString()
}
}
you'd register it in Startup
:你会在
Startup
注册它:
services.AddScoped(typeof(GuidProvider));
and inject into your middleware constructor:并注入您的中间件构造函数:
public RequestResponseLoggingMiddleware(..., GuidProvider guidProvider)
{
guidProvider.CorrelationId; // should stay the same within request
...
}```
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.