In Tomcat JULI, do the 'facility specific'

2019-07-21 10:25发布

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!");
  ...
}

1条回答
爷的心禁止访问
2楼-- · 2019-07-21 11:19

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.

查看更多
登录 后发表回答