From bf15fad9cec1c510f409b2615f700c373e89b379 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Wed, 29 Oct 2025 01:50:42 +0100 Subject: [PATCH] Add logging functionality with ILogger interface and implementations --- GameCore/Combat/WeaponSystem.cs | 1 + GameCore/Config/SimulationConfig.cs | 1 + GameCore/ECS/World.cs | 4 +- GameCore/Logging/CompositeLogger.cs | 33 ++++++++++++ GameCore/Logging/FileLogger.cs | 73 ++++++++++++++++++++++++++ GameCore/Logging/Interfaces/ILogger.cs | 9 ++++ GameCore/Logging/LogLevel.cs | 9 ++++ GameCore/Logging/NullLogger.cs | 22 ++++++++ 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 GameCore/Logging/CompositeLogger.cs create mode 100644 GameCore/Logging/FileLogger.cs create mode 100644 GameCore/Logging/Interfaces/ILogger.cs create mode 100644 GameCore/Logging/LogLevel.cs create mode 100644 GameCore/Logging/NullLogger.cs diff --git a/GameCore/Combat/WeaponSystem.cs b/GameCore/Combat/WeaponSystem.cs index 89c1a83..8892bb5 100644 --- a/GameCore/Combat/WeaponSystem.cs +++ b/GameCore/Combat/WeaponSystem.cs @@ -34,6 +34,7 @@ public class WeaponSystem : ISystem if (!cost.CanAfford(context)) { canFire = false; + world.Logger.Warn("Weapon fire failed due to insufficient resources."); world.PublishEvent(new WeaponFireFailedEvent()); break; } diff --git a/GameCore/Config/SimulationConfig.cs b/GameCore/Config/SimulationConfig.cs index 6d66c39..e88be4a 100644 --- a/GameCore/Config/SimulationConfig.cs +++ b/GameCore/Config/SimulationConfig.cs @@ -3,4 +3,5 @@ namespace GameCore.Config; public class SimulationConfig { public float GravityStrength { get; set; } = 9.81f; + public bool LoggingEnabled { get; set; } = true; } \ No newline at end of file diff --git a/GameCore/ECS/World.cs b/GameCore/ECS/World.cs index 5dd5936..3281994 100644 --- a/GameCore/ECS/World.cs +++ b/GameCore/ECS/World.cs @@ -3,6 +3,7 @@ using GameCore.ECS.Interfaces; using GameCore.Events; using GameCore.Events.Interfaces; using GameCore.Input.Interfaces; +using GameCore.Logging.Interfaces; namespace GameCore.ECS; @@ -11,7 +12,7 @@ namespace GameCore.ECS; /// It manages all entities, components, and systems, and orchestrates the main game loop. /// This class is the primary point of interaction for the Engine/Presentation layer. /// -public class World(IInputService inputService, IWorldQuery worldQuery, SimulationConfig config) +public class World(IInputService inputService, IWorldQuery worldQuery, SimulationConfig config, ILogger logger) { private readonly Dictionary> _componentsByEntityId = new(); private readonly EventBus _eventBus = new(); @@ -19,6 +20,7 @@ public class World(IInputService inputService, IWorldQuery worldQuery, Simulatio public readonly SimulationConfig Config = config; public readonly IInputService InputService = inputService; + public readonly ILogger Logger = logger; public readonly IWorldQuery WorldQuery = worldQuery; private int _nextEntityId; diff --git a/GameCore/Logging/CompositeLogger.cs b/GameCore/Logging/CompositeLogger.cs new file mode 100644 index 0000000..688535e --- /dev/null +++ b/GameCore/Logging/CompositeLogger.cs @@ -0,0 +1,33 @@ +using GameCore.Logging.Interfaces; + +namespace GameCore.Logging; + +public class CompositeLogger : ILogger +{ + private readonly List _loggers = []; + + public CompositeLogger(params ILogger[] loggers) + { + _loggers.AddRange(loggers); + } + + public void Debug(string message) + { + foreach (var logger in _loggers) logger.Debug(message); + } + + public void Info(string message) + { + foreach (var logger in _loggers) logger.Info(message); + } + + public void Warn(string message) + { + foreach (var logger in _loggers) logger.Warn(message); + } + + public void Error(string message, Exception ex = null) + { + foreach (var logger in _loggers) logger.Error(message, ex); + } +} \ No newline at end of file diff --git a/GameCore/Logging/FileLogger.cs b/GameCore/Logging/FileLogger.cs new file mode 100644 index 0000000..36cd76c --- /dev/null +++ b/GameCore/Logging/FileLogger.cs @@ -0,0 +1,73 @@ +using System.Text; +using GameCore.Logging.Interfaces; + +namespace GameCore.Logging; + +public class FileLogger : ILogger, IDisposable +{ + private readonly object _lock = new(); + private readonly LogLevel _minLogLevel = LogLevel.Debug; + private readonly StreamWriter _streamWriter; + + public FileLogger(string filePath, LogLevel minLogLevel = LogLevel.Debug) + { + _minLogLevel = minLogLevel; + + var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.Read); + _streamWriter = new StreamWriter(stream, Encoding.UTF8); + _streamWriter.AutoFlush = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Debug(string message) + { + Log(LogLevel.Debug, message); + } + + public void Info(string message) + { + Log(LogLevel.Info, message); + } + + public void Warn(string message) + { + Log(LogLevel.Warn, message); + } + + public void Error(string message, Exception? ex = null) + { + Log(LogLevel.Error, message, ex); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + lock (_lock) + { + _streamWriter?.Dispose(); + } + } + + ~FileLogger() + { + Dispose(false); + } + + private void Log(LogLevel level, string message, Exception? ex = null) + { + if (level < _minLogLevel) return; + + var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] [{level.ToString().ToUpper()}] {message}"; + + lock (_lock) + { + _streamWriter.WriteLine(logMessage); + if (ex != null) _streamWriter.WriteLine(ex.ToString()); + } + } +} \ No newline at end of file diff --git a/GameCore/Logging/Interfaces/ILogger.cs b/GameCore/Logging/Interfaces/ILogger.cs new file mode 100644 index 0000000..48a6d69 --- /dev/null +++ b/GameCore/Logging/Interfaces/ILogger.cs @@ -0,0 +1,9 @@ +namespace GameCore.Logging.Interfaces; + +public interface ILogger +{ + void Debug(string message); + void Info(string message); + void Warn(string message); + void Error(string message, Exception? ex = null); +} \ No newline at end of file diff --git a/GameCore/Logging/LogLevel.cs b/GameCore/Logging/LogLevel.cs new file mode 100644 index 0000000..bff090f --- /dev/null +++ b/GameCore/Logging/LogLevel.cs @@ -0,0 +1,9 @@ +namespace GameCore.Logging; + +public enum LogLevel +{ + Debug, + Info, + Warn, + Error +} \ No newline at end of file diff --git a/GameCore/Logging/NullLogger.cs b/GameCore/Logging/NullLogger.cs new file mode 100644 index 0000000..8ca3e2d --- /dev/null +++ b/GameCore/Logging/NullLogger.cs @@ -0,0 +1,22 @@ +using GameCore.Logging.Interfaces; + +namespace GameCore.Logging; + +public class NullLogger : ILogger +{ + public void Debug(string message) + { + } + + public void Info(string message) + { + } + + public void Warn(string message) + { + } + + public void Error(string message, Exception ex = null) + { + } +} \ No newline at end of file