본문으로 건너뛰기

로그 설정과 분석

Nginx의 로그는 장애 원인 파악, 성능 분석, 보안 감사의 핵심 도구입니다. 로그 포맷 커스터마이징, 레벨 설정, 로그 로테이션까지 실무에서 필요한 모든 내용을 다룹니다.


로그 종류​

Nginx는 두 가지 로그를 생성합니다.

로그기본 경로용도
access_log/var/log/nginx/access.logHTTP 요청 기록 (IP, URL, 상태 코드, 응답 시간 등)
error_log/var/log/nginx/error.log에러 및 경고 기록 (설정 오류, 파일 없음, 연결 실패 등)

access_log 포맷 커스터마이징​

기본 로그 포맷(main)은 필요한 정보를 대부분 담고 있지만, 실무에서는 응답 시간, 업스트림 정보 등을 추가한 커스텀 포맷을 사용합니다.

기본 포맷​

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
}

기본 포맷 로그 예시:

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" "-"

응답 시간 포함 커스텀 포맷​

log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time ' # 전체 요청 처리 시간(초)
'uct=$upstream_connect_time ' # 업스트림 연결 시간
'uht=$upstream_header_time ' # 업스트림 헤더 수신 시간
'urt=$upstream_response_time' # 업스트림 전체 응답 시간
;

access_log /var/log/nginx/access.log detailed;

상세 포맷 로그 예시:

203.0.113.10 - - [24/Mar/2026:10:00:01 +0900] "GET /api/users HTTP/1.1" 200 512 "-" "curl/7.88" rt=0.043 uct=0.001 uht=0.040 urt=0.042

JSON 포맷 (로그 수집 시스템 연동용)​

ELK, Loki 같은 로그 수집 시스템에 적합한 JSON 포맷입니다.

log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';

access_log /var/log/nginx/access.json json_combined;

JSON 로그 예시:

{"time_local":"24/Mar/2026:10:00:01 +0900","remote_addr":"203.0.113.10","method":"GET","uri":"/api/users","status":200,"bytes_sent":512,"request_time":0.043,"upstream_response_time":"0.042","http_referer":"","http_user_agent":"curl/7.88","http_x_forwarded_for":"-"}

주요 로그 변수 목록​

$remote_addr          # 클라이언트 IP
$remote_user # HTTP Basic Auth 사용자명
$time_local # 요청 시간 (로컬 시간)
$time_iso8601 # ISO 8601 형식 시간
$request # 전체 요청 줄 (메서드 + URI + 프로토콜)
$request_method # HTTP 메서드 (GET, POST 등)
$request_uri # 요청 URI + 쿼리 스트링
$status # HTTP 응답 상태 코드
$body_bytes_sent # 응답 본문 크기 (바이트)
$bytes_sent # 전체 응답 크기 (헤더 포함)
$request_time # 요청 처리 총 시간 (초, 밀리초 단위 소수점)
$upstream_addr # 업스트림 서버 주소
$upstream_status # 업스트림 응답 상태 코드
$upstream_response_time # 업스트림 응답 시간 (초)
$upstream_connect_time # 업스트림 연결 시간
$upstream_header_time # 업스트림 헤더 수신 시간
$http_referer # Referer 헤더
$http_user_agent # User-Agent 헤더
$http_x_forwarded_for # X-Forwarded-For 헤더
$server_name # 처리한 server_name
$server_port # 서버 포트
$scheme # http 또는 https
$ssl_protocol # TLS 버전 (HTTPS인 경우)
$ssl_cipher # 사용된 암호화 스위트

error_log 레벨 설정​

error_log는 로그 레벨을 지정하여 기록할 메시지의 심각도를 조절합니다.

# 문법: error_log 경로 레벨;
error_log /var/log/nginx/error.log warn;

# 레벨 (낮을수록 더 많은 정보)
# debug → 모든 디버그 정보 (매우 상세, 운영환경 사용 금지)
# info → 정보성 메시지
# notice → 일반 알림
# warn → 경고 (기본 권장)
# error → 에러
# crit → 심각한 에러
# alert → 즉시 조치 필요
# emerg → 시스템 불능 상태

특정 서버별 에러 로그 분리​

server {
server_name example.com;
error_log /var/log/nginx/example.com.error.log warn;
}

server {
server_name api.example.com;
error_log /var/log/nginx/api.error.log error;
}

불필요한 로그 비활성화​

정적 파일(favicon, 이미지 등) 요청은 로그에서 제외하여 로그 용량을 줄입니다.

location = /favicon.ico {
log_not_found off; # 파일 없을 때 에러 로그 기록 안 함
access_log off; # 접근 로그 기록 안 함
}

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
access_log off; # 정적 파일 접근 로그 제외
expires 7d;
}

# 헬스체크 엔드포인트 로그 제외
location /health {
access_log off;
return 200 "OK";
}

로그 로테이션 (logrotate)​

Nginx 로그는 방치하면 파일이 무한히 커집니다. logrotate로 자동 관리합니다.

/etc/logrotate.d/nginx 설정​

/var/log/nginx/*.log {
daily # 매일 로테이션
missingok # 파일 없어도 에러 없음
rotate 14 # 14일치 보관
compress # 이전 로그 gzip 압축
delaycompress # 직전 로그는 압축 유예 (현재 쓰는 파일 보호)
notifempty # 빈 파일은 로테이션 안 함
sharedscripts # postrotate 스크립트 한 번만 실행
postrotate
# Nginx에 USR1 시그널 전송 → 로그 파일 다시 열기
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
# 즉시 로테이션 테스트
sudo logrotate -f /etc/logrotate.d/nginx

# 로테이션 확인
ls -la /var/log/nginx/

실전 로그 분석 명령어​

상태 코드별 집계​

# 상태 코드 분포 확인
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 출력 예시:
# 15432 200
# 234 304
# 87 404
# 12 500

응답 시간이 느린 요청 추출​

# 응답 시간 1초 이상인 요청 (detailed 포맷 사용 시)
grep 'rt=' /var/log/nginx/access.log | awk '{
for(i=1;i<=NF;i++) if($i~/^rt=/) print $i, $0
}' | awk -F= '{if($2+0 > 1) print}' | sort -rn | head -20

특정 IP의 요청 횟수​

# 특정 IP 요청 수
grep "203.0.113.10" /var/log/nginx/access.log | wc -l

# IP별 요청 수 상위 10개
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

404 에러 URL 목록​

awk '($9=="404") {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

정리​

항목설정권장 사항
포맷log_format응답 시간($request_time) 포함 커스텀 포맷 사용
에러 레벨error_log운영환경: warn, 디버깅: debug (임시)
정적 파일access_log offfavicon·이미지 등 불필요한 로그 제외
로테이션logrotatedaily + 14일 보관 + compress 설정
분석awk, grep상태 코드·응답 시간·IP별 집계