Come aggiungere il "corpo della richiesta" nell'output di serilog .net core?
Ho un'API web basata su .net core 3.1.
Sto usando la libreria SeriLog come logger.
Ecco la mia configurazione SeriLog. Serilog è stato configurato da "appsettings.json".
Devo aggiungere i parametri del "corpo della richiesta" all'output del log, se esiste. C'è un modo per configurarlo. Inoltre, condivido il mio output di log.
Risposte
Si prega di controllare questo thread e questo articolo . Per registrare le informazioni sulla richiesta e sulla risposta (come: corpo della richiesta), è possibile creare un middleware e acquisire il corpo della richiesta e della risposta (poiché sono flussi, è necessario prima leggerli, quindi utilizzare il metodo Serilogs per registrarli) .
Codice come di seguito:
public class SerilogRequestLogger
{
readonly RequestDelegate _next;
public SerilogRequestLogger(RequestDelegate next)
{
if (next == null) throw new ArgumentNullException(nameof(next));
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
// Push the user name into the log context so that it is included in all log entries
LogContext.PushProperty("UserName", httpContext.User.Identity.Name);
// Getting the request body is a little tricky because it's a stream
// So, we need to read the stream and then rewind it back to the beginning
string requestBody = "";
HttpRequestRewindExtensions.EnableBuffering(httpContext.Request);
Stream body = httpContext.Request.Body;
byte[] buffer = new byte[Convert.ToInt32(httpContext.Request.ContentLength)];
await httpContext.Request.Body.ReadAsync(buffer, 0, buffer.Length);
requestBody = Encoding.UTF8.GetString(buffer);
body.Seek(0, SeekOrigin.Begin);
httpContext.Request.Body = body;
Log.ForContext("RequestHeaders", httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true)
.ForContext("RequestBody", requestBody)
.Debug("Request information {RequestMethod} {RequestPath} information", httpContext.Request.Method, httpContext.Request.Path);
Log.Information(string.Format("Request Body: {0} ", requestBody));
// The reponse body is also a stream so we need to:
// - hold a reference to the original response body stream
// - re-point the response body to a new memory stream
// - read the response body after the request is handled into our memory stream
// - copy the response in the memory stream out to the original response stream
using (var responseBodyMemoryStream = new MemoryStream())
{
var originalResponseBodyReference = httpContext.Response.Body;
httpContext.Response.Body = responseBodyMemoryStream;
await _next(httpContext);
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
var responseBody = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
Log.ForContext("RequestBody", requestBody)
.ForContext("ResponseBody", responseBody)
.Debug("Response information {RequestMethod} {RequestPath} {statusCode}", httpContext.Request.Method, httpContext.Request.Path, httpContext.Response.StatusCode);
await responseBodyMemoryStream.CopyToAsync(originalResponseBodyReference);
}
}
}
Registra il middleware:
app.UseMiddleware<SerilogRequestLogger>();
Riferimento: utilizzo della registrazione Serilog per ASP.NET Core .
Dalla mia comprensione, vuoi aggiungere il HttpRequest Bodyal tuo registro.
Qualcosa di simile dovrebbe aiutarti a iniziare fintanto che è all'interno di un controller con un asyncmetodo, se non hai accesso a HttpRequestpuoi aggiungerne uno con i servizi DI. Addhttpcontextaccessor () nel tuo file di avvio
// Payload.
string payload = string.Empty;
// Create StreamReader And Starting Reading The Request Body.
using (StreamReader streamReader = new StreamReader(this.Request.Body, Encoding.UTF8, true, 1024, true))
{
// Assign The Stream Content To The Payload Object
payload = await streamReader.ReadToEndAsync();
}
// Check If The Payload Has Something.
if (!string.IsEmptyOrNull(payload))
{
// LOG INFO HERE
}
Ho scritto un middleware personalizzato per acquisire sia le richieste HTTP che le risposte. È compatibile con ASP.NET Core 3.X e dovrebbe funzionare anche con 2.X e .NET 5.0, anche se non l'ho testato con quelle versioni del framework.
Ecco il link al mio repository git: https://github.com/matthew-daddario/AspNetCoreRequestResponseLogger
Il codice rilevante è questo:
public class RequestResponseLoggerMiddleware
{
private readonly RequestDelegate _next;
private readonly bool _isRequestResponseLoggingEnabled;
public RequestResponseLoggerMiddleware(RequestDelegate next, IConfiguration config)
{
_next = next;
_isRequestResponseLoggingEnabled = config.GetValue<bool>("EnableRequestResponseLogging", false);
}
public async Task InvokeAsync(HttpContext httpContext)
{
// Middleware is enabled only when the EnableRequestResponseLogging config value is set.
if (_isRequestResponseLoggingEnabled)
{
Console.WriteLine($"HTTP request information:\n" + $"\tMethod: {httpContext.Request.Method}\n" +
$"\tPath: {httpContext.Request.Path}\n" + $"\tQueryString: {httpContext.Request.QueryString}\n" +
$"\tHeaders: {FormatHeaders(httpContext.Request.Headers)}\n" + $"\tSchema: {httpContext.Request.Scheme}\n" +
$"\tHost: {httpContext.Request.Host}\n" + $"\tBody: {await ReadBodyFromRequest(httpContext.Request)}");
// Temporarily replace the HttpResponseStream, which is a write-only stream, with a MemoryStream to capture it's value in-flight.
var originalResponseBody = httpContext.Response.Body;
using var newResponseBody = new MemoryStream();
httpContext.Response.Body = newResponseBody;
// Call the next middleware in the pipeline
await _next(httpContext);
newResponseBody.Seek(0, SeekOrigin.Begin);
var responseBodyText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
Console.WriteLine($"HTTP request information:\n" + $"\tStatusCode: {httpContext.Response.StatusCode}\n" +
$"\tContentType: {httpContext.Response.ContentType}\n" + $"\tHeaders: {FormatHeaders(httpContext.Response.Headers)}\n" +
$"\tBody: {responseBodyText}"); newResponseBody.Seek(0, SeekOrigin.Begin); await newResponseBody.CopyToAsync(originalResponseBody); } else { await _next(httpContext); } } private static string FormatHeaders(IHeaderDictionary headers) => string.Join(", ", headers.Select(kvp => $"{{{kvp.Key}: {string.Join(", ", kvp.Value)}}}"));
private static async Task<string> ReadBodyFromRequest(HttpRequest request)
{
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
request.EnableBuffering();
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
var requestBody = await streamReader.ReadToEndAsync();
// Reset the request's body stream position for next middleware in the pipeline.
request.Body.Position = 0;
return requestBody;
}
}