Supervisor S-0c: Log Viewer im Terminal (JSONL menschenlesbar, CID-Forensik)
Terminal-Fenster jetzt mit TabControl (via Designer): Tab 'Live' = bisherige Anzeige,
Tab 'Log Viewer' = JSONL-Tagesdateien menschenlesbar aufbereitet:
- Filter: Datum (DateTimePicker), Level, CorrelationId (SignalId), Suchtext.
- Doppelklick auf eine CID laedt die komplette Signal-Kette ('zeig mir alles zu diesem Signal').
- LogJson.ParseLine (pur, Gegenstueck zu Format): tolerant gegen kaputte Zeilen (null statt Crash);
Viewer ueberspringt sie. UI-Schutz bei sehr grossen Tagen (20k-Limit).
Tests: +5 (Parse-Roundtrip, kaputte Zeilen). Build 0 Fehler, 341 Tests gruen, --smoke-ui komplett gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bf3e012f8b
commit
ca00977ea3
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using PolyTraderSharp.Services;
|
||||
|
||||
@@ -33,6 +34,72 @@ namespace PolyTraderSharp.Ui.Views
|
||||
miSelectAll.Click += (_, _) => rtbTerminal.SelectAll();
|
||||
miCopyAll.Click += (_, _) => CopyAllToClipboard();
|
||||
miClear.Click += (_, _) => rtbTerminal.Clear();
|
||||
|
||||
// Log Viewer (S-0): JSONL-Tagesdateien menschenlesbar, filterbar nach Level/CID/Text.
|
||||
if (cbViewerLevel.Items.Count > 0) cbViewerLevel.SelectedIndex = 0;
|
||||
btnViewerLoad.Click += (_, _) => LoadViewer();
|
||||
tbViewerCid.KeyDown += (_, e) => { if (e.KeyCode == Keys.Enter) { LoadViewer(); e.SuppressKeyPress = true; } };
|
||||
tbViewerText.KeyDown += (_, e) => { if (e.KeyCode == Keys.Enter) { LoadViewer(); e.SuppressKeyPress = true; } };
|
||||
// Doppelklick auf eine CID: komplette Signal-Kette anzeigen.
|
||||
dgvLogs.CellDoubleClick += (_, e) =>
|
||||
{
|
||||
if (e.RowIndex < 0) return;
|
||||
if (dgvLogs.Rows[e.RowIndex].DataBoundItem is LogViewerRow row && !string.IsNullOrEmpty(row.Cid))
|
||||
{
|
||||
tbViewerCid.Text = row.Cid;
|
||||
tbViewerText.Text = "";
|
||||
LoadViewer();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ===== Log Viewer =====
|
||||
|
||||
/// <summary>Anzeige-Zeile des Log Viewers (Bindung über DataPropertyName).</summary>
|
||||
private sealed class LogViewerRow
|
||||
{
|
||||
public string Time { get; init; } = "";
|
||||
public string Level { get; init; } = "";
|
||||
public string Cid { get; init; } = "";
|
||||
public string Message { get; init; } = "";
|
||||
}
|
||||
|
||||
private void LoadViewer()
|
||||
{
|
||||
try
|
||||
{
|
||||
string path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs",
|
||||
$"{dtViewerDate.Value:yyyy-MM-dd}.jsonl");
|
||||
if (!System.IO.File.Exists(path))
|
||||
{
|
||||
dgvLogs.DataSource = new System.ComponentModel.BindingList<LogViewerRow>();
|
||||
lblViewerStatus.Text = "Keine JSONL-Datei für dieses Datum.";
|
||||
return;
|
||||
}
|
||||
|
||||
string levelFilter = cbViewerLevel.SelectedItem?.ToString() ?? "Alle";
|
||||
string cidFilter = tbViewerCid.Text.Trim();
|
||||
string textFilter = tbViewerText.Text.Trim();
|
||||
|
||||
var rows = new List<LogViewerRow>();
|
||||
foreach (string line in System.IO.File.ReadLines(path))
|
||||
{
|
||||
var p = LogJson.ParseLine(line);
|
||||
if (p == null) continue;
|
||||
if (levelFilter != "Alle" && p.Level != levelFilter) continue;
|
||||
if (cidFilter.Length > 0 && !p.Cid.Contains(cidFilter, StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (textFilter.Length > 0 && !p.Message.Contains(textFilter, StringComparison.OrdinalIgnoreCase)) continue;
|
||||
rows.Add(new LogViewerRow { Time = p.Time, Level = p.Level, Cid = p.Cid, Message = p.Message });
|
||||
if (rows.Count >= 20000) break; // UI-Schutz bei sehr großen Tagen
|
||||
}
|
||||
|
||||
dgvLogs.DataSource = new System.ComponentModel.BindingList<LogViewerRow>(rows);
|
||||
lblViewerStatus.Text = $"{rows.Count} Einträge.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
lblViewerStatus.Text = $"Fehler: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyAllToClipboard()
|
||||
|
||||
Reference in New Issue
Block a user