Skip to content

Commit 4bca927

Browse files
committed
Rewrite log injection
No longer uses filters, this now rewrites log events using a rewrite policy The remapping process has been moved to the logger, as otherwise log4j's extra class info would be lost
1 parent a33e442 commit 4bca927

File tree

8 files changed

+226
-111
lines changed

8 files changed

+226
-111
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
## v1.3.2
22

33
- Fix exceptions when remapping lambda methods
4+
- Added optional remapping of every log message
5+
- Rewrote internal log injection handling
6+
- Now uses Log4j's exception rendering
7+
- Removed "MC//" prefix in stacktrace, minecraft classes are now suffixed with "`~[client-intermediary.jar:?]`"
48

59
## v1.3.1
610

711
- Added support for quilt mappings below 1.19.2
8-
- Now goes down to 1.18.2
12+
- Now goes down to 1.18.2
913

1014
## v1.3.0
1115

README.md

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,68 +21,68 @@ human-readable.
2121
<summary><b>Before</b></summary>
2222

2323
> ```
24-
> [18:04:12] [Render thread/ERROR]: Reported exception thrown!
24+
> [23:13:08] [Render thread/ERROR]: Reported exception thrown!
2525
> net.minecraft.class_148: Manually triggered debug crash
26-
> at net.minecraft.class_309.method_1474(class_309.java:509) ~[client-intermediary.jar:?]
27-
> at net.minecraft.class_310.method_1574(class_310.java:1955) ~[client-intermediary.jar:?]
28-
> at net.minecraft.class_310.method_1523(class_310.java:1180) ~[client-intermediary.jar:?]
29-
> at net.minecraft.class_310.method_1514(class_310.java:801) ~[client-intermediary.jar:?]
30-
> at net.minecraft.client.main.Main.main(Main.java:237) ~[minecraft-1.19.4-client.jar:?]
31-
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462) ~[fabric-loader-0.14.18.jar:?]
32-
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74) ~[fabric-loader-0.14.18.jar:?]
33-
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.14.18.jar:?]
34-
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88) ~[NewLaunch.jar:?]
35-
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126) ~[NewLaunch.jar:?]
36-
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71) ~[NewLaunch.jar:?]
26+
> at net.minecraft.class_309.method_1474(class_309.java:509) ~[client-intermediary.jar:?]
27+
> at net.minecraft.class_310.method_1574(class_310.java:1955) ~[client-intermediary.jar:?]
28+
> at net.minecraft.class_310.method_1523(class_310.java:1180) ~[client-intermediary.jar:?]
29+
> at net.minecraft.class_310.method_1514(class_310.java:801) ~[client-intermediary.jar:?]
30+
> at net.minecraft.client.main.Main.main(Main.java:237) ~[minecraft-1.19.4-client.jar:?]
31+
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462) ~[fabric-loader-0.14.18.jar:?]
32+
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74) ~[fabric-loader-0.14.18.jar:?]
33+
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.14.18.jar:?]
34+
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88) ~[NewLaunch.jar:?]
35+
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126) ~[NewLaunch.jar:?]
36+
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71) ~[NewLaunch.jar:?]
3737
> Caused by: java.lang.Throwable: Manually triggered debug crash
38-
> at net.minecraft.class_309.method_1474(class_309.java:506) ~[client-intermediary.jar:?]
39-
> ... 10 more
38+
> at net.minecraft.class_309.method_1474(class_309.java:506) ~[client-intermediary.jar:?]
39+
> ... 10 more
4040
> ```
4141
4242
</details>
4343
<details>
4444
<summary><b>After (yarn/quilt mappings)</b></summary>
4545
4646
> ```
47-
> [18:02:00] [Render thread/ERROR]: Reported exception thrown!
48-
> [18:02:00] [Render thread/ERROR]: net.minecraft.util.crash.CrashException: Manually triggered debug crash
49-
> at MC//net.minecraft.client.Keyboard.pollDebugCrash(Keyboard.java:509)
50-
> at MC//net.minecraft.client.MinecraftClient.tick(MinecraftClient.java:1955)
51-
> at MC//net.minecraft.client.MinecraftClient.render(MinecraftClient.java:1180)
52-
> at MC//net.minecraft.client.MinecraftClient.run(MinecraftClient.java:801)
53-
> at net.minecraft.client.main.Main.main(Main.java:237)
54-
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462)
55-
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74)
56-
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23)
57-
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88)
58-
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126)
59-
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71)
47+
> [23:11:25] [Render thread/ERROR]: Reported exception thrown!
48+
> net.minecraft.util.crash.CrashException: Manually triggered debug crash
49+
> at net.minecraft.client.Keyboard.pollDebugCrash(Keyboard.java:509) ~[client-intermediary.jar:?]
50+
> at net.minecraft.client.MinecraftClient.tick(MinecraftClient.java:1955) ~[client-intermediary.jar:?]
51+
> at net.minecraft.client.MinecraftClient.render(MinecraftClient.java:1180) ~[client-intermediary.jar:?]
52+
> at net.minecraft.client.MinecraftClient.run(MinecraftClient.java:801) ~[client-intermediary.jar:?]
53+
> at net.minecraft.client.main.Main.main(Main.java:237) ~[minecraft-1.19.4-client.jar:?]
54+
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462) ~[fabric-loader-0.14.18.jar:?]
55+
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74) ~[fabric-loader-0.14.18.jar:?]
56+
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.14.18.jar:?]
57+
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88) ~[NewLaunch.jar:?]
58+
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126) ~[NewLaunch.jar:?]
59+
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71) ~[NewLaunch.jar:?]
6060
> Caused by: java.lang.Throwable: Manually triggered debug crash
61-
> at MC//net.minecraft.client.Keyboard.pollDebugCrash(Keyboard.java:506)
62-
> ... 10 more
61+
> at net.minecraft.client.Keyboard.pollDebugCrash(Keyboard.java:506) ~[client-intermediary.jar:?]
62+
> ... 10 more
6363
> ```
6464
6565
</details>
6666
<details>
6767
<summary><b>After (mojang mappings)</b></summary>
6868
6969
> ```
70-
> [17:52:01] [Render thread/ERROR]: Reported exception thrown!
71-
> [17:52:01] [Render thread/ERROR]: net.minecraft.ReportedException: Manually triggered debug crash
72-
> at MC//net.minecraft.client.KeyboardHandler.tick(KeyboardHandler.java:509)
73-
> at MC//net.minecraft.client.Minecraft.tick(Minecraft.java:1955)
74-
> at MC//net.minecraft.client.Minecraft.runTick(Minecraft.java:1180)
75-
> at MC//net.minecraft.client.Minecraft.run(Minecraft.java:801)
76-
> at net.minecraft.client.main.Main.main(Main.java:237)
77-
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462)
78-
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74)
79-
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23)
80-
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88)
81-
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126)
82-
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71)
70+
> [23:04:12] [Render thread/ERROR]: Reported exception thrown!
71+
> net.minecraft.ReportedException: Manually triggered debug crash
72+
> at net.minecraft.client.KeyboardHandler.tick(KeyboardHandler.java:509) ~[client-intermediary.jar:?]
73+
> at net.minecraft.client.Minecraft.tick(Minecraft.java:1955) ~[client-intermediary.jar:?]
74+
> at net.minecraft.client.Minecraft.runTick(Minecraft.java:1180) ~[client-intermediary.jar:?]
75+
> at net.minecraft.client.Minecraft.run(Minecraft.java:801) ~[client-intermediary.jar:?]
76+
> at net.minecraft.client.main.Main.main(Main.java:237) ~[minecraft-1.19.4-client.jar:?]
77+
> at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:462) ~[fabric-loader-0.14.18.jar:?]
78+
> at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:74) ~[fabric-loader-0.14.18.jar:?]
79+
> at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) ~[fabric-loader-0.14.18.jar:?]
80+
> at org.prismlauncher.launcher.impl.StandardLauncher.launch(StandardLauncher.java:88) ~[NewLaunch.jar:?]
81+
> at org.prismlauncher.EntryPoint.listen(EntryPoint.java:126) ~[NewLaunch.jar:?]
82+
> at org.prismlauncher.EntryPoint.main(EntryPoint.java:71) ~[NewLaunch.jar:?]
8383
> Caused by: java.lang.Throwable: Manually triggered debug crash
84-
> at MC//net.minecraft.client.KeyboardHandler.tick(KeyboardHandler.java:506)
85-
> ... 10 more
84+
> at net.minecraft.client.KeyboardHandler.tick(KeyboardHandler.java:506) ~[client-intermediary.jar:?]
85+
> ... 10 more
8686
> ```
8787
8888
</details>

src/main/java/dev/booky/stackdeobf/StackDeobfMod.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
import dev.booky.stackdeobf.config.StackDeobfConfig;
55
import dev.booky.stackdeobf.mappings.CachedMappings;
6-
import dev.booky.stackdeobf.mappings.RemappingUtil;
6+
import dev.booky.stackdeobf.util.CompatUtil;
7+
import dev.booky.stackdeobf.util.RemappingRewritePolicy;
78
import net.fabricmc.api.ModInitializer;
89
import net.fabricmc.loader.api.FabricLoader;
910
import org.apache.logging.log4j.LogManager;
@@ -18,7 +19,9 @@ public void onInitialize() {
1819
StackDeobfConfig config = this.loadConfig();
1920

2021
if (config.hasLogInjectEnabled()) {
21-
RemappingUtil.injectLogFilter((Logger) LogManager.getRootLogger(), config.shouldRewriteEveryLogMessage());
22+
CompatUtil.LOGGER.info("Injecting into root logger...");
23+
RemappingRewritePolicy policy = new RemappingRewritePolicy(config);
24+
policy.inject((Logger) LogManager.getRootLogger());
2225
}
2326

2427
CachedMappings.init(config.getMappingProvider());

src/main/java/dev/booky/stackdeobf/mappings/RemappedThrowable.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
package dev.booky.stackdeobf.mappings;
22
// Created by booky10 in StackDeobfuscator (18:03 20.03.23)
33

4-
class RemappedThrowable extends Throwable {
4+
import org.jetbrains.annotations.ApiStatus;
55

6+
@ApiStatus.Internal
7+
public class RemappedThrowable extends Throwable {
8+
9+
private final Throwable original;
610
private final String className;
711

8-
public RemappedThrowable(String message, Throwable cause, String className) {
12+
public RemappedThrowable(String message, Throwable cause,
13+
Throwable original, String className) {
914
super(message, cause);
15+
this.original = original;
1016
this.className = className;
1117
}
1218

19+
public Throwable getOriginal() {
20+
return this.original;
21+
}
22+
1323
public String getClassName() {
1424
return this.className;
1525
}

src/main/java/dev/booky/stackdeobf/mappings/RemappingUtil.java

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
package dev.booky.stackdeobf.mappings;
22
// Created by booky10 in StackDeobfuscator (17:43 17.12.22)
33

4-
import dev.booky.stackdeobf.util.CompatUtil;
5-
import org.apache.logging.log4j.core.LogEvent;
6-
import org.apache.logging.log4j.core.filter.AbstractFilter;
7-
8-
import java.io.IOException;
9-
import java.io.PrintWriter;
10-
import java.io.StringWriter;
114
import java.util.regex.Matcher;
125
import java.util.regex.Pattern;
136

@@ -20,57 +13,7 @@ public final class RemappingUtil {
2013
private RemappingUtil() {
2114
}
2215

23-
public static void injectLogFilter(org.apache.logging.log4j.core.Logger logger, boolean rewriteMessages) {
24-
CompatUtil.LOGGER.info("Injecting into root logger...");
25-
logger.addFilter(new AbstractFilter() {
26-
@Override
27-
public Result filter(LogEvent event) {
28-
// TODO: this is a horribly hacky way to rewrite messages, find something better
29-
30-
if (event.getThrown() == null) {
31-
if (!rewriteMessages) {
32-
return Result.NEUTRAL;
33-
}
34-
35-
String message = event.getMessage().getFormattedMessage();
36-
String remappedMessage = RemappingUtil.remapString(message);
37-
38-
if (message.equals(remappedMessage)) {
39-
// didn't change anything, continue
40-
return Result.NEUTRAL;
41-
}
42-
43-
logger.logIfEnabled(event.getLoggerFqcn(), event.getLevel(), event.getMarker(), remappedMessage);
44-
45-
// cancel the underlying event, this needs
46-
// to be rewritten
47-
return Result.DENY;
48-
}
49-
50-
// we need to manually print out the stacktrace, because
51-
// log4j also builds it manually, resulting
52-
// in every logged exception being a "RemappedThrowable"
53-
try (StringWriter strWriter = new StringWriter()) {
54-
try (PrintWriter writer = new PrintWriter(strWriter)) {
55-
RemappingUtil.remapThrowable(event.getThrown()).printStackTrace(writer);
56-
}
57-
58-
// this message doesn't need to be remapped if every message should be remapped,
59-
// as this is handled by the above logic already
60-
logger.logIfEnabled(event.getLoggerFqcn(), event.getLevel(), event.getMarker(), event.getMessage(), null);
61-
62-
logger.logIfEnabled(event.getLoggerFqcn(), event.getLevel(), event.getMarker(), strWriter.toString());
63-
} catch (IOException exception) {
64-
throw new RuntimeException(exception);
65-
}
66-
67-
// cancel the underlying log event
68-
return Result.DENY;
69-
}
70-
});
71-
}
72-
73-
private static String remapClasses(String string) {
16+
public static String remapClasses(String string) {
7417
return CLASS_PATTERN.matcher(string).replaceAll(result -> {
7518
int classId = Integer.parseInt(result.group(2));
7619
String className = CachedMappings.remapClass(classId);
@@ -92,15 +35,15 @@ private static String remapClasses(String string) {
9235
});
9336
}
9437

95-
private static String remapMethods(String string) {
38+
public static String remapMethods(String string) {
9639
return METHOD_PATTERN.matcher(string).replaceAll(result -> {
9740
int methodId = Integer.parseInt(result.group(1));
9841
String methodName = CachedMappings.remapMethod(methodId);
9942
return methodName == null ? result.group() : Matcher.quoteReplacement(methodName);
10043
});
10144
}
10245

103-
private static String remapFields(String string) {
46+
public static String remapFields(String string) {
10447
return FIELD_PATTERN.matcher(string).replaceAll(result -> {
10548
int fieldId = Integer.parseInt(result.group(1));
10649
String fieldName = CachedMappings.remapField(fieldId);
@@ -147,7 +90,7 @@ public static Throwable remapThrowable(Throwable throwable) {
14790
throwableName = remapClasses(throwableName);
14891
}
14992

150-
Throwable remapped = new RemappedThrowable(message, cause, throwableName);
93+
Throwable remapped = new RemappedThrowable(message, cause, throwable, throwableName);
15194
remapped.setStackTrace(stackTrace);
15295
for (Throwable suppressed : throwable.getSuppressed()) {
15396
remapped.addSuppressed(remapThrowable(suppressed));
@@ -179,7 +122,16 @@ public static StackTraceElement remapStackTraceElement(StackTraceElement element
179122
methodName = remapMethods(methodName);
180123
}
181124

182-
return new StackTraceElement(remappedClass ? "MC" : null, element.getModuleName(), element.getModuleVersion(),
125+
String classLoaderName = element.getClassLoaderName();
126+
if (remappedClass) {
127+
if (classLoaderName == null) {
128+
classLoaderName = "MC";
129+
} else {
130+
classLoaderName += "//MC";
131+
}
132+
}
133+
134+
return new StackTraceElement(classLoaderName, element.getModuleName(), element.getModuleVersion(),
183135
className, methodName, fileName, element.getLineNumber());
184136
}
185137
}

src/main/java/dev/booky/stackdeobf/mixin/CrashReportMixin.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.booky.stackdeobf.mixin;
22
// Created by booky10 in StackDeobfuscator (18:35 20.03.23)
33

4+
import dev.booky.stackdeobf.mappings.RemappedThrowable;
45
import dev.booky.stackdeobf.mappings.RemappingUtil;
56
import net.minecraft.CrashReport;
67
import org.spongepowered.asm.mixin.Final;
@@ -10,6 +11,7 @@
1011
import org.spongepowered.asm.mixin.injection.At;
1112
import org.spongepowered.asm.mixin.injection.Inject;
1213
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
14+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
1315

1416
@Mixin(CrashReport.class)
1517
public class CrashReportMixin {
@@ -23,4 +25,20 @@ public class CrashReportMixin {
2325
public void postInit(String title, Throwable throwable, CallbackInfo ci) {
2426
this.exception = RemappingUtil.remapThrowable(throwable);
2527
}
28+
29+
@Inject(
30+
method = "getException",
31+
at = @At("HEAD"),
32+
cancellable = true
33+
)
34+
public void preExceptionGet(CallbackInfoReturnable<Throwable> cir) {
35+
// redirect calls to getException to the original, unmapped Throwable
36+
//
37+
// this method is called in the ReportedException, which
38+
// caused the "RemappedThrowable" name to show up in the logger
39+
40+
if (this.exception instanceof RemappedThrowable remapped) {
41+
cir.setReturnValue(remapped.getOriginal());
42+
}
43+
}
2644
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package dev.booky.stackdeobf.util;
2+
// Created by booky10 in StackDeobfuscator (22:33 14.04.23)
3+
4+
import dev.booky.stackdeobf.mappings.RemappingUtil;
5+
import org.apache.logging.log4j.core.impl.ExtendedStackTraceElement;
6+
import org.apache.logging.log4j.core.impl.ThrowableProxy;
7+
8+
import java.lang.reflect.Field;
9+
10+
final class Log4jRemapUtil {
11+
12+
private static final Field PROXY_NAME = getField(ThrowableProxy.class, "name");
13+
private static final Field PROXY_MESSAGE = getField(ThrowableProxy.class, "message");
14+
private static final Field PROXY_LOCALIZED_MESSAGE = getField(ThrowableProxy.class, "localizedMessage");
15+
private static final Field EXT_STACK_ELEMENT = getField(ExtendedStackTraceElement.class, "stackTraceElement");
16+
17+
private static Field getField(Class<?> clazz, String name) {
18+
try {
19+
Field field = clazz.getDeclaredField(name);
20+
field.setAccessible(true);
21+
return field;
22+
} catch (ReflectiveOperationException exception) {
23+
throw new RuntimeException(exception);
24+
}
25+
}
26+
27+
static void remapThrowableProxy(ThrowableProxy proxy) throws IllegalAccessException {
28+
// remap throwable classname
29+
if (proxy.getName() != null && proxy.getName().startsWith("net.minecraft.class_")) {
30+
PROXY_NAME.set(proxy, RemappingUtil.remapClasses(proxy.getName()));
31+
}
32+
33+
// remap throwable message
34+
if (proxy.getMessage() != null) {
35+
PROXY_MESSAGE.set(proxy, RemappingUtil.remapString(proxy.getMessage()));
36+
}
37+
if (proxy.getLocalizedMessage() != null) {
38+
PROXY_LOCALIZED_MESSAGE.set(proxy, RemappingUtil.remapString(proxy.getLocalizedMessage()));
39+
}
40+
41+
// remap throwable stack trace
42+
for (ExtendedStackTraceElement extElement : proxy.getExtendedStackTrace()) {
43+
StackTraceElement element = extElement.getStackTraceElement();
44+
element = RemappingUtil.remapStackTraceElement(element);
45+
EXT_STACK_ELEMENT.set(extElement, element);
46+
}
47+
48+
// remap cause + suppressed throwables
49+
if (proxy.getCauseProxy() != null) {
50+
remapThrowableProxy(proxy.getCauseProxy());
51+
}
52+
for (ThrowableProxy suppressed : proxy.getSuppressedProxies()) {
53+
remapThrowableProxy(suppressed);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)