Initial commit
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
@page "/logs"
|
||||
@rendermode InteractiveWebAssemblyNoPrerender
|
||||
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
||||
@using System.Text.Json.Serialization
|
||||
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject HttpClient Http
|
||||
@inject IConfiguration Configuration
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>Logs</PageTitle>
|
||||
|
||||
<div class="w-100 h-100 d-flex flex-column">
|
||||
<div class="d-flex flex-row align-items-center justify-content-between" style="border-bottom: 1px solid silver">
|
||||
<MudTextField Class="mt-1 ms-2" T="string" Value="FilterLog" Adornment="Adornment.End" ValueChanged="OnSearch" AdornmentIcon="@Icons.Material.Filled.Search"
|
||||
IconSize="Size.Medium" Variant="Variant.Outlined" Margin="Margin.Dense" AdornmentColor="Color.Secondary" Label="Search"></MudTextField>
|
||||
<MudSpacer />
|
||||
<div class="m-1 d-flex flex-row">
|
||||
<MudDatePicker Class="mx-4" Label="Date" Date="DateLog" DateChanged="OnDateChanged" MaxDate="DateTime.Today" Variant="Variant.Outlined" Color="Color.Primary"
|
||||
ShowToolbar="false" Margin="Margin.Dense" AdornmentColor="Color.Primary" />
|
||||
|
||||
<MudTooltip Text="Export">
|
||||
<MudFab Class="mt-2" Color="Color.Info" StartIcon="@Icons.Material.Filled.ImportExport" Size="Size.Small" OnClick="ExportLogs" />
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="Refresh">
|
||||
<MudFab Class="mx-4 mt-2" StartIcon="@Icons.Material.Filled.Refresh" Color="Color.Primary" Size="Size.Small" OnClick="LoadLogs" />
|
||||
</MudTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1 mt-2 ms-2 position-relative" style="background-color: rgba(0, 0, 0, 0);">
|
||||
<MudOverlay Visible="IsLoading" DarkBackground="true" Absolute="true">
|
||||
<MudProgressCircular Color="Color.Info" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
<div class="h-100 w-100 position-relative">
|
||||
<div class="log-container" @ref="LogContainerRef">
|
||||
@if (ShowRawLog)
|
||||
{
|
||||
<div class="d-flex justify-content-center my-3">
|
||||
<div><MudButton Variant="Variant.Outlined" Size="Size.Small" OnClick="@(() => ShowRawLog = false)">Normal</MudButton></div>
|
||||
</div>
|
||||
<big style="font-size: 14px;">
|
||||
@foreach (var log in ShowLogs)
|
||||
{
|
||||
@log <br />
|
||||
}
|
||||
</big>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (SearchLogs.Count < ShowLogs.Count)
|
||||
{
|
||||
<div class="d-flex justify-content-center my-3">
|
||||
<div><MudButton Variant="Variant.Outlined" Size="Size.Small" OnClick="@(() => ShowRawLog = true)">Raw log</MudButton></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@foreach (var log in SearchLogs)
|
||||
{
|
||||
<div class="log">
|
||||
<span class="log-head @log.BackgroundClass">
|
||||
@log.Time <span class="log-level">@log.Level</span>
|
||||
</span>
|
||||
<span>@log.Message</span>
|
||||
@if (log.HasException)
|
||||
{
|
||||
<br />
|
||||
<pre class="log-exception">
|
||||
@log.Exception
|
||||
</pre>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.ScrollToBottom = (element) => {
|
||||
if (element) {
|
||||
element.scrollTop = element.scrollHeight;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<MudThemeProvider IsDarkMode />
|
||||
<MudPopoverProvider />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
|
||||
@code {
|
||||
private DateTime DateLog = DateTime.Today;
|
||||
private bool IsLoading;
|
||||
private readonly List<string> ShowLogs = new();
|
||||
private readonly List<LoggerModel> SearchLogs = new();
|
||||
private ElementReference LogContainerRef { get; set; }
|
||||
private bool ShowRawLog { get; set; }
|
||||
private string? FilterLog { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (!firstRender) return;
|
||||
|
||||
await LoadLogs();
|
||||
}
|
||||
|
||||
private async Task LoadLogs()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ShowLogs.Clear();
|
||||
StateHasChanged();
|
||||
|
||||
var logs = await Http.GetFromJsonAsync<IEnumerable<string>>($"api/LogsManager?date={DateLog}");
|
||||
ShowLogs.AddRange(logs ?? []);
|
||||
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
|
||||
await ReloadLogs();
|
||||
}
|
||||
catch (AccessTokenNotAvailableException ex)
|
||||
{
|
||||
ex.Redirect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadLogs()
|
||||
{
|
||||
IsLoading = true;
|
||||
SearchLogs.Clear();
|
||||
StateHasChanged();
|
||||
|
||||
foreach (var line in ShowLogs.Where(log => string.IsNullOrEmpty(FilterLog) || log.Contains(FilterLog)).TakeLast(2000))
|
||||
{
|
||||
try
|
||||
{
|
||||
var log = System.Text.Json.JsonSerializer.Deserialize<LoggerModel>(line);
|
||||
if (log is not null) SearchLogs.Add(log);
|
||||
}
|
||||
catch (System.Text.Json.JsonException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
await JSRuntime.InvokeVoidAsync("ScrollToBottom", LogContainerRef);
|
||||
}
|
||||
|
||||
private async Task OnSearch(string text)
|
||||
{
|
||||
FilterLog = text;
|
||||
await ReloadLogs();
|
||||
}
|
||||
|
||||
private async Task OnDateChanged(DateTime? date)
|
||||
{
|
||||
if (date is not null && date.HasValue)
|
||||
{
|
||||
DateLog = date.Value;
|
||||
await LoadLogs();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExportLogs()
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = await Http.GetFromJsonAsync<IEnumerable<string>>($"api/LogsManager?date={DateLog}");
|
||||
var formattedContent = string.Join("\n", fileContent ?? []);
|
||||
var fileName = $"LogsManager_{DateLog.ToShortDateString()}.txt";
|
||||
await JSRuntime.InvokeVoidAsync("downloadFile", fileName, formattedContent, "text/plain");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Lỗi khi tải file: {ex.Message}", Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoggerModel
|
||||
{
|
||||
[JsonPropertyName("time")]
|
||||
public string? Time { get; set; }
|
||||
|
||||
[JsonPropertyName("level")]
|
||||
public string? Level { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string? Message { get; set; }
|
||||
|
||||
[JsonPropertyName("exception")]
|
||||
public string? Exception { get; set; }
|
||||
|
||||
public string ColorClass => Level switch
|
||||
{
|
||||
"WARN" => "text-warning",
|
||||
"INFO" => "text-info",
|
||||
"DEBUG" => "text-success",
|
||||
"ERROR" => "text-danger",
|
||||
"FATAL" => "text-secondary",
|
||||
_ => "text-muted",
|
||||
};
|
||||
|
||||
public string BackgroundClass => Level switch
|
||||
{
|
||||
"WARN" => "bg-warning text-dark",
|
||||
"INFO" => "bg-info text-dark",
|
||||
"DEBUG" => "bg-success text-white",
|
||||
"ERROR" => "bg-danger text-white",
|
||||
"FATAL" => "bg-secondary text-white",
|
||||
_ => "bg-dark text-white",
|
||||
};
|
||||
|
||||
public bool HasException => !string.IsNullOrEmpty(Exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user