Skip to content

Commit ebcdc1b

Browse files
committed
feat(DebugLogViewer): implement browser-based debug log viewer
- Added RitsuDebugLogPipeline to manage the logging pipeline and serve logs to a browser-based viewer. - Introduced RitsuDebugLogViewerOptions for configurable settings such as port and auto-open behavior. - Implemented RitsuDebugLogRecord for structured log entries and RitsuDebugLogRingBuffer for efficient log storage. - Created commands and UI integration for opening the log viewer from the game settings. - Enhanced RitsuLibSettings to include options for the debug log viewer, improving developer debugging capabilities.
1 parent 764038e commit ebcdc1b

37 files changed

Lines changed: 5437 additions & 4 deletions

.gitignore

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ artifacts/nuget/
88
artifacts/github/
99
artifacts/bundle-staging/
1010

11-
Loader/bin/
12-
Loader/obj/
13-
1411
.vs/
1512

1613
local.props
@@ -23,4 +20,4 @@ local.props
2320
__pycache__/
2421
*.py[cod]
2522
*$py.class
26-
.venv/
23+
.venv/

Data/Models/RitsuLibSettings.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,62 @@ public sealed class RitsuLibSettings
6969
[JsonPropertyName("debug_compat_ancient_architect")]
7070
public bool DebugCompatAncientArchitect { get; set; } = true;
7171

72+
/// <summary>
73+
/// Starts the loopback-only browser debug log viewer for this session.
74+
/// 为本会话启动仅监听 loopback 的浏览器调试日志查看器。
75+
/// </summary>
76+
[JsonPropertyName("debug_log_viewer_enabled")]
77+
public bool DebugLogViewerEnabled { get; set; } = true;
78+
79+
/// <summary>
80+
/// Mirrors game logger callbacks into the debug log viewer event stream.
81+
/// 将游戏 logger 回调镜像到调试日志查看器事件流。
82+
/// </summary>
83+
[JsonPropertyName("debug_log_viewer_mirror_game_logs")]
84+
public bool DebugLogViewerMirrorGameLogs { get; set; } = true;
85+
86+
/// <summary>
87+
/// When true, opens the debug log viewer in the system browser if no browser client connects shortly after startup.
88+
/// 为 true 时,启动后短时间内若没有浏览器客户端连接,则在系统浏览器中打开调试日志查看器。
89+
/// </summary>
90+
[JsonPropertyName("debug_log_viewer_auto_open")]
91+
public bool DebugLogViewerAutoOpen { get; set; }
92+
93+
/// <summary>
94+
/// Loopback HTTP port for the debug log viewer.
95+
/// 调试日志查看器的 loopback HTTP 端口。
96+
/// </summary>
97+
[JsonPropertyName("debug_log_viewer_port")]
98+
public int DebugLogViewerPort { get; set; } = 18742;
99+
100+
/// <summary>
101+
/// Number of consecutive ports to try after <see cref="DebugLogViewerPort" /> when the preferred port is busy.
102+
/// 首选端口被占用时,在 <see cref="DebugLogViewerPort" /> 后继续尝试的连续端口数量。
103+
/// </summary>
104+
[JsonPropertyName("debug_log_viewer_port_fallback_count")]
105+
public int DebugLogViewerPortFallbackCount { get; set; } = 20;
106+
107+
/// <summary>
108+
/// Stable browser access token for the loopback debug log viewer.
109+
/// 本机调试日志查看器使用的稳定浏览器访问 token。
110+
/// </summary>
111+
[JsonPropertyName("debug_log_viewer_access_token")]
112+
public string DebugLogViewerAccessToken { get; set; } = "";
113+
114+
/// <summary>
115+
/// Number of recent events retained in memory for newly opened browser sessions.
116+
/// 为新打开的浏览器会话保留在内存中的最近事件数量。
117+
/// </summary>
118+
[JsonPropertyName("debug_log_viewer_ring_buffer_capacity")]
119+
public int DebugLogViewerRingBufferCapacity { get; set; } = 10000;
120+
121+
/// <summary>
122+
/// Maximum pending event count before the non-blocking debug pipeline starts dropping new events.
123+
/// 非阻塞调试管道开始丢弃新事件前允许排队的最大事件数。
124+
/// </summary>
125+
[JsonPropertyName("debug_log_viewer_queue_capacity")]
126+
public int DebugLogViewerQueueCapacity { get; set; } = 4096;
127+
72128
/// <summary>
73129
/// When true, cards, relics, and potions append a hover tip showing their source mod.
74130
/// 为 true 时,卡牌、遗物和药水会追加显示其来源 mod 的悬停提示。

Data/RitsuLibSettingsStore.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System.Security.Cryptography;
12
using STS2RitsuLib.Data.Migrations;
23
using STS2RitsuLib.Data.Models;
4+
using STS2RitsuLib.Diagnostics.Logging;
35
using STS2RitsuLib.Ui.Shell.Theme;
46
using STS2RitsuLib.Ui.Toast;
57
using STS2RitsuLib.Utils.Persistence;
@@ -203,6 +205,32 @@ internal static bool ShouldShowGameTermModSourceHoverTips()
203205
return GetSettings().ModSourceHoverTipsGameTerms;
204206
}
205207

208+
internal static RitsuDebugLogViewerOptions GetDebugLogViewerOptions()
209+
{
210+
Initialize();
211+
var s = GetSettings();
212+
var changed = false;
213+
if (string.IsNullOrWhiteSpace(s.DebugLogViewerAccessToken))
214+
{
215+
s.DebugLogViewerAccessToken =
216+
Convert.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLowerInvariant();
217+
changed = true;
218+
}
219+
220+
if (changed)
221+
Store.Save(Const.SettingsKey);
222+
223+
return new(
224+
s.DebugLogViewerEnabled,
225+
s.DebugLogViewerMirrorGameLogs,
226+
s.DebugLogViewerAutoOpen,
227+
Math.Clamp(s.DebugLogViewerPort, 1, 65535),
228+
Math.Clamp(s.DebugLogViewerPortFallbackCount, 0, 100),
229+
s.DebugLogViewerAccessToken,
230+
s.DebugLogViewerRingBufferCapacity,
231+
s.DebugLogViewerQueueCapacity);
232+
}
233+
206234
private static RitsuLibSettings GetSettings()
207235
{
208236
return Store.Get<RitsuLibSettings>(Const.SettingsKey);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using MegaCrit.Sts2.Core.DevConsole;
2+
using MegaCrit.Sts2.Core.DevConsole.ConsoleCommands;
3+
using MegaCrit.Sts2.Core.Entities.Players;
4+
using STS2RitsuLib.Diagnostics.Logging;
5+
6+
namespace STS2RitsuLib.Diagnostics.Commands
7+
{
8+
/// <summary>
9+
/// Opens the local RitsuLib debug log viewer in the system browser.
10+
/// 在系统浏览器中打开本机 RitsuLib 调试日志查看器。
11+
/// </summary>
12+
public sealed class OpenLogViewerConsoleCmd : AbstractConsoleCmd
13+
{
14+
/// <inheritdoc />
15+
public override string CmdName => "openlogviewer";
16+
17+
/// <inheritdoc />
18+
public override string Args => "";
19+
20+
/// <inheritdoc />
21+
public override string Description => "Opens the local RitsuLib debug log viewer in a browser.";
22+
23+
/// <inheritdoc />
24+
public override bool IsNetworked => false;
25+
26+
/// <inheritdoc />
27+
public override CmdResult Process(Player? issuingPlayer, string[] args)
28+
{
29+
if (args.Length > 0)
30+
return new(false, "Usage: openlogviewer");
31+
32+
var result = RitsuDebugLogPipeline.TryOpenViewerInBrowser();
33+
return new(result.Success, result.Message);
34+
}
35+
}
36+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Godot;
2+
using Godot.Collections;
3+
4+
namespace STS2RitsuLib.Diagnostics.Logging
5+
{
6+
internal sealed partial class RitsuDebugGodotLogListener : Logger
7+
{
8+
public override void _LogMessage(string message, bool error)
9+
{
10+
RitsuDebugLogPipeline.EmitGodotLogMessage(message, error);
11+
}
12+
13+
public override void _LogError(
14+
string function,
15+
string file,
16+
int line,
17+
string code,
18+
string rationale,
19+
bool editorNotify,
20+
int errorType,
21+
Array<ScriptBacktrace> scriptBacktraces)
22+
{
23+
RitsuDebugLogPipeline.EmitGodotLogError(
24+
function,
25+
file,
26+
line,
27+
code,
28+
rationale,
29+
errorType,
30+
FormatScriptBacktraces(scriptBacktraces));
31+
}
32+
33+
private static string FormatScriptBacktraces(Array<ScriptBacktrace> scriptBacktraces)
34+
{
35+
var lines =
36+
(from backtrace in scriptBacktraces where !backtrace.IsEmpty() select backtrace.Format()).ToList();
37+
38+
return string.Join("", lines);
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)