Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.5.x] LoggerConfigurator requires an immutable map #6679

Closed
igmar opened this issue Oct 31, 2016 · 8 comments
Closed

[2.5.x] LoggerConfigurator requires an immutable map #6679

igmar opened this issue Oct 31, 2016 · 8 comments

Comments

@igmar
Copy link

igmar commented Oct 31, 2016

Creating a custom logger configurator in Java is pretty hard / impossible right now. LoggerConfigurator requires scala.collection.immutable.Map, which isn't easy in Java, also since the JavaConversions package only returns mutable maps.

I would be nice if a custom logger configurator can also be done from Java, and not requiring Scala.

In addition, following the directions as set on https://www.playframework.com/documentation/2.5.x/SettingsLogger#Using-a-Custom-Logging-Framework leads to ClassNotFound exceptions.

@wsargent
Copy link
Member

wsargent commented Oct 31, 2016

You can do a custom logger configurator in Java, although it's not pretty. Here's a compile time Java app:

class LoggerConfigurator {

    private final play.api.LoggerConfigurator delegate;

    public LoggerConfigurator(play.api.LoggerConfigurator delegate) {
        this.delegate = delegate;
    }

    public static Optional<LoggerConfigurator> fromClassLoader(ClassLoader classLoader) {
        return OptionConverters.toJava(LoggerConfigurator$.MODULE$.apply(classLoader)).map(LoggerConfigurator::new);
    }

    public void configure(Environment env) {
        delegate.configure(env.underlying());
    }

}

Example project: https://github.com/wsargent/play-java-compile-di

If you can put together a PR that puts together the docs and tests, we can merge it for 2.6...

@igmar
Copy link
Author

igmar commented Nov 1, 2016

It should work if I just do the configurator in Scala, right ? I'll do a pull for this

@gmethvin
Copy link
Member

gmethvin commented Nov 1, 2016

@igmar You could write the Java API version in Scala, but typically we like to write Java APIs in Java so they're more readable to Java programmers. You do end up with weird quirks like the LoggerConfigurator$.MODULE$.apply in Will's example, but it's much more obvious that it's Java API and meant to be used from Java.

@igmar
Copy link
Author

igmar commented Dec 8, 2016

Clear

@igmar
Copy link
Author

igmar commented Feb 10, 2017

I'm working on this one. Some things I ran into :

java.lang.ClassCastException: org.apache.logging.slf4j.Log4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
	at play.api.libs.logback.LogbackLoggerConfigurator.configure(LogbackLoggerConfigurator.scala:80)
	at play.api.libs.logback.LogbackLoggerConfigurator.init(LogbackLoggerConfigurator.scala:26)
	at play.core.server.DevServerStart$$anonfun$mainDev$1.apply(DevServerStart.scala:94)
	at play.core.server.DevServerStart$$anonfun$mainDev$1.apply(DevServerStart.scala:65)
	at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
	at play.core.server.DevServerStart$.mainDev(DevServerStart.scala:64)
	at play.core.server.DevServerStart$.mainDevHttpMode(DevServerStart.scala:54)
	at play.core.server.DevServerStart.mainDevHttpMode(DevServerStart.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at play.runsupport.Reloader$.startDevMode(Reloader.scala:234)
	at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3.devModeServer$lzycompute$1(PlayRun.scala:74)
	at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3.play$sbt$run$PlayRun$$anonfun$$anonfun$$anonfun$$devModeServer$1(PlayRun.scala:74)
	at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3.apply(PlayRun.scala:100)
	at play.sbt.run.PlayRun$$anonfun$playRunTask$1$$anonfun$apply$2$$anonfun$apply$3.apply(PlayRun.scala:53)
	at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)

This is a Java Play 2.5.12 project, with the logging configurator in Scala.

  1. Having some doubts on how to proceed.
  • I prefer to proxy the Java class to a Scala one, and doing the right type conversions. This seems hard to do from Java, probably easier to do from Scala
  • Write it in Java, do the conversions, and reference the underlying Scala object

Any advice ?

@igmar
Copy link
Author

igmar commented Feb 13, 2017

Did some more digging :

sbt plugins
       <snip>
	play.sbt.PlayLayoutPlugin: enabled in root
	play.sbt.PlayLogback
	play.sbt.PlayNettyServer: enabled in root
       <snip>

play run blows up with the above stacktrace. Looking at the code that loads it :

        LoggerConfigurator(this.getClass.getClassLoader) match

it seems that is also contains the Logback logger configurator on the classpath, which the JVM seems to pick first.

running in prod mode seems fine :

(Starting server. Type Ctrl+D to exit logs, the server will remain in background)

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Oops, cannot start the server.
java.util.NoSuchElementException: None.get
	at scala.None$.get(Option.scala:347)
	at scala.None$.get(Option.scala:345)
	at logging.Log4JLoggerConfigurator.configure(Log4JLoggerConfigurator.scala:38)
	at logging.Log4JLoggerConfigurator.configure(Log4JLoggerConfigurator.scala:33)
	at play.api.inject.guice.GuiceApplicationBuilder$$anonfun$applicationModule$1.apply(GuiceApplicationBuilder.scala:102)
	at play.api.inject.guice.GuiceApplicationBuilder$$anonfun$applicationModule$1.apply(GuiceApplicationBuilder.scala:102)
	at scala.Option.foreach(Option.scala:257)
	at play.api.inject.guice.GuiceApplicationBuilder.applicationModule(GuiceApplicationBuilder.scala:101)
	at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:181)
	at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:123)
	at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
	at play.core.server.ProdServerStart$.start(ProdServerStart.scala:47)
	at play.core.server.ProdServerStart$.main(ProdServerStart.scala:22)
	at play.core.server.ProdServerStart.main(ProdServerStart.scala)

Which in this case, is expected behaviour (it's missing the right config files). It however does seem to pick the right configurator.

@mkurz
Copy link
Member

mkurz commented Mar 9, 2017

Reference: #6045

@mkurz
Copy link
Member

mkurz commented May 10, 2017

Fixed via #7187

@mkurz mkurz closed this as completed May 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants