Logging¶
Metaborg Core uses its own logging implementation of ILogger
. You can
ask Guice to inject it into your classes. Underneath it calls Slf4J to do
the logging. IntelliJ IDEA uses its own built-in Logger
implementation
that is called by the IntelliJLoggerAdapter
that’s bound to the Metaborg
Core Slf4J logger.
In unit tests where Guice is not availabel, you can inject the
PrintStreamLogger
. See the Bindings documentation for more
information.
Throwing and logging an exception¶
Often when you want to throw an exception, you first have to format the
exception message and then log the message and finally throw the exception.
The LoggerUtils#exception()
functions make this a whole lot easier.
throw LoggerUtils.exception(this.logger, RuntimeException.class,
"Error occurred in {}.", this.name);
This will format the message, construct the given exception class with the
current stack trace, and return it. You can then throw the exception.
The stack trace is cleaned up such that the LoggerUtils
class doesn’t appear
in the trace.
IntelliJ versus Spoofax¶
Both Spoofax Core (and related Metaborg libraries) and IntelliJ IDEA use slf4j
for logging. The Spoofax for IntelliJ plugin also uses slf4j for logging, so
it depends on org.slf4j:slf4j-api:1.7.10
.
Normally slf4j looks for a StaticLoggerBinder
implementation and bind to
that for logging. IntelliJ IDEA provides a StaticLoggerBinder
by default
(from org.slf4j:slf4j-log4j12:1.7.10
), so we’d like to bind to that. This is
slf4j’s default behavior, so we don’t have to add any dependencies for this.
However, if we try that, we get an exception:
java.lang.LinkageError: loader constraint violation: when resolving method “org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;” the class loader (instance of com/intellij/ide/plugins/cl/PluginClassLoader) of the current class, org/slf4j/LoggerFactory, and the class loader (instance of com/intellij/util/lang/UrlClassLoader) for the method’s defining class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type org/slf4j/ILoggerFactory used in the signature
Apparently there are two class loaders used to resolve the StaticLoggerBinder
,
and they interfere.
We can use our own logger binder (depend on org.slf4j:slf4j-simple:1.7.10
),
but this has some downsides: we have to configure the logger ourselves, and
can’t choose the StaticLoggerBinder
that slf4j should bind to. This causes
it to warn us that there are now two StaticLoggerBinder
classes and it doesn’t
know which one to bind to. It will pick one at random:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/daniel/.IdeaIC14/system/plugins-sandbox/plugins/spoofax-intellij/lib/slf4j-simple-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/daniel/apps/1507-IntelliJ/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
We don’t have a solution for this yet.