Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 46 additions & 21 deletions DebugProbe.AspNetCore/Extensions/DebugProbeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static IServiceCollection AddDebugProbe(
public static IApplicationBuilder UseDebugProbe(this IApplicationBuilder app)
{
app.UseMiddleware<DebugProbeMiddleware>();
app.ApplicationServices.GetRequiredService<DebugEntryStore>();

if (app is WebApplication webApp)
{
Expand Down Expand Up @@ -59,59 +60,83 @@ public static IApplicationBuilder UseDebugProbe(this IApplicationBuilder app)
var prettyRequest = JsonUtils.Format(item.RequestBody);
var prettyResponse = JsonUtils.Format(item.ResponseBody);

var html = HtmlRenderer.RenderDetailsPage(item, prettyRequest, prettyResponse);
var html = HtmlRenderer.RenderDetailsPage(item, store.Environment, prettyRequest, prettyResponse);

ctx.Response.ContentType = "text/html";
await ctx.Response.WriteAsync(html);
}).ExcludeFromDescription();

webApp.MapGet("/debug/compare/{id}", async (string id, string url, DebugEntryStore store) =>
webApp.MapGet("/debug/compare/{id}", async (string id, string baseUrl, string remoteTraceId, DebugEntryStore store) =>
{
var local = store.Get(id);
if (local is null)
var localEnvironment = store.Environment;
var localEntry = store.Get(id);
if (localEntry is null)
{
return Results.NotFound("Local trace not found");
}

DebugEntry? remote;

var normalizedBaseUrl = baseUrl.TrimEnd('/');

var remoteEnvironmentUrl =
$"{normalizedBaseUrl}/debug/environment";

var remoteEntryUrl =
$"{normalizedBaseUrl}/debug/json/{remoteTraceId}";

DebugEntry? remoteEntry;
DebugEnvironment? remoteEnvironment;

try
{
remote = await Http.GetFromJsonAsync<DebugEntry>(url);
remoteEnvironment = await Http.GetFromJsonAsync<DebugEnvironment>(remoteEnvironmentUrl);

if (remoteEnvironment is null)
{
return Results.BadRequest("Failed to load remote environment");
}

remoteEntry = await Http.GetFromJsonAsync<DebugEntry>(remoteEntryUrl);

if (remoteEntry is null)
{
return Results.NotFound("Remote trace not found");
}
}
catch
{
return Results.BadRequest("Failed to reach remote server");
}

if (remote is null)
{
return Results.NotFound("Remote trace not found");
}

var diff = DebugEntryComparer.Compare(local, remote);

var diff = DebugEntryComparer.Compare(localEntry, remoteEntry);

return Results.Ok(new
{
method = new { local = local.Method, remote = remote.Method },
path = new { local = local.Path, remote = remote.Path },
status = new { local = local.StatusCode, remote = remote.StatusCode },
method = new { local = localEntry.Method, remote = remoteEntry.Method },
path = new { local = localEntry.Path, remote = remoteEntry.Path },
status = new { local = localEntry.StatusCode, remote = remoteEntry.StatusCode },

requestTime = new
{
local = local.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
remote = remote.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
local = localEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
remote = remoteEntry.RequestTimeUtc.ToLocalTime().ToString("HH:mm:ss"),
},

environment = new { local = local.Environment, remote = remote.Environment },
culture = new { local = local.Culture, remote = remote.Culture },
requestBody = new { local = local.RequestBody ?? "", remote = remote.RequestBody ?? "" },
responseBody = new { local = local.ResponseBody ?? "", remote = remote.ResponseBody ?? "" },
environment = new { local = localEnvironment.Environment, remote = remoteEnvironment?.Environment ?? "" },
culture = new { local = localEnvironment.Culture, remote = remoteEnvironment?.Culture ?? "" },
requestBody = new { local = localEntry.RequestBody ?? "", remote = remoteEntry.RequestBody ?? "" },
responseBody = new { local = localEntry.ResponseBody ?? "", remote = remoteEntry.ResponseBody ?? "" },

diffs = diff
});
}).ExcludeFromDescription();

webApp.MapGet("/debug/environment", (DebugEntryStore store) =>
{
return Results.Ok(store.Environment);
}).ExcludeFromDescription();

webApp.MapGet("/debug/json/{id}", (string id, DebugEntryStore store) =>
{
var item = store.Get(id);
Expand Down
6 changes: 0 additions & 6 deletions DebugProbe.AspNetCore/Internal/DebugEntryComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ public static List<object> Compare(DebugEntry a, DebugEntry b)
if (a.StatusCode != b.StatusCode)
AddDiff(diffs, "Status", a.StatusCode, b.StatusCode, "meta");

if (a.Environment != b.Environment)
AddDiff(diffs, "Environment", a.Environment, b.Environment, "meta");

if (a.Culture != b.Culture)
AddDiff(diffs, "Culture", a.Culture, b.Culture, "meta");

CompareBody(a.RequestBody, b.RequestBody, RequestBodyPath, diffs);
CompareBody(a.ResponseBody, b.ResponseBody, ResponseBodyPath, diffs);

Expand Down
16 changes: 8 additions & 8 deletions DebugProbe.AspNetCore/Internal/HtmlRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static string RenderIndexPage(List<DebugEntry> items)
return BuildLayout(EmbeddedResources.Index.Replace("{{rows}}", rows));
}

public static string RenderDetailsPage(DebugEntry x, string req, string res)
public static string RenderDetailsPage(DebugEntry x, DebugEnvironment e, string req, string res)
{
var headers = string.Join("", x.Headers.Select(h =>
$"<tr><td>{Encode(h.Key)}</td><td>{Encode(h.Value)}</td></tr>"));
Expand Down Expand Up @@ -71,14 +71,14 @@ public static string RenderDetailsPage(DebugEntry x, string req, string res)
.Replace("{{requestSize}}", x.RequestSize.ToString())
.Replace("{{responseSize}}", x.ResponseSize.ToString())

.Replace("{{env}}", Encode(x.Environment))
.Replace("{{culture}}", Encode(x.Culture))
.Replace("{{env}}", Encode(e.Environment))
.Replace("{{culture}}", Encode(e.Culture))

.Replace("{{machineName}}", Encode(x.MachineName))
.Replace("{{timeZone}}", Encode(x.TimeZone))
.Replace("{{decimalSeparator}}", Encode(x.DecimalSeparator))
.Replace("{{dateFormat}}", x.DateFormat ?? "")
.Replace("{{assemblyVersion}}", Encode(x.AssemblyVersion))
.Replace("{{machineName}}", Encode(e.MachineName))
.Replace("{{timeZone}}", Encode(e.TimeZone))
.Replace("{{decimalSeparator}}", Encode(e.DecimalSeparator))
.Replace("{{dateFormat}}", e.DateFormat ?? "")
.Replace("{{assemblyVersion}}", Encode(e.AssemblyVersion))

.Replace("{{requestUrl}}", Encode(string.IsNullOrEmpty(x.RequestUrl) ? "(empty)" : x.RequestUrl))
.Replace("{{request}}", Encode(string.IsNullOrEmpty(req) ? "(empty)" : req))
Expand Down
17 changes: 0 additions & 17 deletions DebugProbe.AspNetCore/Middleware/DebugProbeMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Text;
using DebugProbe.AspNetCore.Internal;
using DebugProbe.AspNetCore.Models;
using DebugProbe.AspNetCore.Options;
using DebugProbe.AspNetCore.Storage;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;

namespace DebugProbe.AspNetCore.Middleware;

Expand Down Expand Up @@ -83,25 +79,12 @@ public async Task Invoke(HttpContext context, DebugEntryStore store)
await ms.CopyToAsync(originalBody);
context.Response.Body = originalBody;

var shortDatePattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var index = shortDatePattern.LastIndexOf('y');
var dataFormat = index >= 0 ? shortDatePattern[..(index + 1)] : shortDatePattern;

var statusCode = exception && context.Response.StatusCode == 200 ? 500 : context.Response.StatusCode;

store.Add(new DebugEntry
{
Id = Guid.NewGuid().ToString(),

// Environment
Environment = EnvironmentUtils.TryGetEnvironment(),
MachineName = Environment.MachineName,
AssemblyVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(),
TimeZone = TimeZoneInfo.Local.DisplayName,
Culture = CultureInfo.CurrentCulture.Name,
DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator,
DateFormat = dataFormat,

// Overview
Method = context.Request.Method,
Path = context.Request.Path,
Expand Down
11 changes: 0 additions & 11 deletions DebugProbe.AspNetCore/Models/DebugEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@ public class DebugEntry
public long ResponseSize { get; set; }
public string ResponseBody { get; set; } = default!;

// Environment
public string Environment { get; set; } = default!;
public string Culture { get; set; } = default!;
public string? UiCulture { get; set; }

public string? MachineName { get; set; }
public string? AssemblyVersion { get; set; }
public string? TimeZone { get; set; }
public string? DecimalSeparator { get; set; }
public string? DateFormat { get; set; }

// Headers
public Dictionary<string, string> Headers { get; set; } = new();

Expand Down
13 changes: 13 additions & 0 deletions DebugProbe.AspNetCore/Models/DebugEnvironment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace DebugProbe.AspNetCore.Models;

public class DebugEnvironment
{
public string Environment { get; init; } = default!;
public string Culture { get; init; } = default!;
public string? UiCulture { get; init; }
public string? MachineName { get; init; }
public string? AssemblyVersion { get; init; }
public string? TimeZone { get; init; }
public string? DecimalSeparator { get; init; }
public string? DateFormat { get; init; }
}
3 changes: 1 addition & 2 deletions DebugProbe.AspNetCore/Resources/js/debugprobe-compare.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
setCompareResult('<b style="color:orange">Comparing...</b>');

try {
const remoteUrl = `${base.replace(/\/$/, '')}/debug/json/${remoteId}`;
const res = await fetch(`/debug/compare/${id}?url=${encodeURIComponent(remoteUrl)}`);
const res = await fetch(`/debug/compare/${id}?baseUrl=${encodeURIComponent(base)}&remoteTraceId=${encodeURIComponent(remoteId)}`);

if (!res.ok) {
const text = await res.json();
Expand Down
26 changes: 26 additions & 0 deletions DebugProbe.AspNetCore/Storage/DebugEntryStore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Collections.Concurrent;
using System.Globalization;
using System.Reflection;
using DebugProbe.AspNetCore.Internal;
using DebugProbe.AspNetCore.Models;
using DebugProbe.AspNetCore.Options;

Expand All @@ -9,12 +12,26 @@ namespace DebugProbe.AspNetCore.Storage;
/// </summary>
public class DebugEntryStore
{
public DebugEnvironment Environment { get; }

private readonly ConcurrentQueue<DebugEntry> _queue = new();
private readonly int _limit;

public DebugEntryStore(DebugProbeOptions options)
{
_limit = options.MaxEntries;

Environment = new DebugEnvironment
{
Environment = EnvironmentUtils.TryGetEnvironment(),
MachineName = System.Environment.MachineName,
AssemblyVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(),
TimeZone = TimeZoneInfo.Local.DisplayName,
Culture = CultureInfo.CurrentCulture.Name,
UiCulture = CultureInfo.CurrentUICulture.Name,
DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator,
DateFormat = GetDateFormat()
};
}

public void Add(DebugEntry entry)
Expand All @@ -38,4 +55,13 @@ public void Clear()
{
while (_queue.TryDequeue(out _)) { }
}

private string GetDateFormat()
{
var shortDatePattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var index = shortDatePattern.LastIndexOf('y');
var dataFormat = index >= 0 ? shortDatePattern[..(index + 1)] : shortDatePattern;

return dataFormat;
}
}