Apache 로그 설정과 분석
Apache의 로그는 접근 패턴 파악, 에러 진단, 보안 감사의 핵심 도구입니다. CustomLog 포맷 설정, ErrorLog 레벨, rotatelogs를 이용한 로그 로테이션까지 실무에서 필요한 내용을 다룹니다.
로그 종류
| 로그 | 기본 경로 (Ubuntu) | 역할 |
|---|---|---|
| Access Log | /var/log/apache2/access.log | HTTP 요청 기록 |
| Error Log | /var/log/apache2/error.log | 에러·경고 기록 |
CustomLog — 접근 로그 포맷
Apache는 LogFormat 지시어로 커스텀 포맷을 정의하고, CustomLog로 해당 포맷을 사용합니다.
기본 포맷
# 전역 설정 (apache2.conf 또는 httpd.conf)
# combined 포맷 (가장 많이 사용)
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
# common 포맷
LogFormat "%h %l %u %t \"%r\" %>s %O" common
# VirtualHost에서 사용
CustomLog ${APACHE_LOG_DIR}/access.log combined
combined 포맷 로그 예시:
203.0.113.10 - - [24/Mar/2026:10:00:01 +0900] "GET /api/users HTTP/1.1" 200 512 "https://example.com" "Mozilla/5.0"
응답 시간 포함 커스텀 포맷
# 응답 시간 포함 포맷 (%D: 마이크로초, %T: 초)
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_time
# 가상 호스트에서 사용
CustomLog ${APACHE_LOG_DIR}/access.log combined_time
JSON 포맷 (로그 수집 시스템용)
LogFormat "{ \
\"time\": \"%{%Y-%m-%dT%H:%M:%S%z}t\", \
\"remote_addr\": \"%a\", \
\"method\": \"%m\", \
\"uri\": \"%U%q\", \
\"status\": %s, \
\"bytes\": %O, \
\"referer\": \"%{Referer}i\", \
\"user_agent\": \"%{User-Agent}i\", \
\"response_time_us\": %D \
}" json
CustomLog ${APACHE_LOG_DIR}/access.json json
주요 로그 포맷 변수
| 변수 | 설명 |
|---|---|
%h | 클라이언트 IP (HostnameLookups가 Off이면 IP, On이면 호스트명) |
%a | 클라이언트 IP (항상 IP) |
%l | identd 로그인 이름 (대부분 -) |
%u | 인증된 사용자명 (없으면 -) |
%t | 요청 시간 [day/month/year:hour:minute:second zone] |
%r | 요청 줄 (메서드 + URI + 프로토콜) |
%m | 요청 메서드 |
%U | URL 경로 (쿼리 스트링 제외) |
%q | 쿼리 스트링 |
%s | 상태 코드 (리다이렉트된 경우 최초 코드) |
%>s | 최종 상태 코드 |
%O | 응답 크기 (헤더 포함, 바이트) |
%B | 응답 본문 크기 (0 대신 -) |
%b | 응답 본문 크기 (0이면 -) |
%D | 요청 처리 시간 (마이크로초) |
%T | 요청 처리 시간 (초) |
%{Referer}i | Referer 헤더 |
%{User-Agent}i | User-Agent 헤더 |
%{X-Forwarded-For}i | X-Forwarded-For 헤더 |
%v | 가상 호스트 이름 |
%p | 서버 포트 |
%P | 처리한 자식 프로세스 ID |
ErrorLog — 에러 로그 레벨
# 에러 로그 파일 위치
ErrorLog ${APACHE_LOG_DIR}/error.log
# 로그 레벨 설정
# emerg(0) > alert(1) > crit(2) > error(3) > warn(4) > notice(5) > info(6) > debug(7) > trace1~8
LogLevel warn
| 레벨 | 설명 | 운영 환경 |
|---|---|---|
emerg | 서비스 불능 | 항상 기록 |
alert | 즉시 조치 필요 | 항상 기록 |
crit | 심각한 에러 | 항상 기록 |
error | 에러 (요청 처리 실패) | 항상 기록 |
warn | 경고 | 운영 환경 권장 |
notice | 일반 알림 | 개발 환경 |
info | 정보성 메시지 | 디버깅 |
debug | 상세 디버그 | 임시만 사용 |
모듈별 로그 레벨 설정
특정 모듈만 상세 로그를 남기고 싶을 때:
# 전체는 warn, rewrite 모듈은 trace3으로 상세 기록
LogLevel warn rewrite:trace3
# 프록시 모듈만 debug
LogLevel warn proxy:debug
가상 호스트별 로그 분리
<VirtualHost *:80>
ServerName site1.com
DocumentRoot /var/www/site1
# 사이트별 로그 분리
ErrorLog ${APACHE_LOG_DIR}/site1.error.log
CustomLog ${APACHE_LOG_DIR}/site1.access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName site2.com
DocumentRoot /var/www/site2
ErrorLog ${APACHE_LOG_DIR}/site2.error.log
CustomLog ${APACHE_LOG_DIR}/site2.access.log combined
</VirtualHost>
불필요한 로그 제외
# 특정 URL 로그 제외 (헬스체크, 정적 파일 등)
SetEnvIf Request_URI "^/health$" dontlog
SetEnvIf Request_URI "\.(ico|png|jpg|gif|css|js)$" dontlog
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog
로그 로테이션
rotatelogs 사용 (Apache 내장)
# 날짜별 로그 파일 생성 (예: access.20260324.log)
CustomLog "|/usr/bin/rotatelogs /var/log/apache2/access.%Y%m%d.log 86400" combined
# 크기 기반 로테이션 (100MB마다)
CustomLog "|/usr/bin/rotatelogs /var/log/apache2/access.%Y%m%d_%H%M%S.log 100M" combined
logrotate 사용 (시스템 도구)
# /etc/logrotate.d/apache2
/var/log/apache2/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
sharedscripts
postrotate
if invoke-rc.d apache2 status > /dev/null 2>&1; then
invoke-rc.d apache2 reload > /dev/null 2>&1
fi
endscript
}
# 즉시 로테이션 테스트
sudo logrotate -f /etc/logrotate.d/apache2
실전 로그 분석 명령어
# 상태 코드별 집계
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort -rn
# 가장 많이 요청된 URL
awk '{print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# IP별 요청 수
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -10
# 5xx 에러 요청 목록
awk '($9 ~ /^5/) {print $1, $7, $9}' /var/log/apache2/access.log
# 응답 시간 1초 이상 (combined_time 포맷 기준, 마지막 컬럼이 마이크로초)
awk '{if($NF > 1000000) print $0}' /var/log/apache2/access.log
정리
| 항목 | 지시어 | 권장 설정 |
|---|---|---|
| 접근 로그 | CustomLog | combined 또는 응답 시간 포함 커스텀 포맷 |
| 에러 로그 | ErrorLog + LogLevel | 운영: warn, 디버깅: 모듈별 debug |
| 불필요 로그 제외 | SetEnvIf + env=! | 헬스체크, 정적 파일 제외 |
| 로테이션 | logrotate | daily + 14일 + compress |
| 다중 사이트 | VirtualHost별 ErrorLog/CustomLog | 사이트별 로그 분리 |