Some Days ago, I had to refactor a web application to make it fit for production. The existing code was more like a proof-of-concept, not really an application. Especially the logging part was a mess.
In this post I will explain the improvements and also simplifications I made for the logging.
The requirements were quite simple: Log was happen and send an email if an exception occurs.
After study the existing Sourcecode I found in the web-application:
- Used Log4J component
- Own programmed Loghandler
- JavaMail component
My simplification: kick them all out!
All requirements can be accomplished with functions from JDK and the usage of tomcat.
java.util.logging.Logger
Maybe early the java standard logger was limited and log4j was/is great, but since I’m coding in java, I could fulfill all logging requirements with the java standard logger API.
So I added to all class with a logging method a new logger field as follow:
private static transient Logger logger = Logger.getLogger(MyClass.class.getName());
transient because some classes are ‘’Serializable’’ and the logger don’t have to be persistent.
Next part, was to rewrite the logging statements
logger.log(Level.SEVERE, ex.toString()); // or logger.log(Level.FINE, "MyMessage ({0}): {1}", new Object[]{id, myObject.getText()});
Until now, it was straightforward.
logging.properties – the configuration file
This can be a bit wired. Because of the major limitation of the jdk implementation (configure the logging per web-application), tomcat us JULI. With the JULI implementation, you can configure logging at a variety of levels
- Globally for the JVM (
$JAVA_HOME/jre/lib/logging.properties) - Per Tomcat Runtime instance (
$TOMCAT_HOME/conf/logging.properties) - Per Web Application (
WEB-INF/classes/ logging.properties)
At each level you can use a logging.properties file to configure logging granularity.
The default tomcat logging.properties file ($TOMCAT_HOME/conf/) specifies two types of handlers: ConsoleHandler for routing logging to stdout and a FileHandler for writing to a file.
When you configure the logging.properties file for tomcat instance or web application, you use a similar configuration as that of the JDK logging.properties file. You can also specify additional extensions to allow better flexibility in assigning loggers. Some Hints:
- Declare a list of handlers using
handlers - Set handlers for the root logger using the
.handlersproperty - define a list of handlers using the
loggerName.handlersproperty - Use the
handlerName.levelproperty to specify the level of logging you want for a given handler - System property replacement for property values expressed using the format
${systemPropertyName}
See the example config file on the end of this blog entry. This should clarify this above.
Email send
Next step what I had to re-implement (or configure) was sending emails, when an exception occur. Fortunately it exists a smtphandler for the java.util.logging library (smtphandler). It uses the JavaMail API to communicate with the SMTP server. Because of that, I had to copy the library smtphandler-0.7.jar, mailapi.jar, smtp.jar, activation.jar in the endorsed folder of tomcat (see java.endorsed.dirs property). You find them on the smtphandler page or with your favorite search engine.
After that, I had to set the smtphandler as a new log handler and configure it. That’s all.
See the example config file on the end of this blog entry. This should clarify this above.
Console output
The tomcat server will run as a service on a solaris system. No one will sit in front of a console and follow the stdout (or read the catalina.out file). For a better performance the console handler can be removed. From
handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
To
.handlers = 1catalina.org.apache.juli.FileHandler
That stops anything getting written to catalina.out – file.
Log Level
You can set a various log level in the logging.properties files (ALL, SEVERE,… ,FINEST, OFF). Enabling logging at a given level also enables logging at all higher levels. In general, the lower level of logging you enable, the more data is written to the log files, so be careful when setting the logging level very low.
To configure the applications log level, set the level property of the specific handler (f.e. 10aailogin.org.apache.juli.FileHandler.level = FINEST. It is possible to configure the log level for each package or class. ch.enterpriselab.login.level = INFO. The log level of the handler has to be lower than that of the package or class.
Example Configuration Files
For better readability on blog page, I omit all standard comment line
$TOMCAT_HOME/conf/logging.properties
The most part was untouched from the default file.
# Created tdmarti, HSLU T&A - D3S (2011) handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler .handlers = 1catalina.org.apache.juli.FileHandler 1catalina.org.apache.juli.FileHandler.level = SEVERE 1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 1catalina.org.apache.juli.FileHandler.prefix = catalina. 2localhost.org.apache.juli.FileHandler.level = SEVERE 2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 2localhost.org.apache.juli.FileHandler.prefix = localhost. 3manager.org.apache.juli.FileHandler.level = FINE 3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 3manager.org.apache.juli.FileHandler.prefix = manager. 4host-manager.org.apache.juli.FileHandler.level = FINE 4host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 4host-manager.org.apache.juli.FileHandler.prefix = host-manager. org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.FileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.FileHandler
WEB-INF/classes/ logging.properties
# Created tdmarti, HSLU T&A - D3S (2011) handlers = 10webappname.org.apache.juli.FileHandler, smtphandler.SMTPHandler .handlers = 10webappname.org.apache.juli.FileHandler, smtphandler.SMTPHandler ## Define my webapplication logger #Set the log level 10aailogin.org.apache.juli.FileHandler.level = FINEST 10aailogin.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 10aailogin.org.apache.juli.FileHandler.prefix = webappname. 10aailogin.org.apache.juli.FileHandler.limit = 5242880 10aailogin.org.apache.juli.FileHandler.count = 5 org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/secure].level = FINEST org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/secure].handlers = 10webappname.org.apache.juli.FileHandler #Set the log level per package ch.enterpriselab.login.level = INFO smtphandler.SMTPHandler.level=SEVERE smtphandler.SMTPHandler.smtpHost=mta.mymta.com smtphandler.SMTPHandler.to=admin@mymta.com smtphandler.SMTPHandler.from=webapp@mymta.com smtphandler.SMTPHandler.subject=[SMTPHandler] Application message smtphandler.SMTPHandler.bufferSize=512 smtphandler.SMTPHandler.formatter=java.util.logging.SimpleFormatter