diff --git a/src/PolyTrader.Web/Configuration/LogDirectory.cs b/src/PolyTrader.Web/Configuration/LogDirectory.cs
new file mode 100644
index 0000000..afef42b
--- /dev/null
+++ b/src/PolyTrader.Web/Configuration/LogDirectory.cs
@@ -0,0 +1,8 @@
+namespace PolyTrader.Web.Configuration
+{
+ ///
+ /// Ordner der Log-Tagesdateien für den Log-Viewer (#564). Im Betrieb nicht registriert: Dann gilt derselbe
+ /// Ordner, in den der TerminalLogger schreibt. Tests legen ihn in ein temporäres Verzeichnis.
+ ///
+ public sealed record LogDirectory(string Path);
+}
diff --git a/src/PolyTrader.Web/Endpoints/LogsEndpoints.cs b/src/PolyTrader.Web/Endpoints/LogsEndpoints.cs
index 1562e49..4f12780 100644
--- a/src/PolyTrader.Web/Endpoints/LogsEndpoints.cs
+++ b/src/PolyTrader.Web/Endpoints/LogsEndpoints.cs
@@ -1,3 +1,8 @@
+using PolyTrader.Web.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System.IO;
+using System.Globalization;
+using System.Collections.Generic;
using System;
using System.Linq;
using System.Text.Json;
@@ -13,6 +18,17 @@ namespace PolyTrader.Web.Endpoints
{
public static class LogsEndpoints
{
+ /// Wie im Desktop-Viewer: Schutz bei sehr grossen Tagen.
+ internal const int MaxMatches = 20000;
+
+ /// Etwas mehr als ein Jahr - aeltere Tage lassen sich weiter direkt ueber das Datum abrufen.
+ internal const int MaxListedDays = 400;
+
+ /// Ordner der Tagesdateien. Ohne Registrierung derselbe wie im TerminalLogger.
+ private static string ResolveLogDirectory(IServiceProvider sp) =>
+ sp.GetService()?.Path
+ ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
+
public static IEndpointRouteBuilder MapLogsEndpoints(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/api/logs");
@@ -84,6 +100,95 @@ namespace PolyTrader.Web.Endpoints
}
});
+ // ---- Log-Viewer (#564) ---------------------------------------------------------------
+ // Gegenstueck zum Tab "Log Viewer" im Desktop (TerminalWindow.LoadViewer): Tagesdateien
+ // Logs/yyyy-MM-dd.jsonl, gefiltert nach Level, Korrelations-ID und Text, hoechstens
+ // MaxMatches Treffer. Der Live-Stream zeigt nur, was seit dem Prozessstart aufgelaufen ist.
+
+ group.MapGet("/files", (IServiceProvider sp) =>
+ {
+ string dir = ResolveLogDirectory(sp);
+ if (!Directory.Exists(dir))
+ return Results.Ok(new List