From 8f1b013c0a936aecc1dd33a55619a3d23a1d0207 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Thu, 13 Apr 2023 17:43:26 -0500 Subject: [PATCH] Functional server start/stop with log output --- LANCommander/Data/Models/Server.cs | 14 ++--- LANCommander/Hubs/LoggingHub.cs | 12 ++++ LANCommander/LANCommander.csproj | 3 + LANCommander/Logging/LoggingHubConnection.cs | 59 +++++++++++++++++++ LANCommander/Logging/LoggingHubTarget.cs | 37 ++++++++++++ LANCommander/Pages/Servers/Index.razor | 6 ++ LANCommander/Pages/Servers/Logs.razor | 42 +++++++++++++ LANCommander/Program.cs | 6 ++ LANCommander/Services/ServerProcessService.cs | 28 ++++++++- LANCommander/nlog.config | 33 +++++++++++ 10 files changed, 231 insertions(+), 9 deletions(-) create mode 100644 LANCommander/Hubs/LoggingHub.cs create mode 100644 LANCommander/Logging/LoggingHubConnection.cs create mode 100644 LANCommander/Logging/LoggingHubTarget.cs create mode 100644 LANCommander/Pages/Servers/Logs.razor create mode 100644 LANCommander/nlog.config diff --git a/LANCommander/Data/Models/Server.cs b/LANCommander/Data/Models/Server.cs index ff778cd..f9581a1 100644 --- a/LANCommander/Data/Models/Server.cs +++ b/LANCommander/Data/Models/Server.cs @@ -2,13 +2,13 @@ { public class Server : BaseModel { - public string Name { get; set; } - public string Path { get; set; } - public string Arguments { get; set; } - public string WorkingDirectory { get; set; } - - public string OnStartScriptPath { get; set; } - public string OnStopScriptPath { get; set; } + public string Name { get; set; } = ""; + public string Path { get; set; } = ""; + public string Arguments { get; set; } = ""; + public string WorkingDirectory { get; set; } = ""; + + public string OnStartScriptPath { get; set; } = ""; + public string OnStopScriptPath { get; set; } = ""; public bool Autostart { get; set; } } diff --git a/LANCommander/Hubs/LoggingHub.cs b/LANCommander/Hubs/LoggingHub.cs new file mode 100644 index 0000000..bf0a489 --- /dev/null +++ b/LANCommander/Hubs/LoggingHub.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.SignalR; + +namespace LANCommander.Hubs +{ + public class LoggingHub : Hub + { + public void Log(string logMessage) + { + Clients.All.SendAsync("Log", logMessage); + } + } +} diff --git a/LANCommander/LANCommander.csproj b/LANCommander/LANCommander.csproj index 9f89547..7942b71 100644 --- a/LANCommander/LANCommander.csproj +++ b/LANCommander/LANCommander.csproj @@ -21,6 +21,7 @@ + @@ -34,6 +35,8 @@ + + diff --git a/LANCommander/Logging/LoggingHubConnection.cs b/LANCommander/Logging/LoggingHubConnection.cs new file mode 100644 index 0000000..087b203 --- /dev/null +++ b/LANCommander/Logging/LoggingHubConnection.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.SignalR.Client; + +namespace LANCommander.Logging +{ + public class LoggingHubConnection : IAsyncDisposable + { + private HubConnection? HubConnection; + private string HubUrl; + + public LoggingHubConnection(string hubUrl) + { + HubUrl = hubUrl; + } + + public async Task Log(string logMessage) + { + await EnsureConnection(); + + if (HubConnection != null) + await HubConnection.SendAsync("Log", logMessage); + } + + public async Task EnsureConnection() + { + if (HubConnection == null) + { + HubConnection = new HubConnectionBuilder() + .WithUrl(HubUrl) + .Build(); + + await HubConnection.StartAsync(); + } + else if (HubConnection.State == HubConnectionState.Disconnected) + { + await HubConnection.StartAsync(); + } + } + + public async ValueTask DisposeAsync() + { + if (HubConnection != null) + { + try + { + await HubConnection.StopAsync(); + await HubConnection.DisposeAsync(); + } + catch (Exception ex) + { + NLog.Common.InternalLogger.Error(ex, "Exception in LoggingHubConnection.DisposeAsync"); + } + finally + { + HubConnection = null; + } + } + } + } +} diff --git a/LANCommander/Logging/LoggingHubTarget.cs b/LANCommander/Logging/LoggingHubTarget.cs new file mode 100644 index 0000000..4061be6 --- /dev/null +++ b/LANCommander/Logging/LoggingHubTarget.cs @@ -0,0 +1,37 @@ +using NLog; +using NLog.Config; +using NLog.Targets; + +namespace LANCommander.Logging +{ + [Target("LoggingHub")] + public class LoggingHubTarget : AsyncTaskTarget + { + private LoggingHubConnection? Connection; + + [RequiredParameter] + public string HubUrl { get; set; } + + protected override void InitializeTarget() + { + Connection = new LoggingHubConnection(HubUrl); + } + + protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token) + { + string message = Layout.Render(logEvent); + + if (Connection != null) + await Connection.Log(message); + } + + protected override async void CloseTarget() + { + if (Connection != null) + { + await Connection.DisposeAsync(); + Connection = null; + } + } + } +} diff --git a/LANCommander/Pages/Servers/Index.razor b/LANCommander/Pages/Servers/Index.razor index 7a132d4..2deeefb 100644 --- a/LANCommander/Pages/Servers/Index.razor +++ b/LANCommander/Pages/Servers/Index.razor @@ -24,6 +24,7 @@ + @@ -63,6 +64,11 @@ NavigationManager.NavigateTo($"/Servers/{server.Id}/Edit"); } + private void Logs(Server server) + { + NavigationManager.NavigateTo($"/Servers/{server.Id}/Logs"); + } + private void Start(Server server) { ServerProcessService.StartServer(server); diff --git a/LANCommander/Pages/Servers/Logs.razor b/LANCommander/Pages/Servers/Logs.razor new file mode 100644 index 0000000..223e207 --- /dev/null +++ b/LANCommander/Pages/Servers/Logs.razor @@ -0,0 +1,42 @@ +@page "/Servers/{id:guid}/Logs" +@using Microsoft.AspNetCore.SignalR.Client +@attribute [Authorize] +@inject ServerService ServerService +@inject ServerProcessService ServerProcessService +@inject NavigationManager NavigationManager +@implements IAsyncDisposable + +
+    @foreach (var message in Messages)
+    {
+        @message 
+ } +
+ +@code { + [Parameter] public Guid Id { get; set; } + + HubConnection? HubConnection; + List Messages = new List(); + + protected override async Task OnInitializedAsync() + { + HubConnection = new HubConnectionBuilder() + .WithUrl(NavigationManager.ToAbsoluteUri("/hubs/logging")) + .Build(); + + HubConnection.On("Log", (message) => + { + Messages.Add(message); + InvokeAsync(StateHasChanged); + }); + + await HubConnection.StartAsync(); + } + + public async ValueTask DisposeAsync() + { + if (HubConnection is not null) + await HubConnection.DisposeAsync(); + } +} diff --git a/LANCommander/Program.cs b/LANCommander/Program.cs index 7fe2541..149bd1a 100644 --- a/LANCommander/Program.cs +++ b/LANCommander/Program.cs @@ -1,12 +1,14 @@ using BeaconLib; using LANCommander.Data; using LANCommander.Data.Models; +using LANCommander.Hubs; using LANCommander.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; +using NLog.Web; using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -114,6 +116,8 @@ builder.WebHost.UseKestrel(options => options.Limits.MaxRequestBodySize = 1024 * 1024 * 150; }); +builder.Host.UseNLog(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -140,6 +144,8 @@ app.UseAuthorization(); app.UseMvcWithDefaultRoute(); +app.MapHub("/hubs/logging"); + app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); diff --git a/LANCommander/Services/ServerProcessService.cs b/LANCommander/Services/ServerProcessService.cs index 136a489..0cb6f2c 100644 --- a/LANCommander/Services/ServerProcessService.cs +++ b/LANCommander/Services/ServerProcessService.cs @@ -1,10 +1,13 @@ using LANCommander.Data.Models; +using NLog; using System.Diagnostics; namespace LANCommander.Services { public class ServerProcessService { + private readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public Dictionary Processes = new Dictionary(); public Dictionary Threads { get; set; } = new Dictionary(); @@ -15,19 +18,40 @@ namespace LANCommander.Services process.StartInfo.FileName = server.Path; process.StartInfo.WorkingDirectory = server.WorkingDirectory; process.StartInfo.Arguments = server.Arguments; - process.StartInfo.UseShellExecute = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.EnableRaisingEvents = true; + + process.OutputDataReceived += new DataReceivedEventHandler((sender, e) => + { + Logger.Info("Game Server {ServerName} ({ServerId}) Info: {Message}", server.Name, server.Id, e.Data); + }); + + process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => + { + Logger.Error("Game Server {ServerName} ({ServerId}) Error: {Message}", server.Name, server.Id, e.Data); + }); process.Start(); + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + Processes[server.Id] = process; await process.WaitForExitAsync(); } + public void StopServer(Server server) { if (Processes.ContainsKey(server.Id)) - Processes[server.Id].Kill(); + { + var process = Processes[server.Id]; + + process.Kill(); + } } } } diff --git a/LANCommander/nlog.config b/LANCommander/nlog.config new file mode 100644 index 0000000..06075b0 --- /dev/null +++ b/LANCommander/nlog.config @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file