Skip to content

Instantly share code, notes, and snippets.

@djordjedjukic
Last active March 27, 2024 09:14
Show Gist options
  • Save djordjedjukic/c5ada10ff68f389b095d3bbc67a06d65 to your computer and use it in GitHub Desktop.
Save djordjedjukic/c5ada10ff68f389b095d3bbc67a06d65 to your computer and use it in GitHub Desktop.
LoggingMiddleware
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.AspNetCore.Http;
using MoveBoard.Core.Extensions;
using MoveBoard.Core.Infrastructure.Exceptions;
using Newtonsoft.Json;
namespace MoveBoard.Core.Infrastructure.Mediatr
{
internal class LoggingPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IHttpContextAccessor _ctx;
private readonly TelemetryClient _telemetry;
public LoggingPipelineBehavior(IHttpContextAccessor ctx, TelemetryClient telemetry)
{
_ctx = ctx;
_telemetry = telemetry;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
long start = 0;
long stop;
try
{
start = Stopwatch.GetTimestamp();
var result = await next();
stop = Stopwatch.GetTimestamp();
_telemetry.TrackTrace("MediatR", SeverityLevel.Information,
Serialize(request, result, GetElapsedMilliseconds(start, stop)));
return result;
}
catch (Exception e)
{
stop = Stopwatch.GetTimestamp();
HandleExceptionAsync(e, request, start, stop);
throw;
}
}
private void HandleExceptionAsync(Exception exception, TRequest request, long start, long stop)
{
var e = exception is AggregateException aggregateException
? aggregateException.Flatten().InnerExceptions.FirstOrDefault()
: exception;
switch (e)
{
case UnauthorizedAccessException uae:
_telemetry.TrackTrace("MediatR", Serialize(request, default, GetElapsedMilliseconds(start, stop)));
return;
case ValidatingException ve:
_telemetry.TrackTrace("MediatR",
Serialize(request, default, GetElapsedMilliseconds(start, stop), ve.Errors));
return;
}
_telemetry.TrackException(e, Serialize(request, default, GetElapsedMilliseconds(start, stop)));
}
private Dictionary<string, string> Serialize(TRequest request, TResponse result, double elapsedMs,
IEnumerable<ValidatingException.Error> errors = default)
{
var serializationSettings =
new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var properties = new Dictionary<string, string>
{
{ "Elapsed", $"{elapsedMs:0.0000}" },
{ "Type", request.GetType().FullName },
{ "Request", JsonConvert.SerializeObject(request, serializationSettings) },
{ "Response", JsonConvert.SerializeObject(result, serializationSettings) }
};
if (errors.IsNullOrEmpty() == false)
{
properties.Add("Errors", JsonConvert.SerializeObject(errors, serializationSettings));
}
return properties;
}
private double GetElapsedMilliseconds(long start, long stop)
{
return (stop - start) * 1000 / (double)Stopwatch.Frequency;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment