mirror of
https://github.com/Stone-Red-Code/RemSox.git
synced 2026-09-04 00:56:19 +02:00
Add logging system
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
global using Sys = Cosmos.Kernel.System;
|
global using Sys = Cosmos.Kernel.System;
|
||||||
|
|
||||||
using Cosmos.Kernel.Core;
|
using Cosmos.Kernel.Core;
|
||||||
|
using RemSox.Logging;
|
||||||
using RemSox.Processes;
|
using RemSox.Processes;
|
||||||
using RemSox.Processing;
|
using RemSox.Processing;
|
||||||
using RemSox.Processing.IPC;
|
using RemSox.Processing.IPC;
|
||||||
@@ -30,7 +30,8 @@ public class Kernel : Sys.Kernel
|
|||||||
new ListProcessesCommand(),
|
new ListProcessesCommand(),
|
||||||
new StopProcessCommand(),
|
new StopProcessCommand(),
|
||||||
new StartGuiCommand(),
|
new StartGuiCommand(),
|
||||||
new StopGuiCommand()
|
new StopGuiCommand(),
|
||||||
|
new ViewProcessLogs()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Sys.Mouse.MouseManager.Initialize();
|
Sys.Mouse.MouseManager.Initialize();
|
||||||
@@ -62,19 +63,6 @@ public class Kernel : Sys.Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public class TestAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "default";
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test(Name = "HelloCosmos")]
|
|
||||||
public class TestClass
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TestProcess() : Process("Test Process")
|
public class TestProcess() : Process("Test Process")
|
||||||
{
|
{
|
||||||
internal override void Run(string[] args)
|
internal override void Run(string[] args)
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RemSox.Logging
|
||||||
|
{
|
||||||
|
public interface ILogger
|
||||||
|
{
|
||||||
|
void Log(string message, LogSeverity severity);
|
||||||
|
|
||||||
|
void LogInfo(string message);
|
||||||
|
|
||||||
|
void LogError(string message);
|
||||||
|
|
||||||
|
void LogWarning(string message);
|
||||||
|
|
||||||
|
IEnumerable<LogEntry> GetLogs(int? count = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace RemSox.Logging
|
||||||
|
{
|
||||||
|
public class InMemoryLogger : ILogger
|
||||||
|
{
|
||||||
|
readonly ConcurrentBag<LogEntry> logs = [];
|
||||||
|
|
||||||
|
public void Log(string message, LogSeverity severity)
|
||||||
|
{
|
||||||
|
logs.Add(new LogEntry(message, severity, DateTimeOffset.UtcNow));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogError(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInfo(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogWarning(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<LogEntry> GetLogs(int? count = null)
|
||||||
|
{
|
||||||
|
IEnumerable<LogEntry> orderedLogs = logs.OrderBy(l => l.Timestamp);
|
||||||
|
return count.HasValue ? orderedLogs.TakeLast(count.Value) : orderedLogs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RemSox.Logging
|
||||||
|
{
|
||||||
|
public record LogEntry(string Message, LogSeverity Severity, DateTimeOffset Timestamp);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RemSox.Logging
|
||||||
|
{
|
||||||
|
public enum LogSeverity
|
||||||
|
{
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace RemSox.Logging
|
||||||
|
{
|
||||||
|
public class ProxyLogger(IEnumerable<ILogger> loggers) : ILogger
|
||||||
|
{
|
||||||
|
public void Log(string message, LogSeverity severity)
|
||||||
|
{
|
||||||
|
foreach (var logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Log(message, severity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogError(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInfo(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogWarning(string message)
|
||||||
|
{
|
||||||
|
Log(message, LogSeverity.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<LogEntry> GetLogs(int? count = null)
|
||||||
|
{
|
||||||
|
return loggers.SelectMany(logger => logger.GetLogs(count / loggers.Count())).OrderBy(entry => entry.Timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
|
using RemSox.Logging;
|
||||||
using RemSox.Processing.IPC;
|
using RemSox.Processing.IPC;
|
||||||
|
|
||||||
namespace RemSox.Processing;
|
namespace RemSox.Processing;
|
||||||
|
|
||||||
public abstract class Process(string name)
|
public abstract class Process(string name)
|
||||||
{
|
{
|
||||||
public int Id { get; init; }
|
public int Id { get; init; } // Will be set by ProcessManager when the process is spawned
|
||||||
|
|
||||||
|
public ILogger Logger { protected get; init; } = null!; // Will be set by ProcessManager when the process is spawned
|
||||||
|
|
||||||
public string Name { get; set; } = name;
|
public string Name { get; set; } = name;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using RemSox.Logging;
|
||||||
using RemSox.UI.GUI.Windows;
|
using RemSox.UI.GUI.Windows;
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -10,22 +11,34 @@ public static class ProcessManager
|
|||||||
|
|
||||||
private static readonly ConcurrentDictionary<Type, ConcurrentHashSet<int>> processesByType = new();
|
private static readonly ConcurrentDictionary<Type, ConcurrentHashSet<int>> processesByType = new();
|
||||||
|
|
||||||
|
private static readonly InMemoryLogger logger = new();
|
||||||
|
private static readonly ConcurrentDictionary<int, InMemoryLogger> processLoggers = new();
|
||||||
|
|
||||||
private static int nextProcessId = 0;
|
private static int nextProcessId = 0;
|
||||||
|
|
||||||
private static int nextSystemProcessId = -1;
|
private static int nextSystemProcessId = -1;
|
||||||
|
|
||||||
public static int SpawnProcess<T>(string[]? args = null) where T : Process, new()
|
public static int SpawnProcess<T>(string[]? args = null) where T : Process, new()
|
||||||
{
|
{
|
||||||
|
logger.Log($"Attempting to spawn process of type {typeof(T).Name}...", LogSeverity.Info);
|
||||||
|
|
||||||
if (ProcessManifest.HasFlag<T>(ProcessManifest.ProcessManifestFlags.Singleton) && IsProcessRunning<T>())
|
if (ProcessManifest.HasFlag<T>(ProcessManifest.ProcessManifestFlags.Singleton) && IsProcessRunning<T>())
|
||||||
{
|
{
|
||||||
|
logger.Log($"Cannot spawn process of type {typeof(T).Name} because it is marked as a singleton and an instance is already running.", LogSeverity.Warning);
|
||||||
throw new InvalidOperationException($"An instance of process type {typeof(T).Name} is already running.");
|
throw new InvalidOperationException($"An instance of process type {typeof(T).Name} is already running.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = ProcessManifest.HasFlag<T>(ProcessManifest.ProcessManifestFlags.System) ? GetNextSystemProcessId() : GetNextProcessId();
|
int id = ProcessManifest.HasFlag<T>(ProcessManifest.ProcessManifestFlags.System) ? GetNextSystemProcessId() : GetNextProcessId();
|
||||||
|
|
||||||
|
InMemoryLogger processLogger = new();
|
||||||
|
ProxyLogger proxyLogger = new([logger, processLogger]);
|
||||||
|
|
||||||
|
processLoggers.TryAdd(id, processLogger);
|
||||||
|
|
||||||
T process = new()
|
T process = new()
|
||||||
{
|
{
|
||||||
Id = id
|
Id = id,
|
||||||
|
Logger = proxyLogger
|
||||||
};
|
};
|
||||||
|
|
||||||
processesByType.AddOrUpdate(typeof(T), _ => [id], (_, set) =>
|
processesByType.AddOrUpdate(typeof(T), _ => [id], (_, set) =>
|
||||||
@@ -40,6 +53,10 @@ public static class ProcessManager
|
|||||||
{
|
{
|
||||||
process.Run(args ?? []);
|
process.Run(args ?? []);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Log($"Process {process.Name} (ID: {process.Id}) terminated with an exception: {ex}", LogSeverity.Error);
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
processes.TryRemove(id, out _);
|
processes.TryRemove(id, out _);
|
||||||
@@ -52,12 +69,18 @@ public static class ProcessManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowManager.CloseWindowsForProcess(id);
|
WindowManager.CloseWindowsForProcess(id);
|
||||||
|
|
||||||
|
logger.Log($"Process {process.Name} (ID: {process.Id}) has stopped.", LogSeverity.Info);
|
||||||
|
|
||||||
|
processLoggers.TryRemove(id, out _);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
processes.TryAdd(id, (process, thread));
|
processes.TryAdd(id, (process, thread));
|
||||||
thread.Start();
|
thread.Start();
|
||||||
|
|
||||||
|
logger.Log($"Spawned process {process.Name} of type {typeof(T).Name} with ID {id}.", LogSeverity.Info);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +91,7 @@ public static class ProcessManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Log($"Requesting stop of process {entry.Process.Name} (ID: {entry.Process.Id}).", LogSeverity.Info);
|
||||||
entry.Process.RequestStop();
|
entry.Process.RequestStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +102,10 @@ public static class ProcessManager
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Log($"Requesting stop of process {entry.Process.Name} (ID: {entry.Process.Id}).", LogSeverity.Info);
|
||||||
entry.Process.RequestStop();
|
entry.Process.RequestStop();
|
||||||
|
|
||||||
|
logger.Log($"Waiting for process {entry.Process.Name} (ID: {entry.Process.Id}) to stop.", LogSeverity.Info);
|
||||||
await Task.Run(() => entry.Thread.Join());
|
await Task.Run(() => entry.Thread.Join());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +168,21 @@ public static class ProcessManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<LogEntry> GetLogs(int? count = null)
|
||||||
|
{
|
||||||
|
return logger.GetLogs(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<LogEntry> GetProcessLogs(int processId, int? count = null)
|
||||||
|
{
|
||||||
|
if (processLoggers.TryGetValue(processId, out InMemoryLogger? processLogger))
|
||||||
|
{
|
||||||
|
return processLogger.GetLogs(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
private static int GetNextProcessId()
|
private static int GetNextProcessId()
|
||||||
{
|
{
|
||||||
return nextProcessId++;
|
return nextProcessId++;
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using RemSox.Logging;
|
||||||
|
using RemSox.Processing;
|
||||||
|
|
||||||
|
namespace RemSox.UI.CLI.Commands
|
||||||
|
{
|
||||||
|
public class ViewProcessLogs : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "logs";
|
||||||
|
|
||||||
|
public string Description => "View logs for a process";
|
||||||
|
|
||||||
|
public void Execute(string? arguments, Action<string> printLine)
|
||||||
|
{
|
||||||
|
if (int.TryParse(arguments, out int processId))
|
||||||
|
{
|
||||||
|
IEnumerable<LogEntry> logs = ProcessManager.GetProcessLogs(processId);
|
||||||
|
PrintLogs(logs, printLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IEnumerable<LogEntry> logs = ProcessManager.GetLogs();
|
||||||
|
PrintLogs(logs, printLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintLogs(IEnumerable<LogEntry> logs, Action<string> printLine)
|
||||||
|
{
|
||||||
|
if (!logs.Any())
|
||||||
|
{
|
||||||
|
printLine("No logs found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (LogEntry log in logs)
|
||||||
|
{
|
||||||
|
printLine($"[{log.Timestamp:HH:mm:ss}] [{log.Severity}] {log.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user