Add server log file monitoring
This commit is contained in:
parent
4acd8cdc5c
commit
8055b3ae69
10 changed files with 1777 additions and 1 deletions
|
@ -98,6 +98,12 @@ namespace LANCommander.Data
|
||||||
.WithMany(g => g.Servers)
|
.WithMany(g => g.Servers)
|
||||||
.IsRequired(false)
|
.IsRequired(false)
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
.OnDelete(DeleteBehavior.NoAction);
|
||||||
|
|
||||||
|
builder.Entity<Server>()
|
||||||
|
.HasMany<ServerLog>()
|
||||||
|
.WithOne(sl => sl.Server)
|
||||||
|
.IsRequired(true)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<Game>? Games { get; set; }
|
public DbSet<Game>? Games { get; set; }
|
||||||
|
|
|
@ -22,5 +22,7 @@ namespace LANCommander.Data.Models
|
||||||
[ForeignKey(nameof(GameId))]
|
[ForeignKey(nameof(GameId))]
|
||||||
[InverseProperty("Servers")]
|
[InverseProperty("Servers")]
|
||||||
public virtual Game? Game { get; set; }
|
public virtual Game? Game { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<ServerLog>? ServerLogs { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
LANCommander/Data/Models/ServerLog.cs
Normal file
17
LANCommander/Data/Models/ServerLog.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace LANCommander.Data.Models
|
||||||
|
{
|
||||||
|
public class ServerLog : BaseModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public string Path { get; set; } = "";
|
||||||
|
|
||||||
|
public Guid? ServerId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
[ForeignKey(nameof(ServerId))]
|
||||||
|
[InverseProperty("ServerLogs")]
|
||||||
|
public virtual Server? Server { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,23 @@
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using LANCommander.Services;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace LANCommander.Hubs
|
namespace LANCommander.Hubs
|
||||||
{
|
{
|
||||||
public class GameServerHub : Hub
|
public class GameServerHub : Hub
|
||||||
{
|
{
|
||||||
|
readonly ServerProcessService ServerProcessService;
|
||||||
|
public GameServerHub(ServerProcessService serverProcessService) {
|
||||||
|
ServerProcessService = serverProcessService;
|
||||||
|
|
||||||
|
ServerProcessService.OnLog += ServerProcessService_OnLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServerProcessService_OnLog(object sender, ServerLogEventArgs e)
|
||||||
|
{
|
||||||
|
Clients.All.SendAsync("Log", e.Log.ServerId, e.Line);
|
||||||
|
}
|
||||||
|
|
||||||
public void Log(Guid serverId, string message)
|
public void Log(Guid serverId, string message)
|
||||||
{
|
{
|
||||||
Clients.All.SendAsync("Log", serverId, message);
|
Clients.All.SendAsync("Log", serverId, message);
|
||||||
|
|
1417
LANCommander/Migrations/20230815054532_AddServerLogModel.Designer.cs
generated
Normal file
1417
LANCommander/Migrations/20230815054532_AddServerLogModel.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
82
LANCommander/Migrations/20230815054532_AddServerLogModel.cs
Normal file
82
LANCommander/Migrations/20230815054532_AddServerLogModel.cs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace LANCommander.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddServerLogModel : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerLog",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Path = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ServerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
ServerId1 = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||||
|
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerLog", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerLog_AspNetUsers_CreatedById",
|
||||||
|
column: x => x.CreatedById,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerLog_AspNetUsers_UpdatedById",
|
||||||
|
column: x => x.UpdatedById,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerLog_Servers_ServerId",
|
||||||
|
column: x => x.ServerId,
|
||||||
|
principalTable: "Servers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerLog_Servers_ServerId1",
|
||||||
|
column: x => x.ServerId1,
|
||||||
|
principalTable: "Servers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerLog_CreatedById",
|
||||||
|
table: "ServerLog",
|
||||||
|
column: "CreatedById");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerLog_ServerId",
|
||||||
|
table: "ServerLog",
|
||||||
|
column: "ServerId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerLog_ServerId1",
|
||||||
|
table: "ServerLog",
|
||||||
|
column: "ServerId1");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerLog_UpdatedById",
|
||||||
|
table: "ServerLog",
|
||||||
|
column: "UpdatedById");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerLog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -683,6 +683,52 @@ namespace LANCommander.Migrations
|
||||||
b.ToTable("Servers");
|
b.ToTable("Servers");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.ServerLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("CreatedById")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ServerId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ServerId1")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UpdatedById")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedOn")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedById");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId1");
|
||||||
|
|
||||||
|
b.HasIndex("UpdatedById");
|
||||||
|
|
||||||
|
b.ToTable("ServerLog");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
|
@ -1236,6 +1282,33 @@ namespace LANCommander.Migrations
|
||||||
b.Navigation("UpdatedBy");
|
b.Navigation("UpdatedBy");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.ServerLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CreatedById");
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.Server", "Server")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ServerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.Server", null)
|
||||||
|
.WithMany("ServerLogs")
|
||||||
|
.HasForeignKey("ServerId1");
|
||||||
|
|
||||||
|
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UpdatedById");
|
||||||
|
|
||||||
|
b.Navigation("CreatedBy");
|
||||||
|
|
||||||
|
b.Navigation("Server");
|
||||||
|
|
||||||
|
b.Navigation("UpdatedBy");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||||
|
@ -1326,6 +1399,11 @@ namespace LANCommander.Migrations
|
||||||
b.Navigation("Servers");
|
b.Navigation("Servers");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ServerLogs");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LANCommander.Data.Models.User", b =>
|
modelBuilder.Entity("LANCommander.Data.Models.User", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("GameSaves");
|
b.Navigation("GameSaves");
|
||||||
|
|
74
LANCommander/Pages/Servers/Components/ServerLogEditor.razor
Normal file
74
LANCommander/Pages/Servers/Components/ServerLogEditor.razor
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
@using LANCommander.Data.Enums
|
||||||
|
@using LANCommander.Data.Models
|
||||||
|
@using LANCommander.Extensions;
|
||||||
|
|
||||||
|
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||||
|
<SpaceItem>
|
||||||
|
<Table TItem="ServerLog" DataSource="@Value" HidePagination="true">
|
||||||
|
<PropertyColumn Property="m => m.Name">
|
||||||
|
<Input Type="text" @bind-Value="context.Name" />
|
||||||
|
</PropertyColumn>
|
||||||
|
<PropertyColumn Property="m => m.Path">
|
||||||
|
<Input Type="text" @bind-Value="context.Path" />
|
||||||
|
</PropertyColumn>
|
||||||
|
<ActionColumn>
|
||||||
|
<Space Style="display: flex; justify-content: end">
|
||||||
|
<SpaceItem>
|
||||||
|
<Button OnClick="() => RemoveLog(context)" Type="@ButtonType.Text" Danger Icon="@IconType.Outline.Close" />
|
||||||
|
</SpaceItem>
|
||||||
|
</Space>
|
||||||
|
</ActionColumn>
|
||||||
|
</Table>
|
||||||
|
</SpaceItem>
|
||||||
|
|
||||||
|
<SpaceItem>
|
||||||
|
<GridRow Justify="end">
|
||||||
|
<GridCol>
|
||||||
|
<Button OnClick="AddLog" Type="@ButtonType.Primary">Add Log</Button>
|
||||||
|
</GridCol>
|
||||||
|
</GridRow>
|
||||||
|
</SpaceItem>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public ICollection<ServerLog> Value { get; set; }
|
||||||
|
[Parameter] public EventCallback<ICollection<ServerLog>> ValueChanged { get; set; }
|
||||||
|
[Parameter] public Guid ServerId { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
if (Value == null)
|
||||||
|
Value = new List<ServerLog>();
|
||||||
|
|
||||||
|
if (ValueChanged.HasDelegate)
|
||||||
|
await ValueChanged.InvokeAsync(Value);
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AddLog()
|
||||||
|
{
|
||||||
|
if (Value == null)
|
||||||
|
Value = new List<ServerLog>();
|
||||||
|
|
||||||
|
Value.Add(new ServerLog
|
||||||
|
{
|
||||||
|
ServerId = ServerId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ValueChanged.HasDelegate)
|
||||||
|
await ValueChanged.InvokeAsync(Value);
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RemoveLog(ServerLog log)
|
||||||
|
{
|
||||||
|
Value.Remove(log);
|
||||||
|
|
||||||
|
if (ValueChanged.HasDelegate)
|
||||||
|
await ValueChanged.InvokeAsync(Value);
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
|
@ -126,6 +126,11 @@
|
||||||
</AntDesign.Input>
|
</AntDesign.Input>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<FormItem>
|
||||||
|
<ServerLogEditor @bind-Value="Server.ServerLogs" ServerId="Id" />
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using LANCommander.Data.Models;
|
using LANCommander.Data.Models;
|
||||||
|
using LANCommander.Hubs;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
@ -12,11 +14,33 @@ namespace LANCommander.Services
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ServerLogEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public string Line { get; private set; }
|
||||||
|
public ServerLog Log { get; private set; }
|
||||||
|
|
||||||
|
public ServerLogEventArgs(string line, ServerLog log)
|
||||||
|
{
|
||||||
|
Line = line;
|
||||||
|
Log = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class ServerProcessService : BaseService
|
public class ServerProcessService : BaseService
|
||||||
{
|
{
|
||||||
public Dictionary<Guid, Process> Processes = new Dictionary<Guid, Process>();
|
public Dictionary<Guid, Process> Processes = new Dictionary<Guid, Process>();
|
||||||
public Dictionary<Guid, int> Threads { get; set; } = new Dictionary<Guid, int>();
|
public Dictionary<Guid, int> Threads { get; set; } = new Dictionary<Guid, int>();
|
||||||
|
|
||||||
|
public delegate void OnLogHandler(object sender, ServerLogEventArgs e);
|
||||||
|
public event OnLogHandler OnLog;
|
||||||
|
|
||||||
|
private IHubContext<GameServerHub> HubContext;
|
||||||
|
|
||||||
|
public ServerProcessService(IHubContext<GameServerHub> hubContext)
|
||||||
|
{
|
||||||
|
HubContext = hubContext;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task StartServerAsync(Server server)
|
public async Task StartServerAsync(Server server)
|
||||||
{
|
{
|
||||||
var process = new Process();
|
var process = new Process();
|
||||||
|
@ -53,6 +77,11 @@ namespace LANCommander.Services
|
||||||
|
|
||||||
Processes[server.Id] = process;
|
Processes[server.Id] = process;
|
||||||
|
|
||||||
|
foreach (var log in server.ServerLogs)
|
||||||
|
{
|
||||||
|
MonitorLog(log, server);
|
||||||
|
}
|
||||||
|
|
||||||
await process.WaitForExitAsync();
|
await process.WaitForExitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +96,59 @@ namespace LANCommander.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MonitorLog(ServerLog log, Server server)
|
||||||
|
{
|
||||||
|
var logPath = Path.Combine(server.WorkingDirectory, log.Path);
|
||||||
|
|
||||||
|
if (File.Exists(logPath))
|
||||||
|
{
|
||||||
|
var lockMe = new object();
|
||||||
|
using (var latch = new ManualResetEvent(true))
|
||||||
|
using (var fs = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
|
using (var fsw = new FileSystemWatcher(Path.GetDirectoryName(logPath)))
|
||||||
|
{
|
||||||
|
fsw.Changed += (s, e) =>
|
||||||
|
{
|
||||||
|
lock (lockMe)
|
||||||
|
{
|
||||||
|
if (e.FullPath != logPath)
|
||||||
|
return;
|
||||||
|
|
||||||
|
latch.Set();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var sr = new StreamReader(fs))
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
latch.WaitOne();
|
||||||
|
|
||||||
|
lock(lockMe)
|
||||||
|
{
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while ((line = sr.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
HubContext.Clients.All.SendAsync("Log", log.ServerId, line);
|
||||||
|
//OnLog?.Invoke(this, new ServerLogEventArgs(line, log));
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Fsw_Changed(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public ServerProcessStatus GetStatus(Server server)
|
public ServerProcessStatus GetStatus(Server server)
|
||||||
{
|
{
|
||||||
Process process = null;
|
Process process = null;
|
||||||
|
|
Loading…
Add table
Reference in a new issue