|
| 1 | +using System.Net; |
| 2 | +using System.Text.Json; |
| 3 | +using Microsoft.AspNetCore.Diagnostics; |
| 4 | +using Microsoft.AspNetCore.Http; |
| 5 | +using Microsoft.Extensions.Hosting; |
| 6 | +using Microsoft.Extensions.Logging; |
| 7 | +using OpenShock.Common.Errors; |
| 8 | + |
| 9 | +namespace OpenShock.Common.ExceptionHandling; |
| 10 | + |
| 11 | +public sealed class OpenShockExceptionHandler : IExceptionHandler |
| 12 | +{ |
| 13 | + private readonly IHostEnvironment _env; |
| 14 | + private readonly ILogger _logger; |
| 15 | + private readonly JsonSerializerOptions _jsonSerializerOptions; |
| 16 | + |
| 17 | + public OpenShockExceptionHandler(IHostEnvironment env, ILoggerFactory loggerFactory, JsonSerializerOptions jsonSerializerOptions) |
| 18 | + { |
| 19 | + _env = env; |
| 20 | + _logger = loggerFactory.CreateLogger("RequestInfo"); |
| 21 | + _jsonSerializerOptions = jsonSerializerOptions; |
| 22 | + } |
| 23 | + |
| 24 | + public async ValueTask<bool> TryHandleAsync(HttpContext context, Exception exception, CancellationToken cancellationToken) |
| 25 | + { |
| 26 | + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; |
| 27 | + _logger.LogError(exception, "Unhandled exception for {Method} {Path}", context.Request.Method, context.Request.Path); |
| 28 | + |
| 29 | + if (_env.IsDevelopment()) |
| 30 | + { |
| 31 | + await PrintRequestInfo(context); |
| 32 | + } |
| 33 | + |
| 34 | + await ExceptionError.Exception.WriteAsJsonAsync(context, _jsonSerializerOptions, cancellationToken); |
| 35 | + return context.Response.HasStarted; |
| 36 | + } |
| 37 | + |
| 38 | + private async Task PrintRequestInfo(HttpContext context) |
| 39 | + { |
| 40 | + // Rewind our body reader, so we can read it again. |
| 41 | + context.Request.Body.Seek(0, SeekOrigin.Begin); |
| 42 | + // Used to read from the body stream. |
| 43 | + using var stream = new StreamReader(context.Request.Body); |
| 44 | + |
| 45 | + // Create Dictionaries to be logging in our RequestInfo object for both Header values and Query parameters. |
| 46 | + var headers = context.Request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()); |
| 47 | + var queryParams = context.Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString()); |
| 48 | + |
| 49 | + // Create our RequestInfo object. |
| 50 | + var requestInfo = new RequestInfo |
| 51 | + { |
| 52 | + Body = await stream.ReadToEndAsync(), |
| 53 | + Headers = headers, |
| 54 | + TraceId = context.TraceIdentifier, |
| 55 | + Method = context.Request.Method, |
| 56 | + Path = context.Request.Path.Value, |
| 57 | + Query = queryParams |
| 58 | + }; |
| 59 | + |
| 60 | + // Finally log this object on Information level. |
| 61 | + _logger.LogInformation("{@RequestInfo}", requestInfo); |
| 62 | + } |
| 63 | +} |
0 commit comments