Skip to content

Instantly share code, notes, and snippets.

@meziantou
Created June 20, 2022 03:19
Show Gist options
  • Save meziantou/30b602d846589b3148b4c6e82dfd487d to your computer and use it in GitHub Desktop.
Save meziantou/30b602d846589b3148b4c6e82dfd487d to your computer and use it in GitHub Desktop.
<HttpRequestLoggerComponent />
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
using System.Diagnostics;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace HttpRequestLogger
{
public sealed class HttpRequestLoggerComponent : ComponentBase, IDisposable
{
[Inject]
public IJSRuntime? JS { get; set; }
protected override void OnInitialized()
{
HttpLoggerService.RequestStarted += HttpLoggerService_RequestStarted;
HttpLoggerService.RequestStopped += HttpLoggerService_RequestStopped;
}
private void HttpLoggerService_RequestStarted(object? sender, HttpRequestMessage e)
{
if (JS != null)
{
_ = InvokeAsync(async () => await JS.InvokeVoidAsync("console.time", e.RequestUri));
}
}
private void HttpLoggerService_RequestStopped(object? sender, HttpResponseMessage e)
{
if (JS != null && e.RequestMessage != null)
{
_ = InvokeAsync(async () => await JS.InvokeVoidAsync("console.timeEnd", e.RequestMessage.RequestUri));
}
}
public void Dispose()
{
HttpLoggerService.RequestStarted -= HttpLoggerService_RequestStarted;
HttpLoggerService.RequestStopped -= HttpLoggerService_RequestStopped;
}
}
internal static class HttpLoggerService
{
static HttpRequestsObserver _observer;
static IDisposable? _listener;
public static event EventHandler<HttpRequestMessage>? RequestStarted;
public static event EventHandler<HttpResponseMessage>? RequestStopped;
private static void RaiseRequestStarted(Guid id, HttpRequestMessage message)
{
RequestStarted?.Invoke(null, message);
}
private static void RaiseRequestStopped(Guid id, HttpResponseMessage message)
{
RequestStopped?.Invoke(null, message);
}
static HttpLoggerService()
{
_observer = new HttpRequestsObserver();
_listener = DiagnosticListener.AllListeners.Subscribe(_observer);
}
private sealed class HttpRequestsObserver : IDisposable, IObserver<DiagnosticListener>
{
private IDisposable? _subscription;
public void OnNext(DiagnosticListener value)
{
if (value.Name == "HttpHandlerDiagnosticListener")
{
_subscription = value.Subscribe(new HttpHandlerDiagnosticListener());
}
}
public void OnCompleted() { }
public void OnError(Exception error) { }
public void Dispose()
{
_subscription?.Dispose();
}
private sealed class HttpHandlerDiagnosticListener : IObserver<KeyValuePair<string, object?>>
{
private static readonly AsyncLocal<Guid> _currentMessage = new();
private static readonly Func<object, HttpRequestMessage> RequestAccessor = CreateGetRequest();
private static readonly Func<object, HttpResponseMessage> ResponseAccessor = CreateGetResponse();
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(KeyValuePair<string, object?> value)
{
if (value.Key == "System.Net.Http.HttpRequestOut.Start")
{
var request = RequestAccessor(value.Value!);
var id = Guid.NewGuid();
_currentMessage.Value = id;
RaiseRequestStarted(id, request);
}
else if (value.Key == "System.Net.Http.HttpRequestOut.Stop")
{
var response = ResponseAccessor(value.Value!);
RaiseRequestStopped(_currentMessage.Value, response);
}
}
private static Func<object, HttpRequestMessage> CreateGetRequest()
{
var requestDataType = Type.GetType("System.Net.Http.DiagnosticsHandler+ActivityStartData, System.Net.Http", throwOnError: true)!;
var requestProperty = requestDataType.GetProperty("Request")!;
return (object o) => (HttpRequestMessage)requestProperty.GetValue(o)!;
}
private static Func<object, HttpResponseMessage> CreateGetResponse()
{
var requestDataType = Type.GetType("System.Net.Http.DiagnosticsHandler+ActivityStopData, System.Net.Http", throwOnError: true)!;
var requestProperty = requestDataType.GetProperty("Response")!;
return (object o) => (HttpResponseMessage)requestProperty.GetValue(o)!;
}
}
}
}
}
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
@code {
static HttpClient httpClient = new();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await httpClient.GetStringAsync("http://example.com");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment