Hey there! Is there any way to get the file name i...
# random
m
Hey there! Is there any way to get the file name in a Kotlin file? This functionality could be helpful with e.g. logging, where the other approaches have drawbacks: • need to manually define tag (manual, can be messed up when filename changes) • or: throw a full
Throwable()
, to only get the file / class name (performance issues) • or: using
reified
to get the class name (will print weird log tags when scope changes (e.g. it will print
CoroutineScope
in some instances))
s
On the JVM:
Copy code
javaClass.protectionDomain.codeSource.location
(maybe. I haven't tested it 😁)
☝️ after trying out my above suggestion in a few scenarios, it may not be that helpful 😞. It seems more likely to return a directory than a specific file. And if the class is loaded from a
.jar
I expect it'll be even less helpful.
👍 1
m
@ephemient thanks, that is exactly what I’m looking for! Unfortunately it’s not implemented yet.
So I guess that means the best approach is to use a
private const val TAG = "SomeClass"
at the beginning of the file, and reference it in all log calls. Because that at least does not have a performance drawback, or even printing the wrong tag (we have to pass it manual though).
f
If you have a top-level const then you can use reflection since you pay the prize only once during initialization and then its fixed for the entire runtime, no need to manually create strings.
If you’re really concerned about performance and really want to get what is asked for in the ticket above, then you could easily implement it with a compiler plugin. 🙂 Or contribute it to Kotlin. It’s actually a really nice task, maybe I have some time and can take care of it, since it’s trivial.
e
depending on how your use is set up, it may be doable as a bytecode transformation as well, with the benefit being that it isn't tied to Kotlin version (but downside of being tied to JVM of course)
v
Does it have to be the file name? Can you use package to locate the code instead?
Copy code
"${obj.javaClass.packageName}.${obj.javaClass.name}"
e
javaClass
has same issue as
reified
in the original message: "will print weird log tags when scope changes (e.g. it will print
CoroutineScope
in some instances)"
m
@Fleshgrinder that’s actually a nice idea, implementing this with a compiler plugin 🙂 Contributing to Kotlin would be even better 👍
@ephemient exactly, that’s what we found as well. And those instances are really easy to miss, because code changes, and these scopes are not defined explicitly (so very likely to get wrong)
We’ll see, most likely we will move with the static tag for now. That’s the most performant, and less likeliest do get wrong.
Indeed, I tested this locally today, and the static tag approach was 67x faster on JVM tests, and 100-197x faster on Android instrumented tests compared to inferring the tag via
Throwable()
. Also, the Android tests very often failed with errors like
java.lang.OutOfMemoryError: Failed to allocate a 16 byte allocation with 2572768 free bytes and 2512KB until OOM, target footprint 268435456, growth limit 268435456; giving up on allocation because <1% of heap free after GC.
- because it seems the
Throwable().stackTrace
will not be GC’ed correctly (maybe because the implementation is native?), and the memory seems to be to become too fragmented to be cleaned up. So this will not only gain performance for us, but it will actually fix some weird OutOfMemoryError we saw when the app is used for longer time.
n
For class/file name in logging, we currently settled on
private val logger = mu.KotlinLogging.logger {}
as the first statement after imports. But I also dreamed for a long time about a compiler plugin which would effectively convert a `logger.warn { "Bring a towel!" }`into a
logger.warn(file = "Foo.kt", line=123) { "Bring a towel!" }
j
the class name can be retrieved via: MethodHandles.lookup().lookupClass().name on the JVM, then put that inside an inline function. At least that's how the loggers do it
f
I actually started working on a fully static logger implementation that adds diagnostic data at compile time once and had the filename and line number stuff. I did not continue with it because of the announced changes to the compiler (IR) and because of time (classic). Log4Shell is actually a good reason to pick this up again. Logging should be simple and without any boilerplate or ceremony, regardless of whether we are in a class or function (top-level and extension). A thing I’m never sure about is if the Kotlin community as a whole is always looking for multi-platform, or if a JVM only implementation is good enough. For me the only use-case is JVM, always.
j
the only thing you really want from the runtime is which thread it's currently running on and filling in some variables if you're logging a String template, other than that, static logging sounds like a brilliant idea, it definitely removes some of the logging overhead.
1071 Views