Any application produces logs and actually several kind of logs. Container, framework, business code rely on that to communicate basic information to the user (developers, operation teams, etc...). Aggregated or not in a central collector (Elasticsearch/fluentd, Splunk, Syslog...) this will produce by nature a several amount of data you want to control a minimum.

Most of frameworks provide a solution for that need (Log4j2, Logback, ...) but Java Util Logging - JUL or the built-in java logging solution used by Tomcat and TomEE by default -  is pretty raw on that area. Tomcat JULi (think layer on top of JUL) provides a first step allowing to rotate files per day with its FileHandler implementation but it doesn't allow to let the application run without caring of the size used by the logs. In other words whatever the config is you will get a "full disk error" someday.

Without replacing the need of advanced frameworks like Log4j2 for applications relying a lot on logs for audit purpose or other very very verbose needs, TomEE provides a LocalFileHandler JUL handler (appender in log4j semantic) which adds the ability to rotate log files by day but also by size and to archive "old" logs before deleting them after another period.

In practise it means you will allocate a max size to each log file, a duration defining how many time you want to keep these plain text logs and a duration you want to keep them compressed (in gzip or zip format).

Set LocalFileHandler up!

The configuration is pretty straight forward when you are used to Tomcat JULi one:

handlers = 1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler
level = INFO

.handlers = 1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler
.level = INFO

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = WARNING
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler

1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.filenamePattern = ${catalina.base}/logs/catalina.%s.%03d.log
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.encoding = UTF-8
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.dateCheckInterval = 1 second
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.limit = 50 Mega
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.archiveOlderThan = 30 days
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.purgeOlderThan = 30 days
1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler.level = INFO

The first two lines defien the handlers and default level to set up, the next two (which are the same but with the keys prefixed by a dot) defines the same but only for the root logger.

Then the next two lines ([Catalina].[localhost]) defines the handlers associated to the Host in server.xml, you can also define it by webapp and logging will respect Tomcat hierarchy (the one you can see in server.xml).

Until now we only defined level/handlers pairs for several elements of the server (loggers, Host, Context...). Once it is done you need to configure each handler to redirect the log records to the actual handler properly.

Before digging how to configure LocalFileHandler, keep in mind Tomcat uses a hack to support multiple configurations for the same handler. By default a handler uses its classname as key prefix which means you can't define twice the same handler which is quite limiting. To workaround that tomcat allows you to prefix the classname by a string starting with a number (1main in previous snippet) and will use this prefix concatenated to the classname as handler identifier instead of the classname only. In clear org.apache.tomee.jul.handler.rotating.LocalFileHandler becomes 1main.org.apache.tomee.jul.handler.rotating.LocalFileHandler in term of configuration. To allows to define the same handler multiple times with different configuration.

Now we understand how the configuration can be read let see what is actually done:

  • filenamePattern defines where the log files are created, it can use system properties (tomcat one, you can set them up in setenv.[sh|bat]) or reuse Tomcat provided ones like catalina.base. It is the absolute file path and can use two patterns: the date (yyyy-mm-dd format) and the rotation index (starting at 0 for the first file of the day). It reuses java String.format syntax, that's why date uses %s and index %d (%03d enforces to use 3 digits).
  • limit is the size limit per file
  • archiveOlderThan is the duration limit before archiving a log file
  • purgeOlderThan is the duration limit before deleting an archive (note it starts when archived and not when created)

In term of more common configuration you can set the encoding, a filter, a formatter.

If you need a more advanced tuning you can configure too these properties:

  • dateCheckInterval: how often the date string is updated, default is 5 seconds. This is the period you accept your log file to be outdated (ie you can log in day N 5 seconds of log data of the day N+1)
  • bufferSize: buffer used before writing to the actual file (depending the OS allows to speed up the logging), default is -1 which means it is not used
  • archiveDirectory: where to store compressed logs (default to ${catalina.base}/logs/archives)
  • archiveFormat: gzip or zip depending the archives you want, default is gzip
  • compressionLevel: for zip only the zip compression level (default -1)
  • formatterLocale and formatterLocale: only used when no formatter is set, it relies on a String.format which allows you to configure the log format directly on the handler, default is:
%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n

which output something like;

august 13, 2016 6:38:33 PM null
INFOS: message

Limitations

This handler works very well in most of the cases but note two important things to consider before using it for something else than container/framework logs:

  • Rotation is done only when a log record arrives. In other terms you will not rotate if you don't log anything for days. Not a big deal but knowing it avoids to not understand why you have day N and day N+7 log files only if you restart the server once a week.
  • Nothing controls the full disk space allocated to logs in this handler. This is one potential enhancement: adding a maxLogFilePerDay forcing some deletion or archiving but this has some more strategy implication so it is not yet there. It means you can still get a "full disk error" with this handler even if it is more unlikely than with default implementations cause archiving once well configured will already save a lot of space.

 

From the same author:

In the same category: