Refactor logging. Update submodule.

This commit is contained in:
Jared Goodwin 2023-05-23 14:46:20 -07:00
parent db19e92823
commit c647e03aea
9 changed files with 117 additions and 221 deletions

View File

@ -142,7 +142,7 @@ else
private async Task ClearAllLogs()
{
var result = await JsInterop.Confirm("Are you sure you want to delete all logs?");
var result = await JsInterop.Confirm("Are you sure you want to delete all previous logs? Today's logs are retained.");
if (result)
{
using (var _ = LoaderService.ShowLoader("Deleting logs"))

View File

@ -205,7 +205,7 @@ services.AddScoped<IExpiringTokenService, ExpiringTokenService>();
services.AddScoped<IScriptScheduleDispatcher, ScriptScheduleDispatcher>();
services.AddSingleton<IOtpProvider, OtpProvider>();
services.AddSingleton<IEmbeddedServerDataSearcher, EmbeddedServerDataSearcher>();
services.AddSingleton<ILogsManager>(LogsManager.Default);
services.AddSingleton<ILogsManager, LogsManager>();
services.AddSingleton(WeakReferenceMessenger.Default);
services.AddRemoteControlServer(config =>
@ -331,7 +331,7 @@ void ConfigureSerilog(WebApplicationBuilder webAppBuilder)
dataRetentionDays = retentionSetting;
}
var logPath = LogsManager.Default.GetLogsDirectory();
var logPath = LogsManager.DefaultLogsDirectory;
void ApplySharedLoggerConfig(LoggerConfiguration loggerConfiguration)
{

View File

@ -1,6 +1,6 @@
using Nihs.SimpleMessenger;
using Immense.RemoteControl.Shared.Primitives;
using Nihs.SimpleMessenger;
using Remotely.Server.Models.Messages;
using Remotely.Shared.Primitives;
using System;
using System.Threading.Tasks;

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Immense.RemoteControl.Shared.Services;
using Microsoft.Extensions.Logging;
using Remotely.Shared.Extensions;
using Serilog;
using System;
@ -17,28 +18,105 @@ namespace Remotely.Server.Services
{
public interface ILogsManager
{
string GetLogsDirectory();
Task<FileInfo> ZipAllLogs();
Task DeleteLogs();
IAsyncEnumerable<string> GetLogs(
DateTimeOffset startDate,
DateTimeOffset startDate,
DateTimeOffset endDate,
string messageFilter,
LogLevel? logLevelFilter);
string GetLogsDirectory();
Task<FileInfo> ZipAllLogs();
}
public class LogsManager : ILogsManager
{
public static LogsManager Default { get; } = new();
private static readonly ReadOnlyDictionary<string, LogLevel> _logLevelMap = new(new Dictionary<string, LogLevel>()
{
["[VRB]"] = LogLevel.Trace,
["[DBG]"] = LogLevel.Debug,
["[INF]"] = LogLevel.Information,
["[WRN]"] = LogLevel.Warning,
["[ERR]"] = LogLevel.Error,
["[FTL]"] = LogLevel.Critical
});
public static string DefaultLogsDirectory
{
get
{
var logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
if (Directory.Exists("/remotely-data"))
{
logsDir = "/remotely-data/logs";
}
return logsDir;
}
}
public async Task DeleteLogs()
{
var logsDir = GetLogsDirectory();
var files = Directory.GetFiles(logsDir);
if (!files.Any())
{
return;
}
await foreach (var file in files.ToAsyncEnumerable())
{
try
{
if (new FileInfo(file).LastWriteTime.Date == DateTime.Today)
{
continue;
}
File.Delete(file);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to delete log file: {file}. Message: {ex.Message}");
}
}
}
public async IAsyncEnumerable<string> GetLogs(
DateTimeOffset startDate,
DateTimeOffset endDate,
string messageFilter,
LogLevel? logLevelFilter)
{
var fromDate = startDate.UtcDateTime.Date;
var toDate = endDate.UtcDateTime.Date.AddDays(1);
var result = new StringBuilder();
var logsDir = GetLogsDirectory();
var files = Directory
.GetFiles(logsDir)
.Select(x => new FileInfo(x))
.Where(x =>
x.LastWriteTimeUtc >= fromDate &&
x.LastWriteTimeUtc <= toDate)
.OrderBy(x => x.LastWriteTimeUtc);
foreach (var file in files)
{
var linesAsync = GetLines(file, messageFilter, logLevelFilter);
await foreach (var line in linesAsync)
{
yield return line;
}
}
}
public string GetLogsDirectory()
{
var logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
if (Directory.Exists("/remotely-data"))
{
logsDir = "/remotely-data/logs";
}
return Directory.CreateDirectory(logsDir).FullName;
return Directory.CreateDirectory(DefaultLogsDirectory).FullName;
}
public async Task<FileInfo> ZipAllLogs()
@ -64,71 +142,6 @@ namespace Remotely.Server.Services
return new FileInfo(zipFilePath);
}
public async Task DeleteLogs()
{
var logsDir = GetLogsDirectory();
var files = Directory.GetFiles(logsDir);
if (!files.Any())
{
return;
}
await foreach (var file in files.ToAsyncEnumerable())
{
try
{
File.Delete(file);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to delete log file: {file}. Message: {ex.Message}");
try
{
Console.WriteLine("Attempting to zero out log contents.");
using var fs = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
fs.SetLength(0);
}
catch (Exception ex2)
{
Console.WriteLine($"Failed to clear log contents: {file}. Message: {ex2.Message}");
}
}
}
}
public async IAsyncEnumerable<string> GetLogs(
DateTimeOffset startDate,
DateTimeOffset endDate,
string messageFilter,
LogLevel? logLevelFilter)
{
var fromDate = startDate.UtcDateTime.Date;
var toDate = endDate.UtcDateTime.Date.AddDays(1);
var result = new StringBuilder();
var logsDir = GetLogsDirectory();
var files = Directory
.GetFiles(logsDir)
.Select(x => new FileInfo(x))
.Where(x =>
x.LastWriteTimeUtc >= fromDate &&
x.LastWriteTimeUtc <= toDate);
foreach (var file in files)
{
var linesAsync = GetLines(file, messageFilter, logLevelFilter);
await foreach (var line in linesAsync)
{
yield return line;
}
}
}
private async IAsyncEnumerable<string> GetLines(
FileInfo file,
string messageFilter,
@ -192,15 +205,5 @@ namespace Remotely.Server.Services
logLevel = default;
return false;
}
private static readonly ReadOnlyDictionary<string, LogLevel> _logLevelMap = new(new Dictionary<string, LogLevel>()
{
["[VRB]"] = LogLevel.Trace,
["[DBG]"] = LogLevel.Debug,
["[INF]"] = LogLevel.Information,
["[WRN]"] = LogLevel.Warning,
["[ERR]"] = LogLevel.Error,
["[FTL]"] = LogLevel.Critical
});
}
}

View File

@ -70,32 +70,24 @@ namespace Remotely.Server.Services.RcImplementations
return Task.CompletedTask;
}
switch (reason)
{
case SessionSwitchReasonEx.ConsoleDisconnect:
case SessionSwitchReasonEx.RemoteConnect:
case SessionSwitchReasonEx.RemoteDisconnect:
case SessionSwitchReasonEx.SessionLogoff:
case SessionSwitchReasonEx.SessionLock:
case SessionSwitchReasonEx.SessionRemoteControl:
return _serviceHub.Clients
.Client(ex.AgentConnectionId)
.SendAsync("RestartScreenCaster",
ex.ViewerList,
ex.UnattendedSessionId,
ex.AccessKey,
ex.UserConnectionId,
ex.RequesterUserName,
ex.OrganizationName,
ex.OrganizationId);
case SessionSwitchReasonEx.ConsoleConnect:
case SessionSwitchReasonEx.SessionUnlock:
case SessionSwitchReasonEx.SessionLogon:
default:
break;
}
_logger.LogDebug("Windows session changed during remote control. " +
"Reason: {reason}. " +
"Current Session ID: {sessionId}. " +
"Session Info: {@sesisonInfo}",
reason,
currentSessionId,
session);
return Task.CompletedTask;
return _serviceHub.Clients
.Client(ex.AgentConnectionId)
.SendAsync("RestartScreenCaster",
ex.ViewerList,
ex.UnattendedSessionId,
ex.AccessKey,
ex.UserConnectionId,
ex.RequesterUserName,
ex.OrganizationName,
ex.OrganizationId);
}
public Task RestartScreenCaster(RemoteControlSession session, HashSet<string> viewerList)

View File

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Shared.Primitives;
/// <summary>
/// An implementation of <see cref="IDisposable"/> that lets you provide a
/// callback, which will be invoked when the object is disposed.
/// </summary>
public sealed class CallbackDisposable : IDisposable
{
private readonly Action _callback;
private readonly Action<Exception> _exceptionHandler;
/// <summary>
/// Create anew instance where exceptions will be caught and suppressed.
/// </summary>
/// <param name="callback"></param>
public CallbackDisposable(Action callback)
: this(callback, (_) => { })
{
}
/// <summary>
/// Create a new instance where exceptions will be caught and passed to the supplied handler.
/// </summary>
/// <param name="callback"></param>
public CallbackDisposable(
Action callback,
Action<Exception> exceptionHandler)
{
_callback = callback;
_exceptionHandler = exceptionHandler;
}
public void Dispose()
{
try
{
_callback.Invoke();
}
catch (Exception ex)
{
_exceptionHandler.Invoke(ex);
}
}
}

View File

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Remotely.Shared.Primitives;
/// <summary>
/// An implementation of <see cref="IAsyncDisposable"/> that lets you provide a
/// callback, which will be invoked when the object is disposed.
/// </summary>
public sealed class CallbackDisposableAsync : IAsyncDisposable
{
private readonly Func<ValueTask> _callback;
private readonly Func<Exception, ValueTask> _exceptionHandler;
/// <summary>
/// Create anew instance where exceptions will be caught and suppressed.
/// </summary>
/// <param name="callback"></param>
public CallbackDisposableAsync(Func<ValueTask> callback)
: this(callback, (_) => ValueTask.CompletedTask)
{
}
/// <summary>
/// Create a new instance where exceptions will be caught and passed to the supplied handler.
/// </summary>
/// <param name="callback"></param>
public CallbackDisposableAsync(
Func<ValueTask> callback,
Func<Exception, ValueTask> exceptionHandler)
{
_callback = callback;
_exceptionHandler = exceptionHandler;
}
public ValueTask DisposeAsync()
{
try
{
return _callback.Invoke();
}
catch (Exception ex)
{
return _exceptionHandler.Invoke(ex);
}
}
}

View File

@ -188,6 +188,11 @@ namespace Remotely.Shared.Services
$"[Thread ID: {Environment.CurrentManagedThreadId}]\t" +
$"[{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.fff}]\t";
if (exception is not null)
{
entry += $"[Exception: {exception.GetType().Name}]\t";
}
entry += scopeStack.Any() ?
$"[{categoryName} => {string.Join(" => ", scopeStack)}]\t" :
$"[{categoryName}]\t";

@ -1 +1 @@
Subproject commit 51063504e723f2ee5a8fe4e672855d905a3f0eca
Subproject commit f7083733b34b4ad1a207f1103a4e35c0376411d1