32

I'm using log4j2 (2.11.1) with Java 11 and attempting to get a Logger object using:

private static final Logger LOG = LogManager.getLogger();

(Imported from log4j-api in org.apache.logging.log4j)

At runtime, I receive the following error:

WARNING: sun.reflect.Reflection.getCallerClass is not supported. This will impact performance.
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.UnsupportedOperationException: No class provided, and an appropriate one cannot be found.
at 
org.apache.logging.log4j.LogManager.callerClass(LogManager.java:555)
    at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:580)
    at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:567)
    at app.App.<clinit>(App.java:11)

Which does make sense - getCallerClass is not supported and so the logger is unable to determine the class name.

Is it supposed to work this way? Surely I don't have to hard-code the class name into each logger?

Peter Crotty
  • 301
  • 2
  • 9
Daniel Scott
  • 7,418
  • 5
  • 39
  • 58
  • 3
    There's been some "clean up" in the Java API and underlying system code. My guess is something that used to work doesn't anymore, and the Apache folks haven't got around to fixing it. I don't know what you can do other than downgrade to an earlier version of Java or use a different log manager. – markspace Oct 23 '18 at 16:27
  • Could you be precise over which dependency of `log4j2 (2.11.1)`? – Naman Oct 23 '18 at 16:42
  • It's log4j-api - edited question to include – Daniel Scott Oct 23 '18 at 17:05
  • 5
    @markspace wow, really? One of the most popular logging frameworks isn't compatible with current LTS of Java? – Daniel Scott Oct 23 '18 at 17:07
  • @DanielScott I find that third party efforts to outdo [existing standard functionality](https://docs.oracle.com/en/java/javase/11/docs/api/java.logging/java/util/logging/package-summary.html) tend to believe their “clever” hacks are what make them superior. I don’t share their belief. – VGR Oct 23 '18 at 17:36
  • 2
    The official API, [`StackWalker.getCallerClass()`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StackWalker.html#getCallerClass()), has been introduced with Java 9. According to [the changelogs](https://logging.apache.org/log4j/2.x/changes-report.html#a2.9.0), support for it has been added even in log4j2 2.9.0. Perhaps, you need a build specifically for Java 9+… – Holger Oct 23 '18 at 17:45
  • 5
    When I download pre-built binaries from https://logging.apache.org/log4j/2.x/download.html I get multi-release jars, which will automatically select the implementation version when running under Java 9 or newer. So the crucial point is how the version, you are using, has been built. – Holger Oct 23 '18 at 17:57
  • 5
    @Holger is right mostly, seems like you're not using the library under java9+ which is where the [updated implementation of StackLocator](https://github.com/apache/logging-log4j2/blob/master/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java) is. Not able to share a screenshot here in the comments, but the MR-JAR of log4j includes a different implementation of StackLocator at `../.m2/repository/org/apache/logging/log4j/log4j-api/2.11.1/log4j-api-2.11.1.jar!/META-INF/versions/9/org/apache/logging/log4j/util/StackLocator.class` – Naman Oct 23 '18 at 17:58
  • Yep, I have that class file in my dependency... still not working though – Daniel Scott Oct 23 '18 at 19:33
  • And the file is identical to the one in the same location in my shaded jar – Daniel Scott Oct 23 '18 at 19:38

2 Answers2

33

The reason was that the multi-release class files were not being picked up from META-INF/versions/* because I hadn't set the multi-release flag when I built my shaded jar.

I needed to add:

Multi-Release:true

To my manifest, and everything started working.

Daniel Scott
  • 7,418
  • 5
  • 39
  • 58
  • 2
    Yep, I shouldn't have forgotten as well that [log4j is a multi-release jar](https://stackoverflow.com/questions/46662286/) – Naman Oct 24 '18 at 02:40
  • 1
    @Daniel - Can you expand on that? Which manifest did you add that line to? I don't get what "shaded jar" is. – Ajoy Bhatia Jul 25 '19 at 16:25
  • A 'shared' jar is a jar which repackages your code with it's dependencies. It's the manifest in this jar which contains the additional line – Daniel Scott Jul 25 '19 at 21:08
  • @DanielScott so every jar which calls logmanager getlogger should be a multi-release jar if it uses log4j 2.11? – Gaurav Oct 24 '19 at 07:06
  • Best to test that yourself, but I expect that as long as the jar you've built is multi-release and it includes log4j it should be fine – Daniel Scott Oct 24 '19 at 07:27
  • 1
    @DanielScott this solution still not working with jdk14 and log4j2 (2.13.2) . I tried to insert the property in the manifest Multi-Release:true as property but the same error appear. There same changes with the new log4j2 ? – Francesco Capodanno May 02 '20 at 11:03
  • Are you certain it's present in the manifest? Did you unpack the jar to check? – Daniel Scott May 02 '20 at 11:28
13

The answer by @DanielScott is correct. When using the Gradle Shadow plugin, I added the following to my build.gradle to appended the Multi-Release:true flag to the manifest.

jar {
    manifest {
        attributes 'Multi-Release': 'true'
    }
}
Yuri
  • 4,254
  • 1
  • 29
  • 46
Chad Lad
  • 464
  • 5
  • 7