mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-19 23:28:23 -04:00
274 lines
9.6 KiB
Plaintext
274 lines
9.6 KiB
Plaintext
@page "/logging/general"
|
|
@using AliasVault.RazorComponents.Tables
|
|
@inherits MainBase
|
|
|
|
<LayoutPageTitle>System logs</LayoutPageTitle>
|
|
|
|
<PageHeader
|
|
BreadcrumbItems="@BreadcrumbItems"
|
|
Title="@(TotalRecords > 0 ? $"General logs ({TotalRecords:N0})" : "General logs")"
|
|
Description="This page shows an overview of recent system logs.">
|
|
<CustomActions>
|
|
<DeleteButton OnClick="DeleteLogsWithConfirmation" ButtonText="Delete all logs" />
|
|
<RefreshButton OnClick="RefreshData" ButtonText="Refresh" />
|
|
</CustomActions>
|
|
</PageHeader>
|
|
|
|
@if (IsInitialized)
|
|
{
|
|
<div class="px-4">
|
|
<Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />
|
|
|
|
<div class="mb-3 flex space-x-4">
|
|
<div class="flex w-full">
|
|
<div class="w-2/3 pr-2">
|
|
<div class="relative">
|
|
<SearchIcon />
|
|
<input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search logs..." class="w-full px-4 ps-10 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
|
|
</div>
|
|
</div>
|
|
<div class="w-1/3 pl-2">
|
|
<select @bind="SelectedServiceName" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
|
|
<option value="">All Services</option>
|
|
@foreach (var service in ServiceNames)
|
|
{
|
|
<option value="@service">@service</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (IsLoading)
|
|
{
|
|
<LoadingIndicator />
|
|
}
|
|
else
|
|
{
|
|
<div class="px-4">
|
|
<SortableTable Columns="@_tableColumns" SortColumn="@SortColumn" SortDirection="@SortDirection" OnSortChanged="HandleSortChanged">
|
|
@foreach (var log in LogList)
|
|
{
|
|
<SortableTableRow>
|
|
<SortableTableColumn IsPrimary="true">@log.Id</SortableTableColumn>
|
|
<SortableTableColumn>@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
|
|
<SortableTableColumn>@log.Application</SortableTableColumn>
|
|
<SortableTableColumn>
|
|
@{
|
|
string bgColor = log.Level switch
|
|
{
|
|
"Information" => "bg-blue-500",
|
|
"Error" => "bg-red-500",
|
|
"Warning" => "bg-yellow-500",
|
|
"Debug" => "bg-green-500",
|
|
_ => "bg-gray-500"
|
|
};
|
|
}
|
|
<span class="px-2 py-1 rounded-full text-white @bgColor">
|
|
@log.Level
|
|
</span>
|
|
</SortableTableColumn>
|
|
<SortableTableColumn Title="@log.Exception">
|
|
@if (log.SourceContext.Length > 0)
|
|
{
|
|
<span>@log.SourceContext: </span>
|
|
}
|
|
@log.Message
|
|
</SortableTableColumn>
|
|
</SortableTableRow>
|
|
}
|
|
</SortableTable>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
private readonly List<TableColumn> _tableColumns = [
|
|
new TableColumn { Title = "ID", PropertyName = "Id" },
|
|
new TableColumn { Title = "Time", PropertyName = "Timestamp" },
|
|
new TableColumn { Title = "Application", PropertyName = "Application" },
|
|
new TableColumn { Title = "Level", PropertyName = "Level" },
|
|
new TableColumn { Title = "Message", PropertyName = "Message" },
|
|
];
|
|
|
|
private List<Log> LogList { get; set; } = [];
|
|
private bool IsInitialized { get; set; } = false;
|
|
|
|
private bool IsLoading { get; set; } = true;
|
|
private int CurrentPage { get; set; } = 1;
|
|
private int PageSize { get; set; } = 50;
|
|
private int TotalRecords { get; set; }
|
|
|
|
private string _searchTerm = string.Empty;
|
|
private string _lastSearchTerm = string.Empty;
|
|
private string SearchTerm
|
|
{
|
|
get => _searchTerm;
|
|
set
|
|
{
|
|
if (_searchTerm != value)
|
|
{
|
|
_searchTerm = value;
|
|
_ = RefreshData();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _selectedServiceName = string.Empty;
|
|
private string SelectedServiceName
|
|
{
|
|
get => _selectedServiceName;
|
|
set
|
|
{
|
|
if (_selectedServiceName != value)
|
|
{
|
|
_selectedServiceName = value;
|
|
_ = RefreshData();
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<string> ServiceNames { get; set; } = [];
|
|
|
|
private string SortColumn { get; set; } = "Id";
|
|
private SortDirection SortDirection { get; set; } = SortDirection.Descending;
|
|
|
|
private async Task HandleSortChanged((string column, SortDirection direction) sort)
|
|
{
|
|
SortColumn = sort.column;
|
|
SortDirection = sort.direction;
|
|
await RefreshData();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (firstRender)
|
|
{
|
|
await using var dbContext = await DbContextFactory.CreateDbContextAsync();
|
|
ServiceNames = await dbContext.Logs.Select(l => l.Application).Distinct().ToListAsync();
|
|
await RefreshData();
|
|
}
|
|
}
|
|
|
|
private void HandlePageChanged(int newPage)
|
|
{
|
|
CurrentPage = newPage;
|
|
_ = RefreshData();
|
|
}
|
|
|
|
private async Task RefreshData()
|
|
{
|
|
IsLoading = true;
|
|
StateHasChanged();
|
|
|
|
await using var dbContext = await DbContextFactory.CreateDbContextAsync();
|
|
var query = dbContext.Logs.AsQueryable();
|
|
|
|
query = ApplySearchTermFilter(query);
|
|
query = ApplyServiceNameFilter(query);
|
|
|
|
// Apply sort.
|
|
switch (SortColumn)
|
|
{
|
|
case "Application":
|
|
query = SortDirection == SortDirection.Ascending
|
|
? query.OrderBy(x => x.Application)
|
|
: query.OrderByDescending(x => x.Application);
|
|
break;
|
|
case "Message":
|
|
query = SortDirection == SortDirection.Ascending
|
|
? query.OrderBy(x => x.Message)
|
|
: query.OrderByDescending(x => x.Message);
|
|
break;
|
|
case "Level":
|
|
query = SortDirection == SortDirection.Ascending
|
|
? query.OrderBy(x => x.Level)
|
|
: query.OrderByDescending(x => x.Level);
|
|
break;
|
|
case "Timestamp":
|
|
query = SortDirection == SortDirection.Ascending
|
|
? query.OrderBy(x => x.TimeStamp)
|
|
: query.OrderByDescending(x => x.TimeStamp);
|
|
break;
|
|
default:
|
|
query = SortDirection == SortDirection.Ascending
|
|
? query.OrderBy(x => x.Id)
|
|
: query.OrderByDescending(x => x.Id);
|
|
break;
|
|
}
|
|
|
|
TotalRecords = await query.CountAsync();
|
|
LogList = await query
|
|
.Skip((CurrentPage - 1) * PageSize)
|
|
.Take(PageSize)
|
|
.ToListAsync();
|
|
|
|
IsLoading = false;
|
|
IsInitialized = true;
|
|
StateHasChanged();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies a search term filter to the query.
|
|
/// </summary>
|
|
/// <param name="query">The query to apply the filter to.</param>
|
|
private IQueryable<Log> ApplySearchTermFilter(IQueryable<Log> query)
|
|
{
|
|
if (!string.IsNullOrEmpty(SearchTerm))
|
|
{
|
|
// Reset page number back to 1 if the search term has changed.
|
|
if (SearchTerm != _lastSearchTerm)
|
|
{
|
|
CurrentPage = 1;
|
|
}
|
|
_lastSearchTerm = SearchTerm;
|
|
|
|
var searchTerm = SearchTerm.Trim().ToLower();
|
|
query = query.Where(x => EF.Functions.Like(x.Application.ToLower(), "%" + searchTerm + "%") ||
|
|
EF.Functions.Like(x.Message.ToLower(), "%" + searchTerm + "%") ||
|
|
EF.Functions.Like(x.Level.ToLower(), "%" + searchTerm + "%") ||
|
|
EF.Functions.Like(x.SourceContext.ToLower(), "%" + searchTerm + "%"));
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies a service name filter to the query.
|
|
/// </summary>
|
|
/// <param name="query">The query to apply the filter to.</param>
|
|
private IQueryable<Log> ApplyServiceNameFilter(IQueryable<Log> query)
|
|
{
|
|
if (!string.IsNullOrEmpty(SelectedServiceName))
|
|
{
|
|
query = query.Where(x => x.Application == SelectedServiceName);
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
private async Task DeleteLogsWithConfirmation()
|
|
{
|
|
if (await ConfirmModalService.ShowConfirmation("Confirm Delete", "Are you sure you want to delete all logs? This action cannot be undone."))
|
|
{
|
|
await DeleteLogs();
|
|
}
|
|
}
|
|
|
|
private async Task DeleteLogs()
|
|
{
|
|
IsLoading = true;
|
|
StateHasChanged();
|
|
|
|
await using var dbContext = await DbContextFactory.CreateDbContextAsync();
|
|
dbContext.Logs.RemoveRange(dbContext.Logs);
|
|
await dbContext.SaveChangesAsync();
|
|
await RefreshData();
|
|
|
|
IsLoading = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|