using AipGateway.API.Application; using AipGateway.API.Domain.IRepositories.IGenericRepositories; using AipGateway.API.Domain.Models; using AipGateway.API.Domain.Models.Request; using AipGateway.API.Domain.Models.Response; using AipGateway.API.Services.Interfaces; using AipGateway.Messaging.Models; using Microsoft.AspNetCore.Http.Features; using Newtonsoft.Json; using System.Diagnostics; using AipGateway.API.Utils; namespace AipGateway.API.Middlewares { public class RequestResponseLogging { private readonly ILogger _log; private readonly RequestDelegate _next; public RequestResponseLogging(ILogger log, RequestDelegate next) { _next = next ?? throw new ArgumentNullException(nameof(next)); _log = log; _next = next; } public async Task Invoke(HttpContext httpContext, IAipDbLoggingService aipDbLoggingService) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); string requestUrl = httpContext.Request.Path; string? remoteIpAddr = httpContext.Connection.RemoteIpAddress?.ToString(); try { if (!requestUrl.Contains(GlobalConstants.API_PREFIX)) { await _next(httpContext); } else { string fileExt = ""; string dispName = ""; long fileSize = 0; var start = Stopwatch.GetTimestamp(); string guid = Guid.NewGuid().ToString(); ApiCallLog apiLog = new ApiCallLog { ApiGuid = guid, RequestAt = DateTime.Now, ApiEndPoint = httpContext.Request.Path, IPAddress = httpContext.Connection.RemoteIpAddress?.ToString(), ErrorMessage = "", }; httpContext.Items[GlobalConstants.API_GUID] = guid; httpContext.Items[GlobalConstants.API_START_TM] = DateTime.Now; await _next(httpContext); var statusCode = httpContext.Response.StatusCode; apiLog.ResponseStatusCode = statusCode; apiLog.ErrorCode = GlobalConstants.getAttributeInt(httpContext, GlobalConstants.API_RESULT_CODE); apiLog.ErrorMessage = GlobalConstants.getAttributeStr(httpContext, GlobalConstants.API_RESULT_MESSAGE); apiLog.ResponseAt = DateTime.Now; TimeSpan timeDiff = apiLog.ResponseAt - apiLog.RequestAt; apiLog.ResponseTime = timeDiff.Milliseconds; apiLog.TimeStamp = DateTime.Now; aipDbLoggingService.Send(apiLog); if (httpContext.Request.Method != HttpMethod.Post.ToString()) { return; } int apiId = GlobalConstants.GetApiId(httpContext); if ((apiId >= GlobalConstants.API_FILE_INFO && apiId <= GlobalConstants.API_FILE_DELETE_LABEL_PROTECTION) || (apiId >= GlobalConstants.API_STREAM_INFO && apiId <= GlobalConstants.API_STREAM_DELETE_LABEL_PROTECTION)) { FileJobLog fileJobLog = new FileJobLog { FileId = guid, FileName = "", FileExt = "", FileOwner = "", FileLabelGuid = "", FileProtectionGuid = "", FileSize = 0, NewFileName = string.Empty, NewFileExt = string.Empty, NewFileOwner = string.Empty, NewFileLabelGuid = string.Empty, NewFileProtectionGuid = string.Empty, NewFileSize = 0, ApiGuid = guid, ApiId = apiId, ServerIpAddr = remoteIpAddr, JobOwner = string.Empty, ApiKey = string.Empty, DecryptKey = string.Empty, JobResult = 0, JobMessage = string.Empty, JobTime = apiLog.ResponseTime, }; if (httpContext.Items[GlobalConstants.API_REQUEST] is RequestBase req) { fileJobLog.ApiKey = req.apiKey; fileJobLog.DecryptKey = req.decryptKey; fileJobLog.JobOwner = req.email; } if (httpContext.Items[GlobalConstants.API_RESULT] is ResponseBase res) { dispName = res.dispFileName; fileJobLog.JobResult = res.errorCode; fileJobLog.JobMessage = res.errorMessage; fileJobLog.FileName = res.dispFileName; fileJobLog.FileOwner = res.FileOwner; fileJobLog.FileExt = Path.GetExtension(res.dispFileName); fileJobLog.FileSize = res.FileSize; fileJobLog.FileLabelGuid = res.FileLabelGuid; fileJobLog.FileProtectionGuid = res.FileProtectionGuid; fileJobLog.NewFileName = res.NewFileName; fileJobLog.NewFileOwner = res.NewFileOwner; fileJobLog.NewFileExt = Path.GetExtension(res.NewFileName); fileJobLog.NewFileSize = res.NewFileSize; fileJobLog.NewFileLabelGuid = res.NewFileLabelGuid; fileJobLog.NewFileProtectionGuid = res.NewFileProtectionGuid; } fileExt = fileJobLog.FileExt; fileSize = fileJobLog.FileSize; fileJobLog.TimeStamp = DateTime.Now; aipDbLoggingService.Send(fileJobLog); long apiElapsed = TimeUtils.GetElapsedMilliseconds(start, Stopwatch.GetTimestamp()); if (httpContext.Items[GlobalConstants.API_START_TM] is DateTime startTime) { TimeSpan apiTimeDiff = DateTime.Now - startTime; if (apiTimeDiff.Milliseconds > apiElapsed) { apiElapsed = apiTimeDiff.Milliseconds; } } double fileSizekb = Math.Round((double)(fileSize / 1024), 2); if (apiElapsed > 1500) { _log.LogWarning("API Processing: {0}, {1,6} ms. {2,7} KB. {3,-6}, {4}", requestUrl, apiElapsed.ToString("#,##0"), fileSizekb.ToString("#,###,##0"), fileExt, dispName); } else { _log.LogInformation("API Processing: {0}, {1,6} ms. {2,7} KB. {3,-6}, {4}", requestUrl, apiElapsed.ToString("#,##0"), fileSizekb.ToString("#,###,##0"), fileExt, dispName); } } } } catch (Exception e) { _log.LogError($"Invoke Exception: {httpContext.Request.Method}, {httpContext.Request.Path}: Excepton Error: {e}"); throw; } } class BenchmarkToken : IDisposable { private readonly Stopwatch _stopwatch; public BenchmarkToken(Stopwatch stopwatch) { _stopwatch = stopwatch; _stopwatch.Start(); } public void Dispose() => _stopwatch.Stop(); } string GetPath(HttpContext httpContext, bool includeQueryInRequestPath) { /* In some cases, like when running integration tests with WebApplicationFactory the Path returns an empty string instead of null, in that case we can't use ?? as fallback. */ var requestPath = includeQueryInRequestPath ? httpContext.Features.Get()?.RawTarget : httpContext.Features.Get()?.Path; if (string.IsNullOrEmpty(requestPath)) { requestPath = httpContext.Request.Path.ToString(); } return requestPath!; } private static async Task FormatRequest(HttpContext context) { context.Request.EnableBuffering(); var buffer = new byte[Convert.ToInt32(context.Request.ContentLength)]; await context.Request.Body.ReadAsync(buffer); //var bodyAsText = Encoding.UTF8.GetString(buffer); context.Request.Body.Position = 0; var logDto = new ApiCallLog { RequestAt = DateTime.Now, ApiEndPoint = context.Request.Path, IPAddress = context.Connection.RemoteIpAddress?.ToString(), ErrorMessage = "", }; return logDto; } private static async Task FormatResponse(HttpResponse response) { response.Body.Seek(0, SeekOrigin.Begin); string text = await new StreamReader(response.Body).ReadToEndAsync(); response.Body.Seek(0, SeekOrigin.Begin); return text; } } }