Improve your Logging in your Java EE Application with tinylog 1.1
tinylog is a lightweight logging framework for Java. In opposite to Apache Log4j and Logback, tinylog consists of a single JAR file of only 80KB without any dependencies and has a static logger class. This means that you haven’t to use any boilerplate code for creating a logger instance for each class.
public static void main(String[] args) { Logger.info("Hello World!"); // Logging methods are static }
Logging Context
One of the new features of tinylog 1.1 is the support of logging context. This feature is known as mapped logging context (MDC) from other logging frameworks and allows thread-based enrichment of log entries with additional data. For example, you can set the user IP once for a thread and tinylog will include this IP in all log entries.
public class HelloWorld extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { LoggingContext.put("ip", request.getRemoteAddr()); // Set IP for this and all client threads Logger.info("Handle request"); response.getWriter().append("<h1>Hello World</h1><p>"); Logger.info("Done request"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response); } }
If you use a load balancer or a proxy, you will get the real user IP by calling request.getHeader("X-FORWARDED-FOR").
The logging context has to be included in the logging format pattern. tinylog can be configured via a properties file, system properties or the Fluent API. You can use your preferred way to set the logging format pattern for example to “{date} [{context: ip}] {level}: {message}” for getting the following output:
2016-05-15 11:40:31 [89.12.191.39] INFO: Handle request 2016-05-15 11:40:31 [89.12.191.39] INFO: Done request
Such logging context is helpful for reconstructing: which log entry belongs to which request. If you have a log-in system, the user name is another good candidate for logging context. It is possible as well to set multiple values. If you use a thread pool, you shouldn’t forget to clear the logging context while returning a thread to the thread pool.
Writers
tinylog supports multiple writers for outputting log entries. For Java EE applications, the RollingFileWriter and the JdbcWriter are the most popular writers.
The RollingFileWriter can write log entries to a file. In opposite to the basic FileWriter, a new log file can be started after defined events. Such events can be the start of the application, a maximum file size or a date. A defined number of old log files can be kept as backups.
Example for configuring a RollingFileWriter via a properties file:
tinylog.writer = rollingfile tinylog.writer.filename = log.txt tinylog.writer.backups = 10 tinylog.writer.label = count tinylog.writer.policies = startup, daily
In this example, the RollingFileWriter starts a new log file at the application start and after each day runtime. The ten newest log files will be kept as backups and be numbered consecutively.
The JdbcWriter can write log entries to a SQL database. This is a way to centralize logs if you have multiple servers.
Example for configuring a JdbcWriter via a properties file:
tinylog.writer = jdbc tinylog.writer.url = jdbc:mysql://localhost/demo tinylog.writer.table = logs tinylog.writer.columns = date, ip, level, message tinylog.writer.values = DATE, CONTEXT, LEVEL, MESSAGE tinylog.writer.username = demo tinylog.writer.password = demo
In modern standard Java environments and the most web application servers, the JVM can find a database driver via services. Unfortunately, a standalone Apache Tomcat requires a manual loading of the database driver. This can be done by a ServletContextListener.
@WebListener public class LifeCycleListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) { try { new com.mysql.jdbc.Driver(); // Your database driver } catch (SQLException e) { Logger.error(e); } } @Override public void contextDestroyed(ServletContextEvent event) {} }
tinylog 1.2 will support Java EE data sources. You have to define a database connection just once for your application and you can refer to the data source in your tinylog configuration. Another new feature is the reestablishing of a database connection in case of connection losses as soon as the database is up again.
Writing Thread
Out of the box, tinylog writes all log entries synchronously. tinylog’s writing thread can be used to execute writers in a separate thread to avoid blocking by slow IO operations. If activated, the writing thread has to be shutdown together with your application. This can be done my observing a main thread or calling the shutdown method. As there are no reliable long-living threads in Java EE applications, the proposed way is to use a ServletContextListener to shutdown the writing thread.
@WebListener public class LifeCycleListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) {} @Override public void contextDestroyed(ServletContextEvent event) { Configurator.shutdownWritingThread(false); } }
Source Code
The whole source code of the Java EE example application is available on GitHub. Further information about tinylog can be found on the tinylog website, including a complete manual.