Skip to main content
Advertisement

Tomcat Log Management

Tomcat generates several types of logs. Understanding each log's role and configuring it properly is essential for incident diagnosis and performance analysis. This chapter covers catalina.out, the Access Log Valve, and Log4j2 integration.


Tomcat Log Types

Log FileDescriptionDefault Location
catalina.outTomcat stdout/stderr (main log)$CATALINA_HOME/logs/
catalina.YYYY-MM-DD.logjava.util.logging daily log$CATALINA_HOME/logs/
localhost.YYYY-MM-DD.logVirtual host log$CATALINA_HOME/logs/
localhost_access_log.txtHTTP access log (Access Log Valve)$CATALINA_HOME/logs/
manager.YYYY-MM-DD.logManager app log$CATALINA_HOME/logs/

catalina.out

catalina.out is the core log capturing Tomcat start/stop, app deploy/undeploy, errors, and more.

# Real-time monitoring
tail -f /opt/tomcat/latest/logs/catalina.out

# Filter errors only
grep -E "(ERROR|SEVERE|Exception)" /opt/tomcat/latest/logs/catalina.out

# Last 100 lines + real-time
tail -100f /opt/tomcat/latest/logs/catalina.out

Warning: catalina.out is not rotated by default. During long-term operation the file grows unbounded — logrotate configuration is essential.


logging.properties — Base Log Configuration

Configure Tomcat's internal logger (java.util.logging) via conf/logging.properties.

# $CATALINA_HOME/conf/logging.properties

handlers = 1catalina.org.apache.juli.AsyncFileHandler, \
2localhost.org.apache.juli.AsyncFileHandler, \
java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.AsyncFileHandler.level = FINE
1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.
1catalina.org.apache.juli.AsyncFileHandler.suffix = .log
1catalina.org.apache.juli.AsyncFileHandler.encoding = UTF-8

2localhost.org.apache.juli.AsyncFileHandler.level = FINE
2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.
2localhost.org.apache.juli.AsyncFileHandler.suffix = .log

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8

# Suppress unnecessary logs
org.apache.coyote.http11.Http11Processor.level = WARNING

Log Levels (java.util.logging)

LevelDescription
SEVERECritical errors
WARNINGWarnings
INFOGeneral information (default)
FINEDetailed debug
FINERMore detailed
FINESTMaximum detail

Access Log Valve — HTTP Access Log

Add AccessLogValve inside the Host element in server.xml.

Basic Configuration

<Host name="localhost" appBase="webapps" ...>
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="combined"/>
</Host>

Custom Pattern with Response Time

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="access_log"
suffix=".txt"
rotatable="true"
fileDateFormat="yyyy-MM-dd"
pattern="%h %l %u %t &quot;%r&quot; %s %b %D &quot;%{Referer}i&quot; &quot;%{User-Agent}i&quot;"/>
Pattern VariableDescription
%hRemote host (IP)
%lRemote logical username
%uAuthenticated user
%tRequest time
%rRequest line (method + URI + protocol)
%sHTTP status code
%bResponse bytes
%DProcessing time (milliseconds)
%TProcessing time (seconds)
%{Header}iRequest header value

JSON Format

<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="access_log"
suffix=".json"
pattern="{&quot;time&quot;:&quot;%t&quot;,&quot;method&quot;:&quot;%m&quot;,&quot;uri&quot;:&quot;%U%q&quot;,&quot;status&quot;:%s,&quot;bytes&quot;:%b,&quot;duration_ms&quot;:%D,&quot;ip&quot;:&quot;%h&quot;}"/>

For more powerful logging in Java applications, use Log4j2.

Dependencies (Maven)

<!-- pom.xml -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.23.1</version>
</dependency>
<!-- JUL → Log4j2 bridge -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
<version>2.23.1</version>
</dependency>

log4j2.xml Configuration

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="60">

<Properties>
<Property name="LOG_DIR">/opt/tomcat/latest/logs</Property>
<Property name="APP_NAME">myapp</Property>
<Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>

<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>

<RollingFile name="AppLog"
fileName="${LOG_DIR}/${APP_NAME}.log"
filePattern="${LOG_DIR}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>

<RollingFile name="ErrorLog"
fileName="${LOG_DIR}/${APP_NAME}-error.log"
filePattern="${LOG_DIR}/${APP_NAME}-error.%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="30"/>
</RollingFile>
</Appenders>

<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="AppLog"/>
<AppenderRef ref="ErrorLog"/>
</Root>

<Logger name="com.example.myapp" level="DEBUG" additivity="false">
<AppenderRef ref="AppLog"/>
</Logger>

<!-- Suppress noisy library logs -->
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.hibernate" level="WARN"/>
</Loggers>

</Configuration>

logrotate Configuration (catalina.out Rotation)

# /etc/logrotate.d/tomcat
/opt/tomcat/latest/logs/catalina.out {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate # Rotate without restarting Tomcat (important!)
dateext
dateformat -%Y%m%d
}

/opt/tomcat/latest/logs/*.log
/opt/tomcat/latest/logs/*.txt {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
dateext
}
# Test logrotate configuration
sudo logrotate -d /etc/logrotate.d/tomcat

# Force immediate rotation
sudo logrotate -f /etc/logrotate.d/tomcat

Practical Log Analysis

# Analyze slow requests (processing time > 1000ms)
awk '{ if ($NF > 1000) print }' /opt/tomcat/latest/logs/localhost_access_log*.txt

# Count 500 error requests
awk '{print $9}' /opt/tomcat/latest/logs/localhost_access_log*.txt | \
grep "^5" | sort | uniq -c | sort -rn

# Average response time for a specific URL
awk '/\/api\/users/ {sum+=$NF; cnt++} END {print "Avg:", sum/cnt "ms"}' \
/opt/tomcat/latest/logs/localhost_access_log*.txt

# Check OutOfMemoryError occurrences
grep -c "OutOfMemoryError" /opt/tomcat/latest/logs/catalina.out

Summary

LogConfiguration LocationKey Role
catalina.outSystemd service or bin/catalina.shTomcat-wide events
java.util.loggingconf/logging.propertiesInternal Tomcat components
Access Logserver.xml AccessLogValveHTTP access records
Log4j2WEB-INF or classpathStructured app-level logging
logrotate/etc/logrotate.d/tomcatcatalina.out file size control
Advertisement