I have a question related to how logging with sl4j+logback/log4j2 works in web applications deployed on some container like jetty/tomcat/etc.
We are starting our application with embedded jetty. We have 2 github repos:
repoA which has all the logic related to configuring and starting the embedded jetty. This repo has dependency on the required jetty libraries like jetty-server, jetty-webapp etc.
repoB which only has the actual servlet logic.
We copy all the gradle dependencies of repoA to some location say: /home/jettyClasspath and all dependencies of repoB to the classic location webapps/ROOT/WEB-INF/lib/ (the webapp's classpath). I want all the logging from repoA to go to the file say jetty.out and all logging from repoB to go to the file application.log. I couldn't figure out how I can achieve this with just slf4j + log4j2. Since there can only be one log4j2.xml per JVM, how can I say ANY logging from repoA to go to one file and the other way for repoB.
Next thing that I tried is I thought we can use logback for repoA and log4j2 for repoB. To achieve that, I added dependency to slf4j + logback in repoA and slf4j + log4j2 in repoB. This solved my purpose: In logback.xml I said any logging use FileAppender with file as jetty.out and in log4j2.xml I said any logging use file as application.log.
However, with this approach, from the webapp's perspective there are 2 slf4j bindings available: log4j2 from webapp's classloader, and logback from the parent classloader. Hence, it gives me the following warning:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/y/libexec/webapps/ROOT/WEB-INF/lib/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/yahoo/force/libexec/forceHttpServer/jars/slf4j-log4j12-1.7.25.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.apache.logging.slf4j.Log4jLoggerFactory]
The warning is very expected as slf4j will indeed see 2 bindings. But how can I solve this. I assume same scenario can happen in standalone container also. Jetty or tomcat can use logback for it's own logging and the application can chose log4j2. How is this warning avoided in such cases.
Please let me know if I can provide any extra info.
Related
I'm in the process of upgrading a Vaadin Flow application from Vaadin 14 to Vaadin 22. I'm using Gradle 6.9.1 to build the application. When I run the application in Eclipse, I get warnings and errors in the logs identical to those documented in issue 11982. According to this issue, this initial warning about the vaadin-dev-server dependency is the most relevant message:
WARN [] [VaadinServletService] [qtp819245704-14] no DevModeHandlerManager implementation found but dev server enabled. Include the com.vaadin.vaadin-dev-server dependency.
I have included the dependency in my build.gradle file. Here are the dependencies that are relevant to Vaadin:
dependencies {
implementation "com.vaadin:vaadin-core:22.+"
implementation "com.vaadin:vaadin-dev-server:9.0.0"
}
I can see vaadin-dev-server-9.0.0.jar in my Referenced Libraries within Eclipse along with the other Vaadin .jar files. All of them are located in my Gradle cache.
Am I missing something, or is there anything I can do to resolve this issue? Let me know if there's any more information I can provide.
Edit: I'm now trying to upgrade the application to Vaadin 23 instead and I'm experiencing the same issue. I'm also getting the webpack error referenced in this question. If it helps, I'm running the project locally using embedded Jetty and I'm manually instantiating and invoking the initializers used by Vaadin as described in this answer.
Please note: Although I'm using the Grails Shiro plugin I believe this to be a core Grails problem, having nothing to do with the plugin whatsoever.
Mac (Yosemtie) and Grails 2.4.5 here. Here's the top 2 lines from my Config.groovy:
String configFileAbsPath = System.getProperty('configFileAbsPath')
grails.config.locations = [ "file:${configFileAbsPath}" ]
So to run locally I do something like:
grails -DconfigFileAbsPath=/Users/myuser/tmp/myapp.properties run-app
When I run this exact invocation, my app starts up and behaves just fine.
However, in non-local environments I want my config file to live under /opt/myapp/myapp.properties. So on my local machine I create a /opt/myapp directory, and then ran chmod -R 777 /opt/myapp. I then copy myapp.properties to it and run:
grails -DconfigFileAbsPath=/opt/myapp/myapp.properties run-app
This produces the following stack trace:
| Error Error generating web.xml file (NOTE: Stack trace has been filtered. Use --verbose to see entire trace.)
groovy.lang.MissingPropertyException: No such property: shiro for class: java.lang.String
at ShiroGrailsPlugin$_closure4_closure30_closure34.doCall(ShiroGrailsPlugin.groovy:248)
at ShiroGrailsPlugin$_closure4_closure30_closure34.doCall(ShiroGrailsPlugin.groovy)
at ShiroGrailsPlugin$_closure4_closure30.doCall(ShiroGrailsPlugin.groovy:243)
at ShiroGrailsPlugin$_closure4_closure30.doCall(ShiroGrailsPlugin.groovy)
at ShiroGrailsPlugin$_closure4.doCall(ShiroGrailsPlugin.groovy:242)
I seriously doubt that the location of an external config file, or the file permissions set on the config file (or its parent dir) would break the Grails Shiro plugin. I think this is just a misleading Grails/Groovy exception. Most likely some kind of security exception is causing something to not load/populate correctly (and fail silently), and then when Grails Shiro kicks in during run-app, its missing something that should be there and is causing run-app to die.
Any ideas as to what is going on, or what the fix is?
Update: Running grails clean, grails clean-all and/or grails refresh-dependencies prior to invoking grails run-app do not help/solve this error either.
I generally prefer to have .groovy file for external config rather than .properties because I can write printlns into it to check whether the file is loaded or not.
I use OpenEJB to run unit tests for applications ultimately deployed to WebSphere Application Server. My problem is a(n unavoidable) dependency on the WAS runtime jar. I've added an expression to the exclude property (I've also tried the physical path):
p.put("openejb.deployments.classpath.exclude", ".*?runtime-6.1.*?");
// p.put("openejb.deployments.classpath.exclude", "C:/Users/user/.m3/repository/was/runtime/6.1/runtime-6.1.jar");
p.put("openejb.exclude-include.order", "include-exclude");
This value is confirmed in the logs:
OpenEJB.options-2014-08-07-main--INFO -OpenEJB.options:Using 'openejb.exclude-include.order=include-exclude'
OpenEJB.options-2014-08-07-main--INFO -OpenEJB.options:Using 'openejb.deployments.classpath.include=.*eed-jar.*'
OpenEJB.options-2014-08-07-main--INFO -OpenEJB.options:Using 'openejb.deployments.classpath.exclude=.*?runtime-6.1.*?'
but the jar is still being inspected for loadable modules:
OpenEJB.startup.config-2014-08-07-main--INFO -OpenEJB.startup.config:Found EjbModule in classpath: C:\Users\user\.m3\repository\was\runtime\6.1\runtime-6.1.jar
The result is startup failure for OpenEJB:
org.apache.openejb.OpenEjbContainer$InvalidApplicationException: org.apache.openejb.config.ValidationFailedException: Module failed validation. AppModule(name=)
and
WARNING: can't load com.ibm.ws.management.j2ee.ManagementBean
org.apache.openejb.OpenEJBRuntimeException: Management
I've been creating a 0-length dummy file and renaming runtime-6.1.jar to execute the tests in Eclipse, but I need to restore the jar to run the Maven build (which Eclipse will then bind to, necessitating a restart to rename the jar).
What's the right way to exclude this jar?
You might need to set openejb.deployments.classpath.filter.descriptors to true as well. See http://tomee.apache.org/application-discovery-via-the-classpath.html
Otherwise, filters will not be applied to resources that contain a descriptor file (ejb-jar.xml).
If you still have problems, you can debug through org.apache.openejb.config.DeploymentsResolves and see why your resources aren't excluded.
I'm developing with Grails 2.1.1 and now I want to integrate Logback (http://logback.qos.ch) as the default logging framework as it should provide some better logging features and could be also configured via Groovy.
As Logback 1.0.7 (latest) does only work with slf4j 1.6.6 I want to upgrade the Grails dependeny. Grails 2.1.1 is using slf4j 1.6.2. How to do this properly?
I tried the following: in BuildConfig.groovy I exclude grails-plugin-log4j and slf4j-api
grails.project.dependency.resolution = {
// inherit Grails' default dependencies
inherits("global") {
excludes "grails-plugin-log4j", "slf4j-api"
}
...
}
and I try to load slf4j-api 1.6.6 in compile build and runtime along with the other necessary libraries
grails.project.dependency.resolution = {
...
dependencies {
// specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
compile "org.slf4j:slf4j-api:1.6.6"
build "org.slf4j:slf4j-api:1.6.6",
"ch.qos.logback:logback-core:1.0.7",
"ch.qos.logback:logback-classic:1.0.7"
runtime "org.slf4j:slf4j-api:1.6.6",
"org.slf4j:log4j-over-slf4j:1.6.6", // logback dependency for classic module, as seen on http://logback.qos.ch/dependencies.html
"ch.qos.logback:logback-core:1.0.7",
"ch.qos.logback:logback-classic:1.0.7"
}
...
}
now, if I want to do anything from the Grails commandline, either grails compile or grails clean, it's complaining that it couldn't execute the script because it couldn't find the LoggerFactory class:
| Loading Grails 2.1.1
| Configuring classpath
| Error Error executing script Compile: org/slf4j/LoggerFactory (NOTE: Stack trace has been filtered. Use --verbose to see entire trace.)
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:156)
at org.apache.commons.logging.impl.SLF4JLogFactory.getInstance(SLF4JLogFactory.java:132)
at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:272)
at org.springframework.core.io.support.PathMatchingResourcePatternResolver.<clinit>(PathMatchingResourcePatternResolver.java:169)
| Error Error executing script Compile: org/slf4j/LoggerFactory
How can I upgrade the underlying slf4j-api properly?
If I don't exclude the slf4j-api first, I get a conflict with the "old" 1.6.2 api marked as evicted when calling grails dependency-report...
Also, I'd love to have an external config file for Logback. How would I implement it? With Log4j I just declared a log4jConfigurer bean within the conf/spring/resources.groovy file - how would it be done with Logback?
Has anybody experience in logging Grails 2.1.1 with Logback and could give me any advice for this issue?
As I think that this question would be also a matter of fact for other developers willing to implement the Logback Logging Framework with Grails, I'll share my progress on the topic within this answer - trying not to overload the initial question with progress information.
I still had no luck in updating slf4j within Grails, so I stuck
with the solution to simply overload the slf4j-api dependency.
Grails would show no error on grails dependency-report, just an
"eviction notice" on the older slf4j dependency (1.6.2). This seems
to work but I'll keep on searching for a better solution on this
topic.
I'm now able to load an external config file for Logback through
a ServletContextListener and a ConfigLoader class
implemented in grails-app/src/java, with the
ServletContextListener registered within the web.xml file. (to
get the web.xml simply execute grails install-templates on the
commandline. You'll find it under
grails-app/src/templates/war/web.xml) Be sure to make it the first
<listener/> entry within your web.xml so that Logback gets
configured and loaded as soon as possible.
I found this solution along with the full code sample over at
https://bowerstudios.com/node/896 which was the best and shortest
example I found on this topic which seems to work!
This solution just loaded the external configuration file and used
it within the ConfigLoader class but didn't set it for the whole
Grails application. So I googled around a bit more and I found a
solution from Logback, available at GitHub (https://github.com/qos-ch/logback-extensions) and integrating with the
Spring Framework, so I took these classes from this page and
the LogbackConfigurer class from here and tweaked them to fit
my needs.
Now everything works like a charm and I'am able to create an external configuration file and - what I like best - it's hooking in
with the log object which is injected to e.g. Controller classes
by default.
Hope this is also a help for other developers too ;)
If you've got a better or more "groovy" solution for this, pleas let me know!
Because of memory constraint i am trying to build a grails app with smaller memory footprint. I build the war with this argument "--nojars". I created a war file without all the jar and when i deploy within the glassfish i encounter this error
Exception while loading the app : java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: java.lang.ClassNotFoundException: org.codehaus.groovy.grails.web.util.Log4jConfigListener
It seems like the application fail to find where is the jar file.
I had already indicates the path to the library before deploying the application in glassfish.
did i miss out somethinng?
It is commonly recommended to use GlassFish's Common Classloader. That means putting the shared JARS into the $domain-dir/lib folder (but not into a subfolder of that).
You're probably trying to use the Application Classloader with the asadmin deploy --libraries command. This is more complicated and error-prone. If you don't need different versions of the same JARs with different web applications, you should definitely go for the Common Classloader as specified above.
Also see The Classloader Hierarchy for a reference.
EDIT Updated as per the questioner's comment:
The domain/domain1/lib folder definitely works (I've tested that). To validate that, put log4j.jar into that folder and add a test.jsp to domain1/applications/$applicationName, that just contains:
<% out.println(
org.apache.log4j.Logger.getLogger(this.getClass())); %>
If that works but your other code does not, there may be another point to consider: Are you using Log4J's Logger.getLogger(..) or Apache Commons' LogFactory.getInstance(..) in your code?
See the article Taxonomy of class loader problems encountered when using Jakarta Commons Logging for related issues. - I'd also like to advise you to post your complete stacktrace.