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)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
builder.Entity<Server>()
|
||||
.HasMany<ServerLog>()
|
||||
.WithOne(sl => sl.Server)
|
||||
.IsRequired(true)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
public DbSet<Game>? Games { get; set; }
|
||||
|
|
|
@ -22,5 +22,7 @@ namespace LANCommander.Data.Models
|
|||
[ForeignKey(nameof(GameId))]
|
||||
[InverseProperty("Servers")]
|
||||
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;
|
||||
|
||||
namespace LANCommander.Hubs
|
||||
{
|
||||
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)
|
||||
{
|
||||
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");
|
||||
});
|
||||
|
||||
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 =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
@ -1236,6 +1282,33 @@ namespace LANCommander.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||
|
@ -1326,6 +1399,11 @@ namespace LANCommander.Migrations
|
|||
b.Navigation("Servers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
||||
{
|
||||
b.Navigation("ServerLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.User", b =>
|
||||
{
|
||||
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>
|
||||
</FormItem>
|
||||
}
|
||||
|
||||
<FormItem>
|
||||
<ServerLogEditor @bind-Value="Server.ServerLogs" ServerId="Id" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
</FormItem>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using LANCommander.Data.Models;
|
||||
using LANCommander.Hubs;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using NLog;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
@ -12,11 +14,33 @@ namespace LANCommander.Services
|
|||
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 Dictionary<Guid, Process> Processes = new Dictionary<Guid, Process>();
|
||||
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)
|
||||
{
|
||||
var process = new Process();
|
||||
|
@ -53,6 +77,11 @@ namespace LANCommander.Services
|
|||
|
||||
Processes[server.Id] = process;
|
||||
|
||||
foreach (var log in server.ServerLogs)
|
||||
{
|
||||
MonitorLog(log, server);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Process process = null;
|
||||
|
|
Loading…
Add table
Reference in a new issue