It would be truly awesome to get this to work! Triple yay!

I would keep it as simple as possible though, and not worry too much about
being super user friendly.
Asking folks to recompile to get this feature is not a big ask, and
avoiding complexity like weaving is a big advantage.

Not working for Log4j 1 etc is also not a problem, and may even further our
goal to get people to migrate.

On Sat, Jul 9, 2022 at 19:39 Gary Gregory <garydgreg...@gmail.com> wrote:

> There might be something to reuse from https://projectlombok.org/
>
> Gary
>
> On Sat, Jul 9, 2022, 06:05 Volkan Yazıcı <vol...@yazi.ci> wrote:
>
> > 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.)
> >
>

Reply via email to