Can I expect that a log entry for "Hi!" will be emitted to ${catalina.base}/logs/webapp.log
?
Tomcat Config
{$CATALINA_BASE}/conf/logger.properties
# DECLARE HANDLERS
handlers = 3webapp.org.apache.juli.FileHandler
# CONFIGURE HANDLERS
3webapp.org.apache.juli.FileHandler.level = ALL
3webapp.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
3webapp.org.apache.juli.FileHandler.prefix = webapp
3webapp.org.apache.juli.FileHandler.rotatable = false
# Webapp-local 'ROOT' logger?? The Apache-provided logger.properties
# calls these 'Facility specific'
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/webapp].level = ALL
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/webapp].handlers = 3webapp.org.apache.juli.FileHandler
Webapp-level Logging Config
WEB-INF/classes/logger.properties
of WAR deployed to /webapp
au.com.mydomain.level = ALL
Usage from Code
Code in WAR deployed to /webapp
package au.com.mydomain.foo;
import java.util.logging.Logger;
import java.util.logging.Level;
public class Bar {
...
Logger.getLogger(Bar.class.getName()).log(Level.INFO, "Hi!");
...
}
The answer is "NO"
As confirmed in a debugger, if a web-application does:
import java.util.logging.Logger;
...
Logger foo = Logger.getLogger("foo");
Then foo
's immediate parent is the org.apache.juli.ClassLoaderLogManager$RootLogger
which is the JVM-wide logger which is gets set up by Catalina, and is the ultimate ancestor of all other loggers defined in {$CATALINA_BASE}/conf/logger.properties
.
But all is not lost
It's possible to gain a reference to the 'per-webapp' logger:
String prefix = "org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/webapp]";
Logger hmm = Logger.getLogger(prefix);
which means we can get a logger with the per-webapp logger as a parent:
Logger.getLogger(prefix + "." + MyClass.class.getName())
A horrible, horrible hack
But of course, all the existing code does Logger.getLogger() without specifying a prefix.
If you do the following (in somewhere like ServletContextListener#contextInitialized(ServletContextEvent)
, you can:
// eg "org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/webapp]"
String tomcatHack = sce.getServletContext().getInitParameter("tomcatHack");
if (tomcatHack != null) {
Logger ctxLog = Logger.getLogger("webappLogger");
Logger appLog = Logger.getLogger("au.com.mydomain");
appLog.setParent(ctxLog);
}
And now subsequent logs to:
Logger.getLogger("au.com.mydomain.foo").log(Level.INFO, "Ha!");
... will turn up in log.log
.
As a nasty side-effect, if you've got two web-apps that pull of this trick running in the same container (eg, /prod and /test) then they will overwrite the parent.
As a potentially nasty side-effect, our logger root is now a 'child' at two points in the JVM-wide hierarchy of loggers, and that may upset someone, somewhere, sometime.