This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
using FastEndpoints;
|
||||
|
||||
namespace TaxBaik.Web.Endpoints.ClientLogs;
|
||||
|
||||
public sealed class RecentClientLogEntry
|
||||
{
|
||||
public string? Timestamp { get; set; }
|
||||
public string? Level { get; set; }
|
||||
public string? Source { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public string? Route { get; set; }
|
||||
public string? Screen { get; set; }
|
||||
public string? Feature { get; set; }
|
||||
public string? Action { get; set; }
|
||||
public string? Step { get; set; }
|
||||
public string? BuildVersion { get; set; }
|
||||
}
|
||||
|
||||
public sealed class GetRecentLogsEndpoint : EndpointWithoutRequest<IReadOnlyList<RecentClientLogEntry>>
|
||||
{
|
||||
private readonly ILogger<GetRecentLogsEndpoint> _logger;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public GetRecentLogsEndpoint(ILogger<GetRecentLogsEndpoint> logger, IWebHostEnvironment environment)
|
||||
{
|
||||
_logger = logger;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/api/client-logs/recent");
|
||||
Policies("AdminOnly");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(CancellationToken ct)
|
||||
{
|
||||
var logDir = Path.Combine(_environment.ContentRootPath, "logs");
|
||||
if (!Directory.Exists(logDir))
|
||||
{
|
||||
await SendAsync(Array.Empty<RecentClientLogEntry>(), cancellation: ct);
|
||||
return;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(logDir, "taxbaik-*.log")
|
||||
.OrderByDescending(File.GetLastWriteTimeUtc)
|
||||
.Take(3)
|
||||
.ToArray();
|
||||
|
||||
var entries = new List<RecentClientLogEntry>();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
foreach (var line in await File.ReadAllLinesAsync(file, ct))
|
||||
{
|
||||
if (!line.Contains("ClientLog ", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
entries.Add(Parse(line));
|
||||
}
|
||||
}
|
||||
|
||||
await SendAsync(entries.TakeLast(50).Reverse().ToList(), cancellation: ct);
|
||||
}
|
||||
|
||||
private static RecentClientLogEntry Parse(string line)
|
||||
{
|
||||
var entry = new RecentClientLogEntry();
|
||||
var messageIndex = line.IndexOf("ClientLog ", StringComparison.OrdinalIgnoreCase);
|
||||
entry.Message = messageIndex >= 0 ? line[(messageIndex + "ClientLog ".Length)..] : line;
|
||||
|
||||
var timestampEnd = line.IndexOf("] [", StringComparison.Ordinal);
|
||||
if (line.StartsWith("[") && timestampEnd > 1)
|
||||
{
|
||||
entry.Timestamp = line[1..timestampEnd];
|
||||
}
|
||||
|
||||
entry.Level = Extract(line, "ClientLog ", " ");
|
||||
entry.Source = ExtractAfter(line, "ClientLog ", 1);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private static string? Extract(string text, string start, string end)
|
||||
{
|
||||
var index = text.IndexOf(start, StringComparison.OrdinalIgnoreCase);
|
||||
if (index < 0) return null;
|
||||
var slice = text[(index + start.Length)..];
|
||||
var endIndex = slice.IndexOf(end, StringComparison.OrdinalIgnoreCase);
|
||||
return endIndex < 0 ? slice : slice[..endIndex];
|
||||
}
|
||||
|
||||
private static string? ExtractAfter(string text, string start, int tokenIndex)
|
||||
{
|
||||
var tokens = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
return tokens.Length > tokenIndex ? tokens[tokenIndex] : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user