diff --git a/src/Web/AdminPanel/Pages/LogFiles.razor b/src/Web/AdminPanel/Pages/LogFiles.razor index aa36577..2693e25 100644 --- a/src/Web/AdminPanel/Pages/LogFiles.razor +++ b/src/Web/AdminPanel/Pages/LogFiles.razor @@ -15,7 +15,7 @@
Log Files -
@@ -39,7 +39,7 @@ var isSelected = this._selectedFile?.FullName == entry.FullName; - @if (this._selectedFile != null) @@ -78,13 +78,13 @@
- +
- -
@@ -97,7 +97,7 @@ @if (!string.IsNullOrEmpty(this._searchText)) { - + } @@ -110,7 +110,7 @@ } else { - var filteredLines = GetFilteredLines(); + var filteredLines = this.GetFilteredLines(); @if (filteredLines.Count == 0) {
No log entries match your filter.
@@ -127,9 +127,9 @@
- Showing @GetFilteredLines().Count of @this._logLines.Count lines (Last 300 lines loaded). + Showing @this.GetFilteredLines().Count of @this._logLines.Count lines (Last 300 lines loaded).
-
@@ -140,13 +140,19 @@ @code { - private readonly List _files = new (); + private readonly List _files = new(); private FileInfo? _selectedFile; - private List _logLines = new (); + private List _logLines = new(); private string _searchText = string.Empty; - private bool _liveUpdate = false; + private bool _liveUpdate; private System.Threading.Timer? _timer; - private bool _shouldScrollToBottom = false; + private bool _shouldScrollToBottom; + + /// + public void Dispose() + { + this._timer?.Dispose(); + } /// protected override void OnInitialized() @@ -154,6 +160,87 @@ this.RefreshFileList(); } + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (this._shouldScrollToBottom) + { + this._shouldScrollToBottom = false; + await this.ScrollToBottomAsync().ConfigureAwait(false); + } + } + + private static string FormatFileSize(long size) + { + return size switch + { + < 1024 => $"{size} bytes", + < 1024 * 1024 => $"{Math.Round(size / 1024D, 2)} KiB", + < 1024L * 1024 * 1024 => $"{Math.Round(size / (1024D * 1024D), 2)} MiB", + _ => $"{Math.Round(size / (1024D * 1024D * 1024D), 2)} GiB", + }; + } + + private static List ReadLastLines(string path, int maxLines) + { + var lines = new List(); + try + { + using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + long offset = Math.Max(0, fs.Length - 102400); // Read last 100 KB + fs.Seek(offset, SeekOrigin.Begin); + using var reader = new StreamReader(fs, System.Text.Encoding.UTF8); + + if (offset > 0) + { + // Discard partial line + reader.ReadLine(); + } + + string? line; + while ((line = reader.ReadLine()) != null) + { + lines.Add(line); + } + + if (lines.Count > maxLines) + { + lines = lines.Skip(lines.Count - maxLines).ToList(); + } + } + catch (Exception ex) + { + lines.Add($"Error reading log file: {ex.Message}"); + } + + return lines; + } + + private static string GetLineColorStyle(string line) + { + if (line.Contains("[Error]", StringComparison.OrdinalIgnoreCase) || line.Contains("[Critical]", StringComparison.OrdinalIgnoreCase)) + { + return "color: #ff6b6b; font-weight: bold;"; + } + + if (line.Contains("[Warning]", StringComparison.OrdinalIgnoreCase)) + { + return "color: #feca57;"; + } + + if (line.Contains("[Debug]", StringComparison.OrdinalIgnoreCase)) + { + return "color: #8a8d93; font-style: italic;"; + } + + if (line.Contains("[Information]", StringComparison.OrdinalIgnoreCase)) + { + return "color: #1dd1a1;"; + } + + return "color: #d1d2d6;"; + } + private void RefreshFileList() { this._files.Clear(); @@ -203,7 +290,7 @@ { this._timer ??= new System.Threading.Timer(_ => { - InvokeAsync(() => + this.InvokeAsync(() => { this.RefreshLogLines(); this.StateHasChanged(); @@ -225,45 +312,10 @@ } this._selectedFile = new FileInfo(this._selectedFile.FullName); - this._logLines = this.ReadLastLines(this._selectedFile.FullName, 300); + this._logLines = ReadLastLines(this._selectedFile.FullName, 300); this._shouldScrollToBottom = true; } - private List ReadLastLines(string path, int maxLines) - { - var lines = new List(); - try - { - using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - long offset = Math.Max(0, fs.Length - 102400); // Read last 100 KB - fs.Seek(offset, SeekOrigin.Begin); - using var reader = new StreamReader(fs, System.Text.Encoding.UTF8); - - if (offset > 0) - { - // Discard partial line - reader.ReadLine(); - } - - string? line; - while ((line = reader.ReadLine()) != null) - { - lines.Add(line); - } - - if (lines.Count > maxLines) - { - lines = lines.Skip(lines.Count - maxLines).ToList(); - } - } - catch (Exception ex) - { - lines.Add($"Error reading log file: {ex.Message}"); - } - - return lines; - } - private List GetFilteredLines() { if (string.IsNullOrWhiteSpace(this._searchText)) @@ -276,67 +328,15 @@ .ToList(); } - private string GetLineColorStyle(string line) - { - if (line.Contains("[Error]", StringComparison.OrdinalIgnoreCase) || line.Contains("[Critical]", StringComparison.OrdinalIgnoreCase)) - { - return "color: #ff6b6b; font-weight: bold;"; - } - - if (line.Contains("[Warning]", StringComparison.OrdinalIgnoreCase)) - { - return "color: #feca57;"; - } - - if (line.Contains("[Debug]", StringComparison.OrdinalIgnoreCase)) - { - return "color: #8a8d93; font-style: italic;"; - } - - if (line.Contains("[Information]", StringComparison.OrdinalIgnoreCase)) - { - return "color: #1dd1a1;"; - } - - return "color: #d1d2d6;"; - } - - private async Task ScrollToBottom() + private async Task ScrollToBottomAsync() { try { - await JSRuntime.InvokeVoidAsync("eval", "var el = document.getElementById('log-terminal'); if (el) { el.scrollTop = el.scrollHeight; }"); + await this.JSRuntime.InvokeVoidAsync("eval", "var el = document.getElementById('log-terminal'); if (el) { el.scrollTop = el.scrollHeight; }").ConfigureAwait(false); } catch { // Ignore error } } - - /// - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (this._shouldScrollToBottom) - { - this._shouldScrollToBottom = false; - await this.ScrollToBottom(); - } - } - - /// - public void Dispose() - { - this._timer?.Dispose(); - } - - private string FormatFileSize(long size) - { - return size switch - { - (< 1024 << 10) => $"{Math.Round(size / 1024D, 2)} KiB", - (< 1024 << 20) => $"{Math.Round(size * 1D / (1024 << 10), 2)} MiB", - (< 1024L << 30) => $"{Math.Round(size * 1D / (1024L << 20), 2)} GiB", - _ => $"{size} bytes" - }; - } }