Tomcat 로그 관리
Tomcat은 여러 종류의 로그를 생성합니다. 각 로그의 역할을 이해하고 적절히 설정하면 장애 진단과 성능 분석에 큰 도움이 됩니다. 이 챕터에서는 catalina.out, 액세스 로그 Valve, 그리고 Log4j2 통합까지 다룹니다.
Tomcat 로그 종류
| 로그 파일 | 설명 | 기본 위치 |
|---|---|---|
catalina.out | Tomcat 표준 출력/오류 (메인 로그) | $CATALINA_HOME/logs/ |
catalina.YYYY-MM-DD.log | java.util.logging 일별 로그 | $CATALINA_HOME/logs/ |
localhost.YYYY-MM-DD.log | 가상 호스트 로그 | $CATALINA_HOME/logs/ |
localhost_access_log.txt | HTTP 접근 로그 (Access Log Valve) | $CATALINA_HOME/logs/ |
manager.YYYY-MM-DD.log | Manager 앱 로그 | $CATALINA_HOME/logs/ |
catalina.out
catalina.out은 Tomcat 시작/종료, 앱 배포/언배포, 에러 등이 모두 기록되는 핵심 로그입니다.
# 실시간 모니터링
tail -f /opt/tomcat/latest/logs/catalina.out
# 에러만 필터링
grep -E "(ERROR|SEVERE|Exception)" /opt/tomcat/latest/logs/catalina.out
# 최근 100줄 + 실시간
tail -100f /opt/tomcat/latest/logs/catalina.out
# 특정 앱 관련 로그
grep "myapp" /opt/tomcat/latest/logs/catalina.out
주의:
catalina.out은 기본적으로 로테이션되지 않습니다. 장기 운영 시 파일 크기가 무한정 커질 수 있으므로logrotate설정이 필수입니다.
logging.properties — 기본 로그 설정
conf/logging.properties로 Tomcat 내부 로거(java.util.logging)를 설정합니다.
# $CATALINA_HOME/conf/logging.properties
# 핸들러 목록
handlers = 1catalina.org.apache.juli.AsyncFileHandler, \
2localhost.org.apache.juli.AsyncFileHandler, \
3manager.org.apache.juli.AsyncFileHandler, \
java.util.logging.ConsoleHandler
# 기본 로그 레벨
.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler
# catalina 핸들러 설정
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
# localhost 핸들러 설정
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
2localhost.org.apache.juli.AsyncFileHandler.encoding = UTF-8
# 콘솔 핸들러 (catalina.out으로 가는 스트림)
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8
# 특정 패키지 로그 레벨 조정
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler
# 불필요한 로그 억제 예시
org.apache.coyote.http11.Http11Processor.level = WARNING
로그 레벨 (java.util.logging)
| 레벨 | 설명 |
|---|---|
SEVERE | 심각한 에러 |
WARNING | 경고 |
INFO | 일반 정보 (기본) |
CONFIG | 설정 관련 |
FINE | 상세 디버그 |
FINER | 더 상세 |
FINEST | 최대 상세 |
Access Log Valve — HTTP 접근 로그
server.xml의 Host 내에 AccessLogValve를 추가합니다.
기본 설정
<!-- server.xml -->
<Host name="localhost" appBase="webapps" ...>
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="combined"/>
</Host>
커스텀 패턴 — 응답 시간 포함
<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""/>
| 패턴 변수 | 설명 |
|---|---|
%h | 원격 호스트 (IP) |
%l | 원격 논리적 사용자명 |
%u | 인증된 사용자 |
%t | 요청 시각 |
%r | 요청 라인 (메서드 + URI + 프로토콜) |
%s | HTTP 상태 코드 |
%b | 응답 바이트 수 |
%D | 처리 시간 (밀리초) |
%T | 처리 시간 (초) |
%{헤더명}i | 요청 헤더 값 |
JSON 형식 로그
<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 통합 (권장)
Java 애플리케이션에서 더 강력한 로깅이 필요하다면 Log4j2를 사용합니다.
의존성 추가 (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 브릿지 (Tomcat 내부 로그도 Log4j2로) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
<version>2.23.1</version>
</dependency>
log4j2.xml 설정
<!-- src/main/resources/log4j2.xml -->
<?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>
<!-- SQL 로그 (개발용) -->
<Logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
<AppenderRef ref="AppLog"/>
</Logger>
<!-- 불필요한 라이브러리 로그 억제 -->
<Logger name="org.springframework" level="WARN"/>
<Logger name="org.hibernate" level="WARN"/>
</Loggers>
</Configuration>
logrotate 설정 (catalina.out 로테이션)
# /etc/logrotate.d/tomcat
/opt/tomcat/latest/logs/catalina.out {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate # Tomcat 재시작 없이 로테이션 (중요!)
dateext
dateformat -%Y%m%d
postrotate
# 로테이션 후 알림 (선택)
echo "Tomcat log rotated: $(date)" | logger -t tomcat-logrotate
endscript
}
# 액세스 로그도 함께 관리
/opt/tomcat/latest/logs/*.log
/opt/tomcat/latest/logs/*.txt {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
dateext
}
# logrotate 설정 테스트
sudo logrotate -d /etc/logrotate.d/tomcat
# 즉시 강제 실행
sudo logrotate -f /etc/logrotate.d/tomcat
실전 로그 분석
# 느린 요청 분석 (처리 시간 > 1000ms)
awk '{ if ($NF > 1000) print }' /opt/tomcat/latest/logs/localhost_access_log*.txt
# 500 에러 요청 집계
awk '{print $9}' /opt/tomcat/latest/logs/localhost_access_log*.txt | \
grep "^5" | sort | uniq -c | sort -rn
# 특정 URL 평균 응답 시간
awk '/\/api\/users/ {sum+=$NF; cnt++} END {print "Avg:", sum/cnt "ms"}' \
/opt/tomcat/latest/logs/localhost_access_log*.txt
# OutOfMemoryError 발생 확인
grep -c "OutOfMemoryError" /opt/tomcat/latest/logs/catalina.out
Summary
| 로그 | 설정 위치 | 주요 역할 |
|---|---|---|
| catalina.out | Systemd 서비스 or bin/catalina.sh | Tomcat 전반 이벤트 |
| java.util.logging | conf/logging.properties | Tomcat 내부 컴포넌트 |
| Access Log | server.xml AccessLogValve | HTTP 접근 기록 |
| Log4j2 | WEB-INF 또는 classpath | 앱 레벨 구조화 로그 |
| logrotate | /etc/logrotate.d/tomcat | catalina.out 파일 크기 제어 |