How to use logback-classic inside plugin? Conflicts with clion jar containing log4j binding

Answered

In my plugin I need to output logging messages with line number, time and so on. So for that purpose I need some logging framework which output I would redirect to ui.

So I tried using logback-classic and providing custom appender, that would output messages to ui. To ininitialize the appender in my plugin I cast Logger returned from slf4j to logback-classic. But slf4j can be bound only to one logging framework at a time and when I run plugin, slf4j binds to log4j from clion jar.

I tried to exclude the log4j from the classpath but that didn't work. I also tried to use log4j that clion uses instead of logback-classic, but I was unable to get log4j logger because it is deprecated.

 Can I use some slf4j logging framework (like logback-classic) inside of a plugin? Or can I somehow remove log4j binding from the classpath?

0
9 comments

Here are the conflicts with log4j binding: 

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/vol0ncar/.gradle/caches/modules-2/files-2.1/com.jetbrains.intellij.clion/clion/2021.3.3/f8447b540fa4bc964c57e12e331280c55406aba4/clion-2021.3.3/lib/3rd-party-rt.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/vol0ncar/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/logback-classic-1.2.3.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.Log4jLoggerFactory]
0

Here is the code where I cast slf4j logger to logback-classic logger and because the log4j binding is used, the error is raised here: 

(newLogger.underlyingLogger as Logger).getAppender("ClientAppender").let {
(it as ClientLogAppender).outputWindow =
project.service<OutputWindowProvider>().outputs[OutputType.CLIENT_LOG]
}
0

Can you please share the dependencies section in your Gradle build script?

0

Yes, of course

val coroutinesVersion by extra("1.5.2")
val protobufVersion by extra("3.18.0")
val grpcVersion by extra("1.40.1")
val grpcKotlinVersion by extra("1.1.0")

dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutinesVersion")

// grpc and protobuf
implementation("com.google.protobuf:protobuf-java:$protobufVersion")
implementation("com.google.protobuf:protobuf-java-util:$protobufVersion")
implementation("io.grpc:grpc-netty-shaded:$grpcVersion")
implementation("io.grpc:grpc-protobuf:$grpcVersion")
implementation("io.grpc:grpc-stub:$grpcVersion")
implementation("io.grpc:grpc-kotlin-stub:$grpcKotlinVersion")
compileOnly("javax.annotation:javax.annotation-api:1.3.2")
implementation("com.google.protobuf:protobuf-kotlin:$protobufVersion")

implementation("ch.qos.logback:logback-core:1.2.3")
implementation("ch.qos.logback:logback-classic:1.2.3")
implementation("io.github.microutils:kotlin-logging-jvm:2.1.20")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.6.0")

implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2")
}
0

Why don't you simply use slf4j in your plugin, without specifying any implementation? This will use the implementation provided by CLion, which is log4j in CLion 2021.3 and java.util.logging in CLion 2022.1.

0

The logger output is redirected to ui by Appender, so I need to access the Appender directly to specify ui component. For that I cast slf4j logger to concrete implementation of logback-classic, and then I grab appender and specify ui component (I added the code earlier in comments)

0

If you need to display logs coming from your code in your UI, the best solution is to implement an entirely separate mini-logging framework, not depending on SLF4J or anything else, and use that to keep track of your logs. Your framework can also forward logs to the SLF4J logger if you need to write your logs to idea.log too.

Any solution relying on casting a logger instance to a specific implementation class is very likely to break in CLion 2022.1, which will switch from log4j to java.util.logging.

0

I'll think about implementing a separate logging. I was wondering may be I could use java.util.logging or something like tinylog, which does not depend on SLF4J, is it reasonable idea? Or something can break? 

0

Tinylog should work, yes.

0

Please sign in to leave a comment.