diff --git a/Server/Pages/ServerLogs.razor b/Server/Pages/ServerLogs.razor index 538a1177..5f6156e8 100644 --- a/Server/Pages/ServerLogs.razor +++ b/Server/Pages/ServerLogs.razor @@ -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")) diff --git a/Server/Program.cs b/Server/Program.cs index b1138051..b40e5095 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -205,7 +205,7 @@ services.AddScoped(); services.AddScoped(); services.AddSingleton(); services.AddSingleton(); -services.AddSingleton(LogsManager.Default); +services.AddSingleton(); 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) { diff --git a/Server/Services/LoaderService.cs b/Server/Services/LoaderService.cs index 3222eef1..37d87437 100644 --- a/Server/Services/LoaderService.cs +++ b/Server/Services/LoaderService.cs @@ -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; diff --git a/Server/Services/LogsManager.cs b/Server/Services/LogsManager.cs index e69f33c2..4dc0fba9 100644 --- a/Server/Services/LogsManager.cs +++ b/Server/Services/LogsManager.cs @@ -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 ZipAllLogs(); Task DeleteLogs(); + IAsyncEnumerable GetLogs( - DateTimeOffset startDate, + DateTimeOffset startDate, DateTimeOffset endDate, string messageFilter, LogLevel? logLevelFilter); + + string GetLogsDirectory(); + Task ZipAllLogs(); } public class LogsManager : ILogsManager { - public static LogsManager Default { get; } = new(); + private static readonly ReadOnlyDictionary _logLevelMap = new(new Dictionary() + { + ["[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 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 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 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 GetLines( FileInfo file, string messageFilter, @@ -192,15 +205,5 @@ namespace Remotely.Server.Services logLevel = default; return false; } - - private static readonly ReadOnlyDictionary _logLevelMap = new(new Dictionary() - { - ["[VRB]"] = LogLevel.Trace, - ["[DBG]"] = LogLevel.Debug, - ["[INF]"] = LogLevel.Information, - ["[WRN]"] = LogLevel.Warning, - ["[ERR]"] = LogLevel.Error, - ["[FTL]"] = LogLevel.Critical - }); } } diff --git a/Server/Services/RcImplementations/HubEventHandler.cs b/Server/Services/RcImplementations/HubEventHandler.cs index e98381bb..456f14a1 100644 --- a/Server/Services/RcImplementations/HubEventHandler.cs +++ b/Server/Services/RcImplementations/HubEventHandler.cs @@ -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 viewerList) diff --git a/Shared/Primitives/CallbackDisposable.cs b/Shared/Primitives/CallbackDisposable.cs deleted file mode 100644 index 3e286e6d..00000000 --- a/Shared/Primitives/CallbackDisposable.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Remotely.Shared.Primitives; - - -/// -/// An implementation of that lets you provide a -/// callback, which will be invoked when the object is disposed. -/// -public sealed class CallbackDisposable : IDisposable -{ - private readonly Action _callback; - private readonly Action _exceptionHandler; - - /// - /// Create anew instance where exceptions will be caught and suppressed. - /// - /// - public CallbackDisposable(Action callback) - : this(callback, (_) => { }) - { - } - - /// - /// Create a new instance where exceptions will be caught and passed to the supplied handler. - /// - /// - public CallbackDisposable( - Action callback, - Action exceptionHandler) - { - _callback = callback; - _exceptionHandler = exceptionHandler; - } - - - public void Dispose() - { - try - { - _callback.Invoke(); - } - catch (Exception ex) - { - _exceptionHandler.Invoke(ex); - } - } -} diff --git a/Shared/Primitives/CallbackDisposableAsync.cs b/Shared/Primitives/CallbackDisposableAsync.cs deleted file mode 100644 index fe01ca8f..00000000 --- a/Shared/Primitives/CallbackDisposableAsync.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Remotely.Shared.Primitives; - - -/// -/// An implementation of that lets you provide a -/// callback, which will be invoked when the object is disposed. -/// -public sealed class CallbackDisposableAsync : IAsyncDisposable -{ - private readonly Func _callback; - private readonly Func _exceptionHandler; - - /// - /// Create anew instance where exceptions will be caught and suppressed. - /// - /// - public CallbackDisposableAsync(Func callback) - : this(callback, (_) => ValueTask.CompletedTask) - { - } - - /// - /// Create a new instance where exceptions will be caught and passed to the supplied handler. - /// - /// - public CallbackDisposableAsync( - Func callback, - Func exceptionHandler) - { - _callback = callback; - _exceptionHandler = exceptionHandler; - } - - - public ValueTask DisposeAsync() - { - try - { - return _callback.Invoke(); - } - catch (Exception ex) - { - return _exceptionHandler.Invoke(ex); - } - } -} diff --git a/Shared/Services/FileLogger.cs b/Shared/Services/FileLogger.cs index 01d5542d..5bbb06c7 100644 --- a/Shared/Services/FileLogger.cs +++ b/Shared/Services/FileLogger.cs @@ -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"; diff --git a/submodules/Immense.RemoteControl b/submodules/Immense.RemoteControl index 51063504..f7083733 160000 --- a/submodules/Immense.RemoteControl +++ b/submodules/Immense.RemoteControl @@ -1 +1 @@ -Subproject commit 51063504e723f2ee5a8fe4e672855d905a3f0eca +Subproject commit f7083733b34b4ad1a207f1103a4e35c0376411d1