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 File | Description | Default Location |
|---|---|---|
catalina.out | Tomcat stdout/stderr (main log) | $CATALINA_HOME/logs/ |
catalina.YYYY-MM-DD.log | java.util.logging daily log | $CATALINA_HOME/logs/ |
localhost.YYYY-MM-DD.log | Virtual host log | $CATALINA_HOME/logs/ |
localhost_access_log.txt | HTTP access log (Access Log Valve) | $CATALINA_HOME/logs/ |
manager.YYYY-MM-DD.log | Manager 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.outis not rotated by default. During long-term operation the file grows unbounded —logrotateconfiguration 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)
| Level | Description |
|---|---|
SEVERE | Critical errors |
WARNING | Warnings |
INFO | General information (default) |
FINE | Detailed debug |
FINER | More detailed |
FINEST | Maximum 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 "%r" %s %b %D "%{Referer}i" "%{User-Agent}i""/>
| Pattern Variable | Description |
|---|---|
%h | Remote host (IP) |
%l | Remote logical username |
%u | Authenticated user |
%t | Request time |
%r | Request line (method + URI + protocol) |
%s | HTTP status code |
%b | Response bytes |
%D | Processing time (milliseconds) |
%T | Processing time (seconds) |
%{Header}i | Request header value |
JSON Format
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="access_log"
suffix=".json"
pattern="{"time":"%t","method":"%m","uri":"%U%q","status":%s,"bytes":%b,"duration_ms":%D,"ip":"%h"}"/>
Log4j2 Integration (Recommended)
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
| Log | Configuration Location | Key Role |
|---|---|---|
| catalina.out | Systemd service or bin/catalina.sh | Tomcat-wide events |
| java.util.logging | conf/logging.properties | Internal Tomcat components |
| Access Log | server.xml AccessLogValve | HTTP access records |
| Log4j2 | WEB-INF or classpath | Structured app-level logging |
| logrotate | /etc/logrotate.d/tomcat | catalina.out file size control |