Fix #3625: add the clear logs function.

This commit is contained in:
Ben 2024-04-03 09:21:13 +08:00
parent 578b7b0512
commit 4c08a527be
8 changed files with 104 additions and 49 deletions

View File

@ -86,47 +86,48 @@ else
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
<ActionDialog Header="Clear Logs" Message="@Localizer["Confirm.ClearLogs"]" Action="ClearLogs" Class="btn btn-secondary" OnClick="@(async () => await ClearLogs())" ResourceKey="ClearLogs" />
</TabPanel>
</TabStrip>
}
@code {
private string _level = "-";
private string _function = "-";
private string _rows = "10";
private int _page = 1;
private List<Log> _logs;
private int _retention = 30;
private string _level = "-";
private string _function = "-";
private string _rows = "10";
private int _page = 1;
private List<Log> _logs;
private int _retention = 30;
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
try
{
if (UrlParameters.ContainsKey("level"))
{
_level = UrlParameters["level"];
}
if (UrlParameters.ContainsKey("function"))
{
_function = UrlParameters["function"];
}
if (UrlParameters.ContainsKey("rows"))
{
_rows = UrlParameters["rows"];
}
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
{
_page = page;
}
protected override async Task OnParametersSetAsync()
{
try
{
if (UrlParameters.ContainsKey("level"))
{
_level = UrlParameters["level"];
}
if (UrlParameters.ContainsKey("function"))
{
_function = UrlParameters["function"];
}
if (UrlParameters.ContainsKey("rows"))
{
_rows = UrlParameters["rows"];
}
if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
{
_page = page;
}
await GetLogs();
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
}
catch (Exception ex)
{
@ -213,22 +214,37 @@ else
return classname;
}
private async Task SaveSiteSettings()
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
private async Task SaveSiteSettings()
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
private async Task ClearLogs()
{
try
{
await LogService.ClearLogsAsync(PageState.Site.SiteId);
await GetLogs();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Clearing Logs {Error}", ex.Message);
AddModuleMessage(Localizer["Error.ClearLogs"], MessageType.Error);
}
}
private void OnPageChange(int page)
{

View File

@ -210,4 +210,13 @@
<data name="Success.SaveSiteSettings" xml:space="preserve">
<value>Settings Saved Successfully</value>
</data>
<data name="ClearLogs" xml:space="preserve">
<value>Clear Logs</value>
</data>
<data name="Confirm.ClearLogs" xml:space="preserve">
<value>Are you sure you wish to clear all the logs?</value>
</data>
<data name="Error.ClearLogs" xml:space="preserve">
<value>Error Clearing Logs</value>
</data>
</root>

View File

@ -29,6 +29,13 @@ namespace Oqtane.Services
/// <returns></returns>
Task<Log> GetLogAsync(int logId);
/// <summary>
/// Clear the entire logs of the given site.
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
Task ClearLogsAsync(int siteId);
/// <summary>
/// Creates a new log entry
/// </summary>

View File

@ -35,6 +35,11 @@ namespace Oqtane.Services
return await GetJsonAsync<Log>($"{Apiurl}/{logId}");
}
public async Task ClearLogsAsync(int siteId)
{
await DeleteAsync($"{Apiurl}?siteid={siteId}&includeErrors=true");
}
public async Task Log(int? pageId, int? moduleId, int? userId, string category, string feature, LogFunction function, LogLevel level, Exception exception, string message, params object[] args)
{
await Log(null, pageId, moduleId, userId, category, feature, function, level, exception, message, args);

View File

@ -76,5 +76,20 @@ namespace Oqtane.Controllers
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
[HttpDelete]
[Authorize(Roles = RoleNames.Admin)]
public void Delete(string siteId, bool includeErrors)
{
if (int.TryParse(siteId, out int parsedSiteId) && parsedSiteId == _alias.SiteId)
{
_logs.DeleteLogs(parsedSiteId, 0, includeErrors);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Log Delete Attempt {SiteId}", siteId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -8,6 +8,6 @@ namespace Oqtane.Repository
IEnumerable<Log> GetLogs(int siteId, string level, string function, int rows);
Log GetLog(int logId);
void AddLog(Log log);
int DeleteLogs(int siteId, int age);
int DeleteLogs(int siteId, int age, bool includeErrors = false);
}
}

View File

@ -53,20 +53,20 @@ namespace Oqtane.Repository
db.SaveChanges();
}
public int DeleteLogs(int siteId, int age)
public int DeleteLogs(int siteId, int age, bool includeErrors = false)
{
using var db = _dbContextFactory.CreateDbContext();
// delete logs in batches of 100 records
var count = 0;
var purgedate = DateTime.UtcNow.AddDays(-age);
var logs = db.Log.Where(item => item.SiteId == siteId && item.Level != "Error" && item.LogDate < purgedate)
var logs = db.Log.Where(item => item.SiteId == siteId && (includeErrors || item.Level != "Error") && item.LogDate < purgedate)
.OrderBy(item => item.LogDate).Take(100).ToList();
while (logs.Count > 0)
{
count += logs.Count;
db.Log.RemoveRange(logs);
db.SaveChanges();
logs = db.Log.Where(item => item.SiteId == siteId && item.Level != "Error" && item.LogDate < purgedate)
logs = db.Log.Where(item => item.SiteId == siteId && (includeErrors || item.Level != "Error") && item.LogDate < purgedate)
.OrderBy(item => item.LogDate).Take(100).ToList();
}
return count;

View File

@ -35,6 +35,9 @@ app {
}
/* Action Dialog */
.app-actiondialog{
position: absolute;
}
.app-actiondialog .modal {
position: fixed; /* Stay in place */
z-index: 9999; /* Sit on top */