Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ public LaunchOptions getLaunchOptions(String version, JavaRuntime javaVersion, P
.setNativesDir(vs.getNativesDir())
.setProcessPriority(vs.getProcessPriority())
.setRenderer(vs.getRenderer())
.setShowDebugLog(vs.isShowDebugLogsOutput())
.setUseNativeGLFW(vs.isUseNativeGLFW())
.setUseNativeOpenAL(vs.isUseNativeOpenAL())
.setDaemon(!makeLaunchScript && vs.getLauncherVisibility().isDaemon())
Expand Down
16 changes: 16 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,20 @@ public void setShowLogs(boolean showLogs) {
showLogsProperty.set(showLogs);
}

private final BooleanProperty showDebugLogsProperty = new SimpleBooleanProperty(this, "showDebugLogs", false);

public BooleanProperty showDebugLogsProperty() {
return showDebugLogsProperty;
}

Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new method isEnableDebugLogOutput() lacks JavaDoc documentation while the similar method isShowLogs() above it has documentation. For consistency and clarity, consider adding documentation like: "/** * True if debug level logging should be enabled in log4j2 configuration. */" to explain what this option does.

Suggested change
/**
* True if debug level logging should be enabled in log4j2 configuration.
*/

Copilot uses AI. Check for mistakes.
public boolean isShowDebugLogsOutput() {
return showDebugLogsProperty.get();
}

public void setShowDebugLogs(boolean u) {
this.showDebugLogsProperty.set(u);
}

// Minecraft settings.

private final StringProperty serverIpProperty = new SimpleStringProperty(this, "serverIp", "");
Expand Down Expand Up @@ -775,6 +789,7 @@ public JsonElement serialize(VersionSetting src, Type typeOfSrc, JsonSerializati
obj.addProperty("notCheckJVM", src.isNotCheckJVM());
obj.addProperty("notPatchNatives", src.isNotPatchNatives());
obj.addProperty("showLogs", src.isShowLogs());
obj.addProperty("showDebugLogs", src.isShowDebugLogsOutput());
obj.addProperty("gameDir", src.getGameDir());
obj.addProperty("launcherVisibility", src.getLauncherVisibility().ordinal());
obj.addProperty("processPriority", src.getProcessPriority().ordinal());
Expand Down Expand Up @@ -845,6 +860,7 @@ public VersionSetting deserialize(JsonElement json, Type typeOfT, JsonDeserializ
vs.setNotCheckJVM(Optional.ofNullable(obj.get("notCheckJVM")).map(JsonElement::getAsBoolean).orElse(false));
vs.setNotPatchNatives(Optional.ofNullable(obj.get("notPatchNatives")).map(JsonElement::getAsBoolean).orElse(false));
vs.setShowLogs(Optional.ofNullable(obj.get("showLogs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setShowDebugLogs(Optional.ofNullable(obj.get("showDebugLogs")).map(JsonElement::getAsBoolean).orElse(false));
vs.setLauncherVisibility(parseJsonPrimitive(obj.getAsJsonPrimitive("launcherVisibility"), LauncherVisibility.class, LauncherVisibility.HIDE));
vs.setProcessPriority(parseJsonPrimitive(obj.getAsJsonPrimitive("processPriority"), ProcessPriority.class, ProcessPriority.NORMAL));
vs.setUseNativeGLFW(Optional.ofNullable(obj.get("useNativeGLFW")).map(JsonElement::getAsBoolean).orElse(false));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public void run() {
private final MultiFileItem.FileOption<GameDirectoryType> gameDirCustomOption;
private final JFXComboBox<ProcessPriority> cboProcessPriority;
private final OptionToggleButton showLogsPane;
private final OptionToggleButton showDebugLogsPane;
private final ImagePickerItem iconPickerItem;

private final ChangeListener<Collection<JavaRuntime>> javaListChangeListener;
Expand Down Expand Up @@ -411,6 +412,9 @@ public VersionSettingsPage(boolean globalSetting) {
showLogsPane = new OptionToggleButton();
showLogsPane.setTitle(i18n("settings.show_log"));

showDebugLogsPane = new OptionToggleButton();
showDebugLogsPane.setTitle(i18n("settings.show_debug_log"));

BorderPane processPriorityPane = new BorderPane();
{
Label label = new Label(i18n("settings.advanced.process_priority"));
Expand Down Expand Up @@ -476,6 +480,7 @@ public VersionSettingsPage(boolean globalSetting) {
launcherVisibilityPane,
dimensionPane,
showLogsPane,
showDebugLogsPane,
processPriorityPane,
serverPane,
showAdvancedSettingPane
Expand Down Expand Up @@ -553,6 +558,7 @@ public void loadVersion(Profile profile, String versionId) {
chkAutoAllocate.selectedProperty().unbindBidirectional(lastVersionSetting.autoMemoryProperty());
chkFullscreen.selectedProperty().unbindBidirectional(lastVersionSetting.fullscreenProperty());
showLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showLogsProperty());
showDebugLogsPane.selectedProperty().unbindBidirectional(lastVersionSetting.showDebugLogsProperty());
FXUtils.unbindEnum(cboLauncherVisibility, lastVersionSetting.launcherVisibilityProperty());
FXUtils.unbindEnum(cboProcessPriority, lastVersionSetting.processPriorityProperty());

Expand Down Expand Up @@ -587,6 +593,7 @@ public void loadVersion(Profile profile, String versionId) {
chkAutoAllocate.selectedProperty().bindBidirectional(versionSetting.autoMemoryProperty());
chkFullscreen.selectedProperty().bindBidirectional(versionSetting.fullscreenProperty());
showLogsPane.selectedProperty().bindBidirectional(versionSetting.showLogsProperty());
showDebugLogsPane.selectedProperty().bindBidirectional(versionSetting.showDebugLogsProperty());
FXUtils.bindEnum(cboLauncherVisibility, versionSetting.launcherVisibilityProperty());
FXUtils.bindEnum(cboProcessPriority, versionSetting.processPriorityProperty());

Expand Down
1 change: 1 addition & 0 deletions HMCL/src/main/resources/assets/lang/I18N.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,7 @@ settings.memory.unit.mib=MiB
settings.memory.used_per_total=%1$.1f GiB Used / %2$.1f GiB Total
settings.physical_memory=Physical Memory Size
settings.show_log=Show Logs
settings.show_debug_log=Output debug log
settings.tabs.installers=Loaders
settings.take_effect_after_restart=Applies After Restart
settings.type=Settings Type of Instance
Expand Down
1 change: 1 addition & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ settings.memory.unit.mib=MiB
settings.memory.used_per_total=已使用 %1$.1f GiB / 總記憶體 %2$.1f GiB
settings.physical_memory=實體記憶體大小
settings.show_log=查看日誌
settings.show_debug_log=输出除錯日誌
settings.tabs.installers=自動安裝
settings.take_effect_after_restart=重啟後生效
settings.type=實例遊戲設定類型
Expand Down
1 change: 1 addition & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,7 @@ settings.memory.unit.mib=MiB
settings.memory.used_per_total=设备中已使用 %1$.1f GiB / 设备总内存 %2$.1f GiB
settings.physical_memory=物理内存大小
settings.show_log=查看日志
settings.show_debug_log=输出调试日志
settings.tabs.installers=自动安装
settings.take_effect_after_restart=重启后生效
settings.type=实例游戏设置类型
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class LaunchOptions implements Serializable {
private Renderer renderer = Renderer.DEFAULT;
private boolean useNativeGLFW;
private boolean useNativeOpenAL;
private boolean showDebugLog;
private boolean daemon;

/**
Expand Down Expand Up @@ -282,6 +283,10 @@ public boolean isUseNativeOpenAL() {
return useNativeOpenAL;
}

Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new method isEnableDebugLogOutput() lacks JavaDoc documentation. While some similar methods like isUseNativeGLFW() also lack documentation, many other getter methods in this class have JavaDoc comments explaining their purpose (e.g., isDaemon(), isNoGeneratedJVMArgs()). Consider adding documentation like: "/** * Whether to enable debug level logging output in log4j2 configuration. */" to explain what this option does.

Suggested change
/**
* Whether to enable debug level logging output in log4j2 configuration.
*/

Copilot uses AI. Check for mistakes.
public boolean isShowDebugLog() {
return showDebugLog;
}

/**
* Will launcher keeps alive after game launched or not.
*/
Expand Down Expand Up @@ -497,5 +502,9 @@ public Builder setDaemon(boolean daemon) {
return this;
}

public Builder setShowDebugLog(boolean u) {
options.showDebugLog = u;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,14 @@ private Command generateCommandLine(Path nativeFolder) throws IOException {
res.addDefault("-Dcom.sun.jndi.rmi.object.trustURLCodebase=", "false");
res.addDefault("-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=", "false");

String formatMsgNoLookups = res.addDefault("-Dlog4j2.formatMsgNoLookups=", "true");
if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && isUsingLog4j()) {
if (options.isShowDebugLog()) {
res.addDefault("-Dlog4j2.formatMsgNoLookups=", "false");
res.addDefault("-Dlog4j.configurationFile=", FileUtils.getAbsolutePath(getLog4jConfigurationFile()));
} else {
String formatMsgNoLookups = res.addDefault("-Dlog4j2.formatMsgNoLookups=", "true");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

请在最外面调用此方法。该参数总是应该被默认添加,不应该依赖 isUsingLog4j 的检测。即便该 MC 版本本身没有使用 log4j2,也不排除模组依赖 log4j2 的可能性。log4j2 的这项功能存在极高的风险性,而该 JVM 参数又几乎没有负面作用,我们应当以最保险的方式添加此参数。

if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && isUsingLog4j()) {
res.addDefault("-Dlog4j.configurationFile=", FileUtils.getAbsolutePath(getLog4jConfigurationFile()));
}
}

// Default JVM Args
Expand Down Expand Up @@ -421,14 +426,23 @@ public Path getLog4jConfigurationFile() {

public void extractLog4jConfigurationFile() throws IOException {
Path targetFile = getLog4jConfigurationFile();
InputStream source;

StringBuilder sourcePathBuilder = new StringBuilder("/assets/game/log4j2-");

if (GameVersionNumber.asGameVersion(repository.getGameVersion(version)).compareTo("1.12") < 0) {
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.7.xml");
sourcePathBuilder.append("1.7");
} else {
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.12.xml");
sourcePathBuilder.append("1.12");
}

try (InputStream input = source) {

if (options.isShowDebugLog()) {
sourcePathBuilder.append("-debug");
}

sourcePathBuilder.append(".xml");
String sourcePath = sourcePathBuilder.toString();

try (InputStream input = DefaultLauncher.class.getResourceAsStream(sourcePath)) {
Files.copy(input, targetFile, StandardCopyOption.REPLACE_EXISTING);
Comment on lines +441 to 442
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The InputStream returned by getResourceAsStream could be null if the resource file is not found. The code should add a null check (similar to the pattern used in MaintainTask.java line 166) to provide a clearer error message if the bundled log4j configuration file is missing. Consider using Objects.requireNonNull(input, "Bundled log4j2 configuration is missing.") before passing it to Files.copy.

Copilot uses AI. Check for mistakes.
}
}
Expand Down
37 changes: 37 additions & 0 deletions HMCLCore/src/main/resources/assets/game/log4j2-1.12-debug.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" packages="">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
</Console>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %msg{nolookups}%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<OnStartupTriggeringPolicy/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level] [%logger]: %msg{nolookups}%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<OnStartupTriggeringPolicy/>
</Policies>
<DefaultRolloverStrategy max="999" fileIndex="min"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="all">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
</filters>
<AppenderRef ref="SysOut" level="info"/>
<AppenderRef ref="ServerGuiConsole" level="info"/>
<AppenderRef ref="File" level="info"/>
<AppenderRef ref="DebugFile" level="all"/>
</Root>
</Loggers>
</Configuration>
36 changes: 36 additions & 0 deletions HMCLCore/src/main/resources/assets/game/log4j2-1.7-debug.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
</Console>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<OnStartupTriggeringPolicy/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%t/%level] [%logger]: %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<OnStartupTriggeringPolicy/>
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="all">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
</filters>
<AppenderRef ref="SysOut" level="info"/>
<AppenderRef ref="ServerGuiConsole" level="info"/>
<AppenderRef ref="File" level="info"/>
<AppenderRef ref="DebugFile" level="all"/>
</Root>
</Loggers>
</Configuration>