Inspired by this SO post <https://stackoverflow.com/a/72437386/1278899> and with some help from Piotr <https://stackoverflow.com/a/72916795/1278899>, I have drafted an example where I redefine a class such that every logger call is preceded with a static source location capture. The experiment aims to replace the current source location capture that uses an exception-based expensive mechanism with inlining the source location using bytecode weaving. The sources are publicly available on GitHub. <https://github.com/vy/asm-playground/tree/master/src/main/java/com/vlkan> In a nutshell, the magic is as follows:
I have a logger library (Log4j.java <https://github.com/vy/asm-playground/blob/master/src/main/java/com/vlkan/Log4j.java>) as follows: public static final ThreadLocal<SourceLocation> LOCATION_REF = ThreadLocal.withInitial(SourceLocation::new); public static void log() { SourceLocation location = LOCATION_REF.get(); boolean locationProvided = location.lineNumber > 0; if (!locationProvided) { StackTraceElement[] stackTraceElements = new Throwable().getStackTrace(); // Skip the first element pointing to this method. StackTraceElement stackTraceElement = stackTraceElements[1]; location.init( stackTraceElement.getFileName(), stackTraceElement.getClassName(), stackTraceElement.getMethodName(), stackTraceElement.getLineNumber()); } System.out.format( "[%s] %s%n", location, locationProvided ? "provided location" : "populated location"); } Here note how `log()` uses a thread-local to see if there is already a `SourceLocation` provided. If so, it leverages that, otherwise it populates the source location using the stack trace of an exception. Below is my actual application (AppActual.java <https://github.com/vy/asm-playground/blob/master/src/main/java/com/vlkan/AppActual.java>), that is, what the actual/existing user code looks like: public static void main(String[] args) { System.out.println("should log at line 9"); log(); System.out.println("nothing to see here"); System.out.println("should log at line 12"); log(); f(); } private static void f() { System.out.println("adding some indirection"); System.out.println("should log at line 19"); log(); } I want to transform this into the following expected form (AppExpected.java <https://github.com/vy/asm-playground/blob/master/src/main/java/com/vlkan/AppExpected.java>) that exploits the `LOCATION_REF` thread-local to inline the source location information: public static void main(String[] args) { System.out.println("should log at line 9"); LOCATION_REF.get().init("AppExpected.java", "com.vlkan.AppExpected", "main", 9); log(); System.out.println("nothing to see here"); System.out.println("should log at line 12"); LOCATION_REF.get().init("AppExpected.java", "com.vlkan.AppExpected", "main", 12); log(); f(); } private static void f() { System.out.println("adding some indirection"); System.out.println("should log at line 19"); LOCATION_REF.get().init("AppExpected.java", "com.vlkan.AppExpected", "main", 19); log(); } And... 👉 AppTransforming.java <https://github.com/vy/asm-playground/blob/master/src/main/java/com/vlkan/AppTransforming.java> 👈, my dear friends, performs exactly this transformation: converts `AppActual` bytecode into `AppExpected`. 😍 I think we can extend this experiment to implement zero-cost source location capture for Log4j. Though I will appreciate your help on some loose ends. Assuming we have a bullet-proof mechanism to inline source location capture given a class, what is the right way to ship this? As a Maven plugin that kicks in at compile time? Wouldn't that make this feature impossible to use without recompiling user sources? As a runtime utility? If so, what about the cost of classpath scanning & weaving? If the bytecode weaving only intercepts at Log4j API calls, this won't work for Log4j 1 bridge, SLF4J, or any other indirect access to the Log4j API. What do you think? I have used a thread-local to pass the source location to the caller, is there a better alternative? (Putting aside the dynamic-scoped variables to be shipped with Loom.)