Giter VIP home page Giter VIP logo

Comments (9)

Hakky54 avatar Hakky54 commented on May 31, 2024

Hi Olaf,

That error is indeed the classic error and pretty annoying, but it should be resolvable. LogCaptor is still using SLF4J v1.7.x but I am planning to bump it to v2.x.x However LogCaptor is compatible with Log4J2 and currently also tested with the latest version 2.19.0

Looking at your issue you have log4j-slf4j-impl on your classpath. That dependency relies on SLF4J v1.7.x and is compatible with LogCaptor. Excluding log4j-slf4j-impl from your classpath during the test phase should work as I have a working example here: https://github.com/Hakky54/java-tutorials/blob/main/log-captor-examples/log-captor-with-slf4j-log4j/pom.xml

So I have the feeling that another dependency in your project might be causing this issue. If another dependency has a transitive dependency on SLF4J v2.x.x than it will fail to captor the logs and give you indeed those warnings. Can you validate that by running a dependency tree and check which SLF4J version iss being used?

from log-captor.

Zordid avatar Zordid commented on May 31, 2024

Yes, and the move to SLF4J 2.x.x is deliberate... :-( I cannot get this out as my main logging library is io.github.microutils:kotlin-logging-jvm:3.0.0

They moved from API 1.7 to API 2.x - and thus I get into problems. Formerly I used uk.org.lidalia:slf4j-test:1.2.0 to test my logs in unit tests, but that one is very, very old and also cannot work with API 2.x.

To be honest, this whole world of Log4J 1, 2, API versions, implementations, bindings, etc is completely frustrating me. :-/

from log-captor.

Hakky54 avatar Hakky54 commented on May 31, 2024

No worries, I understand your frustration. It is awfull that there are so many logging frameworks and you are getting this issue because other dependencies which are using different versions.

My overall suggestion for your project would be to make sure you use 1 logging framework. Either SLF4J or Log4J2 and redirect all logs to that logging framework. For this you need to analyse your dependency tree. And next to that make sure you are using one specific version by excluding the transitive dependencies and by specifiying it in the dependency management section (if you are using maven). But that is something which can take some time to refactor and not needed to just get LogCaptor working.

The main reason why I didn't upgraded SLF4J for LogCaptor to version v2.x.x is because Log4j2 maintainers didn't released an adapter for it yet. They have one for SLF4J v1.7.x which I am using, see here for the dependency: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-to-slf4j/2.19.0

There are couple of things what you can do. Either one of the options alone should work:

  1. Exclude SLF4J from kotlin-logging-jvm during the test phase. In that way LogCaptor will use SLF4J version 1.7.x I tried this in the past, but could not get it working. So I am unfortunately not able to provide you examples...
  2. Add ch.qos.logback:logback-classic:1.4.3 and org.apache.logging.log4j:log4j-to-jul:2.19.0 as a test dependency. Logback is being used by LogCaptor as the engine to capture logs. This version of Logback is compatible with SLF4J v2.x.x and the log4j-to-jul will forward your own logs to java util logging which LogCaptor is able to capture regardless of the SLF4J version. In that way you don't need an adapter for Log4J2 for SLF4J v2.x.x and it should just work with the java util logging adapter.

Can you give it a try and share your results here? I am also curious which option you would prefer 😄

from log-captor.

Zordid avatar Zordid commented on May 31, 2024

Oh gosh... I don't even know the differences between slf4j and log4j2... this is all so, so confusing. Sorry to bother you with this...

I do use Gradle, not Maven. All I did to start my logging is to rely on kotlin-logging-jvm, which adds this transitive dependency: slf4j-api:2.0.1
When you start the application with this single dependency, you get this warning:
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.

Sounds reasonable, as you need to also add logging implementations, aka providers.
So I add: org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0 to my dependencies.
This leads to:
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...

Ok, I do as instructed - adding org.apache.logging.log4j:log4j-core:2.19.0 to my dependencies.
We are now at:

    implementation("io.github.microutils:kotlin-logging-jvm:3.0.0")
    implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0")
    implementation("org.apache.logging.log4j:log4j-core:2.19.0")

This is the first working solution. I do not really know how I would name this. Am I using Log4J2 now? Or SLF4J? API 1.7 or 2.0.x? Of what. Here the confusion is already happening...

Now - I want to write a unit test, making sure some code part logs an error.
I now add

testImplementation("io.github.hakky54:logcaptor:2.7.10")

And now my code crashes with this:
org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j

Okay, we now have a conflicting dependency, right? I have NO clue what log4j-slf4j-impl vs log4j-to-slf4j are. We do people come up with such confusing names... grrrr And where the hack is my second "2" as in log4j-slf4j2-impl??

I try to exclude one dependency at test runtime using

configurations.testRuntimeOnly {
    // don't allow two log4j implementations to be present at test time
    exclude(module = "log4j-slf4j-impl")
}

This does not change anything.

So I try your second approach, adding:

    testImplementation("io.github.hakky54:logcaptor:2.7.10")
    testImplementation("ch.qos.logback:logback-classic:1.4.3")
    testImplementation("org.apache.logging.log4j:log4j-to-jul:2.19.0")

Which changes behaviour to this problem:
SLF4J: Class path contains multiple SLF4J providers.
SLF4J: Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@7fc95ed5]
SLF4J: Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@1771617f]
SLF4J: See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual provider is of type [org.apache.logging.slf4j.SLF4JServiceProvider@7fc95ed5]

It feels I am getting closer, but still I am not there... now that there are TWO implementations and the "wrong" one gets picked, testing the logging still does not work.

Why on earth is logging such an overly complex thing? Working through Stack Overflow is a nightmare with all those wrong and outdated answers... :-(

Thanks a lot for trying to help!

from log-captor.

Hakky54 avatar Hakky54 commented on May 31, 2024

Yes, you are definitely getting closer but we still need to do some minor changes. Thank you by the way for explaining all the stepds you did, it gives me a better unstanding of what you did and what went wrong. I think it is pretty easy to solve as it is now clear for me that you don't need Log4J2. It seems like you added it by accident because the warning message of SLF4J was not clear enough. I would suggest the following changes to your project:

Can you remove these parts in your gradle build:

implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.19.0")
implementation("org.apache.logging.log4j:log4j-core:2.19.0")
testImplementation("org.apache.logging.log4j:log4j-to-jul:2.19.0")

configurations.testRuntimeOnly {
    // don't allow two log4j implementations to be present at test time
    exclude(module = "log4j-slf4j-impl")
}

You can keep the following dependency:

testImplementation("ch.qos.logback:logback-classic:1.4.3")

Can you adjust the logcaptor dependency declaration:

testImplementation("io.github.hakky54:logcaptor:2.7.10"){
    exclude("ch.qos.logback", "logback-classic")
    exclude("org.apache.logging.log4j", "log4j-to-slf4j")
}

So your resulting gradle build configuration should have the following dependencies:

implementation("io.github.microutils:kotlin-logging-jvm:3.0.0")

testImplementation("ch.qos.logback:logback-classic:1.4.3")
testImplementation("io.github.hakky54:logcaptor:2.7.10"){
    exclude("ch.qos.logback", "logback-classic")
    exclude("org.apache.logging.log4j", "log4j-to-slf4j")
}

Can you retry and share your results?

from log-captor.

Zordid avatar Zordid commented on May 31, 2024

Hi again! Thanks for all your time and effort, I really appreciate this!

So. Your last configuration does the job, but only for test side. Everything runs smooth there and LogCaptor can do its job.

But... on the production side, when I run my product, it complains again about no providers for logging:
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.

This is exactly where I started: using the main logging util dependency kotlin-logging which does not include any providers.

If I only knew now which provider/dependency I can add to the implementation dependencies without disturbing the test side again...

from log-captor.

Hakky54 avatar Hakky54 commented on May 31, 2024

So kotlin-logging-jvm provides slf4j-api and it is up to you which slf4j implementation you want to add to your project. For hobby projects I prefer to use org.slf4j:slf4j-simple which just prints the logs to the console, hoewever for bigger projects or enterprise projects I prefer to use Logback which is capable of doing more advanced stuff like appending the logs to a file and also to the console and other features, see here for the documentation: https://logback.qos.ch/manual/index.html

If you want to use org.slf4j:slf4j-simple than your configuration would be:

implementation("io.github.microutils:kotlin-logging-jvm:3.0.0")
implementation("org.slf4j:slf4j-simple:2.0.3")

testImplementation("ch.qos.logback:logback-classic:1.4.3")
testImplementation("io.github.hakky54:logcaptor:2.7.10"){
    exclude("ch.qos.logback", "logback-classic")
    exclude("org.apache.logging.log4j", "log4j-to-slf4j")
}

configurations.testRuntimeOnly {
    exclude(module = "slf4j-simple")
}

If you want to use logback, than your configuration would be:

implementation("io.github.microutils:kotlin-logging-jvm:3.0.0")
implementation("ch.qos.logback:logback-classic:1.4.3")

testImplementation("io.github.hakky54:logcaptor:2.7.10"){
    exclude("ch.qos.logback", "logback-classic")
    exclude("org.apache.logging.log4j", "log4j-to-slf4j")
}

This should do the trick for you and resolve the last issue.

from log-captor.

Hakky54 avatar Hakky54 commented on May 31, 2024

@Zordid Any updates from your side, did it work?

from log-captor.

stale avatar stale commented on May 31, 2024

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

from log-captor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.