WEB 요청에 대한 로그이다. 어플리케이션의 주요 내용과는 관련 없이 프로토콜 정보, client 정보, upstream 서버 정보(리버스프록시인 경우는 Nginx가 연결해주는 대상 서버), 응답시간, 콘텐츠 사이즈 등의 정보를 나열하여 간략하게 볼 수 있다.
Access Log는 웹 요청에 대한 정보를 기록하는 로그 파일이다. 어플리케이션 내부 로직과는 무관하게, 클라이언트 정보, 요청 정보, 응답 상태, 응답 시간, 콘텐츠 크기 등 주요한 HTTP 수준의 메타 정보를 확인할 수 있다.
Access Log는 형식만 통일되어 있다면, 어플리케이션의 구현 방식과 상관없이 전체 서비스의 상태를 빠르게 파악할 수 있는 유용한 수단이다. 예를 들어, SRE(Site Reliability Engineering) 조직에서는 Access Log의 표준화를 통해 자동화된 모니터링과 진단 체계를 구축한다.
SRE : Site Reliabliity Engineering
서비스가 이용가능한 상태, 신뢰성 높은 상태로 유지되는지 전문적으로 하자.
실제로 각 서버가 NGINX를 사용하는 경우, 일반적으로 bar log라는 디렉토리에 Access Log가 저장되도록 구성하며, 중앙 수집 시스템은 해당 로그들을 자동으로 수집하여 상태 코드(status code) 기반의 분석을 수행할 수 있다.
예를 들어, 응답 코드가 200번대이면 정상, 400번대는 잘못된 요청, 500번대는 서버 측 에러를 의미한다. 이러한 코드를 기반으로 로그의 개수나 시간에 따른 추이를 분석함으로써 문제가 발생한 시점을 빠르게 파악할 수 있다. 이 과정은 실제 어플리케이션의 코드를 몰라도 수행할 수 있다.
즉, 어플리케이션 개발 담당자가 실수로 에러 알림 설정을 하지 않았더라도, Access Log만 수집되고 있다면 500번대 에러가 지속적으로 발생 중인 사실을 다른 운영팀이 조기에 인지할 수 있다.
이러한 분석이 가능한 전제는 대부분의 웹 서비스가 어플리케이션 서버 앞단에 웹 서버(NGINX 등)를 위치시킨다는 점이다. 따라서 어플리케이션 로그보다 먼저, 웹 서버의 Access Log를 수집 및 분석하는 것이 서비스 상태를 진단하는 첫 번째 단계라고 할 수 있다.
요약하자면, 모든 서비스에서 가장 먼저 모니터링해야 할 대상은 Access Log이다.
Access Log는 NGINX 설정 파일에서 log_format
지시어를 사용하여 포맷을 정의하고, access_log
지시어에서 해당 포맷을 사용할 수 있다.
http {
log_format upstream_time '$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"';
server {
access_log /spool/logs/nginx-access.log upstream_time;
...
}
}
로그포멧에서 세미콜론을 찍기 전까지 작은 따옴표로 묶여서 엔터 쳐진 것들은 이어져있는 것으로 인식된다.
json 포멧으로 하고 싶다면 json 포멧으로 적기만 하면 된다.
위 설정에서 정의된 항목들은 다음과 같은 의미를 가진다:
변수명 | 설명 |
---|---|
$remote_addr | 요청한 클라이언트 IP 주소. 문제가 발생한 위치를 유추할 수 있다. |
$remote_user | 인증된 사용자 이름. 일반적으로 HTTP 인증이 사용될 때 기록된다. |
$time_local | 요청이 도착한 시간. 필수적으로 기록하는 것이 좋다. |
$request | 요청의 전체 라인 (예: GET /index.html HTTP/1.1 ) |
$status | 응답 상태 코드. HTTP 200, 404, 500 등의 코드가 기록된다. |
$body_bytes_sent | 클라이언트로 전송된 바디의 크기. 전송된 데이터의 규모를 파악할 수 있다. |
$http_user_agent | 클라이언트 애플리케이션 정보 (브라우저, 크롤러 등) |
$upstream_response_time | 업스트림 서버(NGINX가 요청을 전달한 실제 서버)의 응답 시간 |
$upstream_connect_time , $upstream_header_time 등 | 업스트림 서버와 연결된 시간, 응답 헤더를 수신하는 데 걸린 시간 등 |
로그 포맷은 사용자가 원하는 형태로 자유롭게 커스터마이징할 수 있다. 예를 들어, 로그를 분석하기 쉽게 하기 위해 키=값 형식으로 기록하거나, JSON 포맷을 수동으로 구성하여 다음과 같이 사용할 수도 있다:
log_format json_format '{'
'"remote_addr":"$remote_addr",'
'"time_local":"$time_local",'
'"request":"$request",'
'"status": "$status"'
'}';
자동으로 JSON 포맷으로 변환해주는 기능은 없으며, 원하는 형식을 직접 정의해야 한다.
Access Log에 너무 많은 정보를 남기면 오히려 부하를 유발할 수 있다. 예를 들어 대용량 파일의 본문(body)을 기록하거나, 응답 콘텐츠 전체를 저장하게 되면, NGINX 성능 저하 및 로그 수집 시스템 과부하가 발생할 수 있다. 따라서 꼭 필요한 정보만 남기는 것이 좋다.
기본적으로 NGINX는 다음 경로에 로그를 저장한다:
/var/log/nginx/access.log
/var/log/nginx/error.log
Access Log와 Error Log는 반드시 분리해서 저장하는 것이 좋다.
Access Log는 포맷이 정형화되어 있어 분석에 적합하지만, Error Log는 자유 형식이며 복잡한 분석이 어려워 수집 시스템에 큰 부담을 줄 수 있다.
로그 위치와 포맷은 다음과 같이 서버 블록에서 지정할 수 있다:
server {
access_log $yourpath $yourlogformat;
error_log $yourpath $yourlogformat;
}
로그가 무한정 쌓이면 디스크 공간을 초과하게 되므로 logrotate
를 설정하여 주기적으로 로그 파일을 분리, 압축, 삭제하도록 한다
설정 파일 예시: /etc/logrotate.d/nginx
sudo vi /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 640 www-data www-data
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
주요 설정 항목 설명
옵션 | 설명 |
---|---|
daily | 매일 로그를 분리 |
rotate 30 | 최대 30개의 이전 로그 파일을 보관 |
compress | 이전 로그 파일을 gzip 등으로 압축 |
delaycompress | rotate 직후에는 압축하지 않고 그 다음에 압축 |
missingok | 로그 파일이 없어도 에러를 내지 않음 |
notifempty | 로그 내용이 비어 있다면 rotate하지 않음 |
create 640 www-data www-data | 새 로그 파일을 해당 권한과 소유자로 생성 |
postrotate | rotate 이후에 실행되는 명령. kill -USR1 은 NGINX에 새로운 로그 파일을 사용하라는 신호를 보냄 |
postrotate
설정이 없으면 NGINX는 이전 로그 파일에 계속 쓰게 되므로 로그 로테이션이 무의미해진다.
logrotate -d -f /etc/logrotate.d/nginx
또한 /etc/cron.daily/logrotate
가 존재하므로 별도의 크론 작업 없이도 logrotate 설정은 매일 자동으로 실행된다.
필요 시 log_format
, access_log
경로 설정, logrotate
테스트까지 포함한 스크립트를 자동화하여 운영환경에서도 쉽게 적용할 수 있다.