What I want to have is a function that is able to print the lines + file from where it was called.
The behavior would be something like this:
log("x") //Called at line 15 of file Blax.scala
Should print
Blax.scala, line 15, message: x
Something analogous to the following code for c++:
void _log(const char* message, const char* file, int line) { std::cout << file << ", " << line << ", message: " <<
message
<< "\n";}
#define log(message) _log(message, __FILE__, __LINE__)
Is this possible to do in Scala? Is it possible to do it in a way that is half-readable and somewhat compatible with future and older version of the compiler?
I've been trying to implement something similar based on the macro section here: https://docs.scala-lang.org/overviews/macros/overview.html
But the code is extremely hard to read and the articles don't seem to cover anything that closely related to what I need.
I guess my question could be split into two or three:
a) Is it possible to do any type of "traditional" code inlining in Scala by calling a macro just like a function?
So basically calling blax(a)
which expands into impl_blax(a, "b", __C__)
before compilation.
b) What is the "recommended" way of getting the current line number and file name at compile time in Scala?
c) If either a or b is impossible, is there still away to accomplish what I want?
Clarification:
Yes, this is easily doable using a Stack trace, but throwing an exception and getting the Stack trace is unbearably slow and not suitable for anything but certain niche debug cases. I'm looking for a no-overhead or low overhead solution, hence why I asked for one using macros
Edits (In order to answer flags):
@Seth Tisue on the marking of the question as duplicate
If the question you marked this as a duplicate of solves my problem I'd love to hear how. Some of the links in the original questions lead to 404, the code of the highest voted answer (which is not marked correct) doesn't seem to do anything related to what I want and parts of it are marked as deprecated in scala 2.12... I did stumble upon the question but I hoped that being more explicit about the problem may yield an answer which that previously linked 4 years old question doesn't have.
Unless you want to do this as an exercise and hence roll your own solution, I'd say the recommended way is to reuse libraries that exist, have been tested, and fulfil your requirements.
I found two candidates:
The sourcecode library is based on macros and provides metadata at compile-time.
Example (taken from their page adjusted to your question):
object Main extends App {
def log(message: String)(implicit line: sourcecode.Line, file: sourcecode.File) =
println(s"${file.value}, line ${line.value}, message: $message")
log("x")
}
This will print:
/Users/jhoffmann/Development/sourcecode/src/main/scala/Main.scala, line 5, message: x
As a standard alternative just use plain old logging. Take a look at scala-logging. Using logback as backend you can use %file
, %line
and %message
in your pattern.
As an example, consider the following patten configured in logback.xml
:
<pattern>%file, line %line, message: %message%n</pattern>
Then this code:
object Main extends App with LazyLogging {
logger.info("x")
}
will print:
Main.scala, line 4, message: x
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With