HAProxy 는 로그 파일을 직접 쓰지 않는다. syslog 로 보내고 rsyslog 가 파일에 떨어뜨린다. 그래서 설정이 두 군데에 있다.
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log /dev/log local1 notice
defaults
log global
mode http
option httplog
option dontlognull
# /etc/rsyslog.d/49-haproxy.conf
local0.* /var/log/haproxy.log
& stop
systemctl restart rsyslog
systemctl reload haproxy
세 가지 중 하나라도 빠지면 로그가 남지 않는다. global 의 log 지시자, defaults 의 log global, option httplog 또는 option tcplog.
# 설정에 로그 지시자가 있는지
grep -nE '^\s*(log|option (httplog|tcplog))' /etc/haproxy/haproxy.cfg
# rsyslog 규칙이 있는지
grep -r haproxy /etc/rsyslog.conf /etc/rsyslog.d/
# 소켓 존재 여부
ls -l /dev/log
# 대안 경로
journalctl -u haproxy -f
log 127.0.0.1 local2 로 보내는 구성이면 rsyslog 가 UDP 514 를 듣고 있어야 한다($ModLoad imudp). /dev/log 소켓으로 보내면 네트워크 수신이 필요 없어 더 단순하다./dev/log 가 없는 경우가 많다. 표준 출력으로 내보낸다.global
log stdout format raw local0
journalctl 쪽을 본다.facility 가 겹치면 한 파일에 섞인다. 예를 들어 sftp 쪽에서 local2 를 쓰고 있는데 HAProxy 도 local2 라면 /var/log/sftp.log 에 함께 쌓인다.
해결은 facility 를 나누는 것이다. local0 부터 local7 중 비어 있는 값을 HAProxy 에 주고 rsyslog 규칙도 함께 바꾼다.
# haproxy.cfg
global
log /dev/log local3
# /etc/rsyslog.d/49-haproxy.conf
local3.* /var/log/haproxy.log
& stop
& stop 이 있어야 그 메시지가 아래 규칙(/var/log/messages 등)으로 흘러가지 않는다.
SSH · SFTP 는 facility 를 임의로 바꿀 수 없다. sshd 가 쓰는 값이 정해져 있으므로, 분리하려면 rsyslog 쪽에서 조건으로 거른다.
if ($programname == 'sshd') and ($msg contains 'sftp') then {
action(type="omfile" file="/var/log/sftp.log")
stop
}
로그 회전은 /etc/logrotate.d/haproxy 에 정의한다. 회전 후 rsyslog 에 HUP 을 보내지 않으면 새 파일에 기록되지 않는다.
192.168.0.10:52344 [06/May/2026:12:00:01.123] fe_http be_app/srv1 0/0/1/5/6 200 512 - - ---- 1/1/0/0/0 0/0 "GET /index.html HTTP/1.1"
| 위치 | 뜻 |
|---|---|
192.168.0.10:52344 |
클라이언트 IP:포트 |
[...] |
요청 수신 시각 |
fe_http |
프론트엔드 이름 |
be_app/srv1 |
백엔드/서버 이름. 서버가 <NOSRV> 면 선택조차 못한 것 |
0/0/1/5/6 |
타이밍(ms). TR/Tw/Tc/Tr/Ta |
200 |
상태 코드 |
512 |
응답 바이트 |
---- |
종료 상태 플래그 |
1/1/0/0/0 |
연결 수(프로세스/프론트엔드/백엔드/서버/재시도) |
0/0 |
큐 대기(백엔드/서버) |
타이밍 필드의 의미는 다음과 같다.
| 필드 | 뜻 | 커지면 |
|---|---|---|
TR |
요청 전체를 받기까지 | 클라이언트가 느리다 |
Tw |
큐에서 대기한 시간 | 백엔드 동시 처리 한계에 걸렸다 |
Tc |
백엔드 TCP 연결 시간 | 네트워크 · 백엔드 수용 문제 |
Tr |
백엔드 응답까지 | 애플리케이션이 느리다 |
Ta |
전체 소요 | 종합 |
값이 -1 이면 그 단계에 도달하지 못한 것이다. Tc=-1 이면 연결 자체를 못 맺었다는 뜻이다.
종료 상태 플래그 앞 두 글자로 원인을 좁힌다. sH 는 백엔드 응답 대기 타임아웃, cD 는 클라이언트 데이터 대기 타임아웃, SC 는 백엔드 연결 실패다.
# 5xx 만
awk '$11 ~ /^5/' /var/log/haproxy.log | tail -50
# 느린 요청 상위
awk '{split($10,t,"/"); if (t[5]+0 > 1000) print t[5], $0}' /var/log/haproxy.log | sort -rn | head
# 백엔드를 못 고른 요청
grep '<NOSRV>' /var/log/haproxy.log | tail
# 상태 코드 분포
awk '{print $11}' /var/log/haproxy.log | sort | uniq -c | sort -rn | head
필드 위치는 로그 포맷에 따라 달라지므로 실제 출력으로 확인한 뒤 쓴다. 장기 분석이 필요하면 JSON 포맷으로 내보내 로그 수집기에 넣는 편이 낫다.
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %tsc %ac/%fc/%bc/%sc/%rc %{+Q}r"
systemctl status haproxy 에서 다음이 보일 때가 있다.
journal has been rotated since unit was started, output may be incomplete
서비스가 뜬 뒤 저널이 회전되어 status 가 초기 로그를 못 보여 준다는 뜻이며 오류가 아니다. journalctl -u haproxy 로 보면 된다.