feat: add logging feature with console, Godot and file outputs
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using Game.Features.Console.Application;
|
using Game.Features.Console.Application;
|
||||||
using Game.Features.Console.Contracts;
|
using Game.Features.Console.Contracts;
|
||||||
using Game.Features.Console.Infrastructure;
|
using Game.Features.Console.Infrastructure;
|
||||||
|
using Game.Features.Logging.Contracts;
|
||||||
using Game.Godot;
|
using Game.Godot;
|
||||||
using Game.Godot.Extensions;
|
using Game.Godot.Extensions;
|
||||||
using Game.Godot.Mappings;
|
using Game.Godot.Mappings;
|
||||||
@@ -8,7 +9,7 @@ using Godot;
|
|||||||
|
|
||||||
namespace Game.Features.Console.Godot;
|
namespace Game.Features.Console.Godot;
|
||||||
|
|
||||||
public partial class ConsoleController : CanvasLayer
|
public partial class ConsoleController : CanvasLayer, ILogTarget
|
||||||
{
|
{
|
||||||
[Export] public LineEdit CommandInput;
|
[Export] public LineEdit CommandInput;
|
||||||
[Export] public RichTextLabel OutputLog;
|
[Export] public RichTextLabel OutputLog;
|
||||||
@@ -33,16 +34,6 @@ public partial class ConsoleController : CanvasLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AppendLine(string line)
|
|
||||||
{
|
|
||||||
OutputLog.AppendText(line + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UseCommandRegistry(ICommandRegistry registry)
|
|
||||||
{
|
|
||||||
_commandExecute = new CommandExecute(registry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInputSubmitted(string text)
|
private void OnInputSubmitted(string text)
|
||||||
{
|
{
|
||||||
CommandInput.Clear();
|
CommandInput.Clear();
|
||||||
@@ -64,6 +55,21 @@ public partial class ConsoleController : CanvasLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AppendLine(string line)
|
||||||
|
{
|
||||||
|
OutputLog.AppendText(line + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UseCommandRegistry(ICommandRegistry registry)
|
||||||
|
{
|
||||||
|
_commandExecute = new CommandExecute(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteLogLine(string logLine)
|
||||||
|
{
|
||||||
|
AppendLine(logLine);
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
this.AssertRequiredExports();
|
this.AssertRequiredExports();
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Game.Features.Logging.Contracts;
|
||||||
|
|
||||||
|
public interface ILogTarget
|
||||||
|
{
|
||||||
|
void WriteLogLine(string logLine);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cm0ugu0l8td0e
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Game.Features.Logging.Contracts;
|
||||||
|
|
||||||
|
public interface ILogger
|
||||||
|
{
|
||||||
|
void Debug(string category, string message);
|
||||||
|
void Info(string category, string message);
|
||||||
|
void Warning(string category, string message);
|
||||||
|
void Error(string category, string message);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://0hjq84kko3td
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Game.Features.Logging.Contracts;
|
||||||
|
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://2ee58okj156n
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using Game.Features.Logging.Contracts;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Game.Features.Logging.Godot;
|
||||||
|
|
||||||
|
public sealed class FileLogger : ILogger, IDisposable
|
||||||
|
{
|
||||||
|
private const string LogPath = "user://logs/latest.log";
|
||||||
|
|
||||||
|
private readonly FileAccess _file;
|
||||||
|
|
||||||
|
public FileLogger()
|
||||||
|
{
|
||||||
|
DirAccess.MakeDirRecursiveAbsolute(LogPath.GetBaseDir());
|
||||||
|
_file = FileAccess.Open(LogPath, FileAccess.ModeFlags.Write);
|
||||||
|
|
||||||
|
if (_file is null) return;
|
||||||
|
|
||||||
|
_file.StoreLine($"=== Log started at {DateTime.Now:yyyy-MM-dd HH:mm:ss} ===");
|
||||||
|
_file.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Write(LogLevel level, string category, string message)
|
||||||
|
{
|
||||||
|
if (_file is null) return;
|
||||||
|
|
||||||
|
_file.StoreLine($"{DateTime.Now:HH:mm:ss} [{level}] [{category}] {message}");
|
||||||
|
_file.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Debug(string category, string message)
|
||||||
|
=> Write(LogLevel.Debug, category, message);
|
||||||
|
|
||||||
|
public void Info(string category, string message)
|
||||||
|
=> Write(LogLevel.Info, category, message);
|
||||||
|
|
||||||
|
public void Warning(string category, string message)
|
||||||
|
=> Write(LogLevel.Warning, category, message);
|
||||||
|
|
||||||
|
public void Error(string category, string message)
|
||||||
|
=> Write(LogLevel.Error, category, message);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_file?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://f45qb03i1dua
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Game.Features.Logging.Contracts;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Game.Features.Logging.Godot;
|
||||||
|
|
||||||
|
public sealed class GodotConsoleLogger : ILogger
|
||||||
|
{
|
||||||
|
private static string Format(LogLevel level, string category, string message)
|
||||||
|
=> $"[{level}] [{category}] {message}";
|
||||||
|
|
||||||
|
public void Debug(string category, string message)
|
||||||
|
=> GD.Print(Format(LogLevel.Debug, category, message));
|
||||||
|
|
||||||
|
public void Info(string category, string message)
|
||||||
|
=> GD.Print(Format(LogLevel.Info, category, message));
|
||||||
|
|
||||||
|
public void Warning(string category, string message)
|
||||||
|
=> GD.PushWarning(Format(LogLevel.Warning, category, message));
|
||||||
|
|
||||||
|
public void Error(string category, string message)
|
||||||
|
=> GD.PushError(Format(LogLevel.Error, category, message));
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://jyq0in2wtq02
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Game.Features.Logging.Contracts;
|
||||||
|
|
||||||
|
namespace Game.Features.Logging.Infrastructure;
|
||||||
|
|
||||||
|
public sealed class CompositeLogger(IReadOnlyList<ILogger> loggers) : ILogger
|
||||||
|
{
|
||||||
|
public void Debug(string category, string message)
|
||||||
|
{
|
||||||
|
foreach (ILogger logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Debug(category, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string category, string message)
|
||||||
|
{
|
||||||
|
foreach (ILogger logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Info(category, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warning(string category, string message)
|
||||||
|
{
|
||||||
|
foreach (ILogger logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Warning(category, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string category, string message)
|
||||||
|
{
|
||||||
|
foreach (ILogger logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Error(category, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cumunwgy6i71s
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Game.Features.Logging.Contracts;
|
||||||
|
|
||||||
|
namespace Game.Features.Logging.Infrastructure;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forwards log entries to an ILogTarget.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LogTargetLogger(ILogTarget target) : ILogger
|
||||||
|
{
|
||||||
|
public void Debug(string category, string message)
|
||||||
|
=> target.WriteLogLine(message);
|
||||||
|
|
||||||
|
public void Info(string category, string message)
|
||||||
|
=> target.WriteLogLine(message);
|
||||||
|
|
||||||
|
public void Warning(string category, string message)
|
||||||
|
=> target.WriteLogLine(message);
|
||||||
|
|
||||||
|
public void Error(string category, string message)
|
||||||
|
=> target.WriteLogLine(message);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bxnu6wg0qe03r
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using Game.Features.Console.Application.Commands;
|
using Game.Features.Console.Application.Commands;
|
||||||
using Game.Features.Console.Godot;
|
using Game.Features.Console.Godot;
|
||||||
using Game.Features.Console.Infrastructure;
|
using Game.Features.Console.Infrastructure;
|
||||||
|
using Game.Features.Logging.Godot;
|
||||||
|
using Game.Features.Logging.Infrastructure;
|
||||||
using Game.Features.Multiplayer.Godot;
|
using Game.Features.Multiplayer.Godot;
|
||||||
using Game.Features.Players.Godot;
|
using Game.Features.Players.Godot;
|
||||||
using Game.Godot.Extensions;
|
using Game.Godot.Extensions;
|
||||||
@@ -15,6 +17,19 @@ public partial class GameBootstrap : Node
|
|||||||
[Export] public PlayerController Player;
|
[Export] public PlayerController Player;
|
||||||
[Export] public MultiplayerController MultiplayerController;
|
[Export] public MultiplayerController MultiplayerController;
|
||||||
|
|
||||||
|
private FileLogger _fileLogger;
|
||||||
|
private CompositeLogger _logger;
|
||||||
|
|
||||||
|
private void InitializeLogging()
|
||||||
|
{
|
||||||
|
_fileLogger = new FileLogger();
|
||||||
|
_logger = new CompositeLogger([
|
||||||
|
new GodotConsoleLogger(),
|
||||||
|
new LogTargetLogger(Console),
|
||||||
|
_fileLogger
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterConsoleCommands()
|
private void RegisterConsoleCommands()
|
||||||
{
|
{
|
||||||
CommandRegistry registry = new();
|
CommandRegistry registry = new();
|
||||||
@@ -31,11 +46,11 @@ public partial class GameBootstrap : Node
|
|||||||
private void InitializeMultiplayer()
|
private void InitializeMultiplayer()
|
||||||
{
|
{
|
||||||
MultiplayerController.UsePeerFactory(new EnetMultiplayerPeerFactory());
|
MultiplayerController.UsePeerFactory(new EnetMultiplayerPeerFactory());
|
||||||
MultiplayerController.Connected += () => Console.AppendLine("Connected successfully.");
|
MultiplayerController.Connected += () => _logger.Info("Multiplayer", "Connected successfully.");
|
||||||
MultiplayerController.ConnectionFailed += () => Console.AppendLine("Connection failed.");
|
MultiplayerController.ConnectionFailed += () => _logger.Warning("Multiplayer", "Connection failed.");
|
||||||
MultiplayerController.Disconnected += () => Console.AppendLine("Server disconnected.");
|
MultiplayerController.Disconnected += () => _logger.Warning("Multiplayer", "Server disconnected.");
|
||||||
MultiplayerController.PeerJoined += id => Console.AppendLine($"Peer {id} joined.");
|
MultiplayerController.PeerJoined += id => _logger.Info("Multiplayer", $"Peer {id} joined.");
|
||||||
MultiplayerController.PeerLeft += id => Console.AppendLine($"Peer {id} left.");
|
MultiplayerController.PeerLeft += id => _logger.Info("Multiplayer", $"Peer {id} left.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
@@ -43,6 +58,8 @@ public partial class GameBootstrap : Node
|
|||||||
this.AssertRequiredExports();
|
this.AssertRequiredExports();
|
||||||
|
|
||||||
MouseCapture.Capture();
|
MouseCapture.Capture();
|
||||||
|
|
||||||
|
InitializeLogging();
|
||||||
InitializeMultiplayer();
|
InitializeMultiplayer();
|
||||||
RegisterConsoleCommands();
|
RegisterConsoleCommands();
|
||||||
}
|
}
|
||||||
@@ -54,4 +71,9 @@ public partial class GameBootstrap : Node
|
|||||||
MouseCapture.Toggle();
|
MouseCapture.Toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void _ExitTree()
|
||||||
|
{
|
||||||
|
_fileLogger?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user