GitHub user mcmacker4 edited a discussion: No use of `inline` in 
`log4j-api-kotlin`?

As a preface, i will say that i am writing this here but I am not entirely sure 
that this is the appropriate forum. It looks to me as if `log4j-api-kotlin` is 
an independent project based on the fact that it is hosted in a [different 
github repo](https://github.com/apache/logging-log4j-kotlin) and it is not 
included in `log4j-bom`. **If this is not the correct forum, I would appreciate 
being pointed in the right direction**. I didn't want to open an actual github 
issue in the repo before even discussing this somewhere, but that repo doesn't 
have discussions enabled, so I will use this one as fallback.

---

While i was experimenting with reactive spring, i added log4j's kotlin api, 
which is supposed to use kotlin's super cool powers to make `log4j` more kotlin 
idiomatic by moving lambdas to the last argument and promoting string templates 
instead of log4j's template arguments.

Another of kotlin's powers is function call inlining. What i noticed, though, 
is that none of the lambda based functions in `KotlinLogger` are inline.

**Was this discussed at any point?** I cannot find any discussion or issues 
related to this topic.

Designing KotlinLogger's lambda based functions to be inline would allow things 
like suspending in the middle of the lambda.

I noticed this quirk when i wanted to log the current coroutine context. The 
function `currentCoroutineContext()` from `kotlinx-coroutines` is a suspending 
function, but the following code will not compile:

```kotlin
// this would be in a class
suspend fun fetchUser() {
    // logger is defined in the companion object, as recommended in the docs 
    logger.info { "Context: ${currentCoroutineContext()" }
                              ^ compiler error
}
```

The problem here is that, since the lambda passed to the `info()` method is 
neither `suspend` nor inlined, you cannot call a suspending function inside 
that lambda. Marking the lambda as `suspend` would not allow use in regular 
non-`suspend` code.

This is because the implementation delegates to `logIfEnabled`, turning the 
lambda reference into a `org.apache.logging.log4j.util.Supplier`. On the other 
hand, if the `info()` method was redesigned to allow inlining, the error would 
disappear. See this simple example:

```kotlin
inline fun info(supplier: () -> String) {
    if (delegate.isInfoEnabled)
        delegate.info(supplier())
}
```

With this change, the method call would be translated into the if statement in 
the method body, and the call to the lambda would also be inlined.

This function:
```kotlin
fun main() {
    val logger = logger("com.example")
    logger.info { "Message: ${expensiveOperation()}" }
}
```

Compiles into this code (decompiled with fernflower, slightly cleaned up for 
readability)
```java
public static final void main() {
    KotlinLogger logger = LoggingFactoryKt.logger("com.example");
    if (logger.getDelegate().isInfoEnabled()) {
         ExtendedLogger delegate = logger.getDelegate();
         delegate.info("Message: " + expensiveOperation());
    }
}
```

As you can see, the call to `expensiveOperation()` happens directly in `main`, 
but only if the `info` level is enabled.

What this means is that now, if the call site is a suspend function, you can 
call a suspend function in the lambda and, after compilation, it would be 
called directly in the suspend function, which is legal, so the first code 
block in this post (the one that failed to compile when calling 
`currentCoroutineContext()`) would compile and work just fine.

GitHub link: https://github.com/apache/logging-log4j2/discussions/4146

----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: [email protected]

Reply via email to