sy/dev
Study
16 min read

리눅스 로그 보는 명령어 정리 — tail, less, journalctl, lnav

서버에서 장애 조사할 때 가장 먼저 손이 가는 도구들. tail로 따라가기, less로 탐색, journalctl로 systemd 로그 보기, lnav 같은 현대적 대안까지 정리.

공부하게 된 계기

서버에서 뭔가 안 되면 가장 먼저 하는 일이 로그 보기다. 그런데 로그는 한두 GB짜리 파일인 경우도 흔하고, 실시간으로 계속 새 줄이 추가되기도 한다. cat으로 열었다간 터미널이 마비된다.

이 글은 로그를 다루는 흐름 순서로 정리한다 — 어디 있는지 찾고 → 보고 → 따라가고 → 필터링하고 → 분석하기.

1) 로그가 사는 곳

리눅스 로그는 보통 /var/log/ 아래에 있다.

경로내용
/var/log/syslog 또는 /var/log/messages시스템 전반
/var/log/auth.log인증/로그인 (Ubuntu 계열)
/var/log/secure인증 (RHEL 계열)
/var/log/kern.log커널 메시지
/var/log/dmesg부팅 시점 커널 메시지
/var/log/nginx/access.log error.logNginx
/var/log/apache2/Apache
/var/log/journal/systemd journal (바이너리)

systemd 시대(2015~)에 와서는 텍스트 파일 대신 journal이라는 바이너리 포맷에 모인다. journalctl로 본다 (뒤에서 다룸).

2) cat — 작은 파일에만

cat /var/log/syslog

100MB짜리 파일에 cat을 쓰면 터미널이 한참 멈춘다. 로그에 cat을 쓰는 건 거의 항상 잘못된 선택이다. 다음 도구들이 있다.

3) head, tail — 앞/뒤 일부만

# 처음 10줄
head /var/log/syslog
 
# 처음 100줄
head -n 100 /var/log/syslog
 
# 마지막 10줄
tail /var/log/syslog
 
# 마지막 50줄
tail -n 50 /var/log/syslog

대부분의 경우 로그는 시간 순으로 뒤가 최신이다. 그래서 tail이 훨씬 자주 쓰인다.

4) tail -f — 실시간 추적 (가장 자주 씀)

tail -f /var/log/nginx/access.log

새 줄이 추가될 때마다 화면에 실시간으로 출력된다. 장애 재현 중일 때, 배포 직후 모니터링할 때 필수.

종료는 Ctrl+C.

-F는 더 안전하다

tail -F /var/log/app.log

대문자 -F는 파일이 로테이션돼도 따라간다. 로그가 매일 자정에 app.logapp.log.1로 회전하는 경우, -f는 옛 파일을 계속 보고 있지만 -F는 새 app.log로 자동 전환한다.

여러 파일 동시에

tail -F /var/log/nginx/access.log /var/log/nginx/error.log

각 줄 앞에 파일명이 붙는다. 두 로그를 동시에 봐야 할 때.

시작 위치 조정

# 마지막 1000줄부터 보기 시작
tail -n 1000 -F /var/log/app.log
 
# 특정 줄부터 끝까지
tail -n +500 /var/log/app.log

5) less — 큰 파일 탐색의 정석

less /var/log/syslog

cat과 다른 점:

  • 페이지 단위로 한 화면씩 보여준다
  • 검색, 점프, 따라가기 가능
  • 파일을 메모리에 다 안 올린다 — GB 단위 파일도 즉시 열림

less 키 조작

동작
Space, f다음 페이지
b이전 페이지
g맨 처음
G맨 끝
/단어단어 검색 (앞 → 뒤)
?단어단어 검색 (뒤 → 앞)
n, N다음/이전 매치
&patternpattern 매치되는 줄만 표시
Ftail -f 모드 (Ctrl+C로 빠져나옴)
q종료

lessF 키 — 화면 맨 끝으로 가서 tail -f처럼 동작한다. tail -f로 보다가 위로 스크롤하고 싶을 때 Ctrl+C로 빠져나와 g로 처음으로 갔다가 다시 F로 돌아갈 수 있다. 한 도구 안에서 다 됨.

more보다 less

more는 옛 도구. less가 뒤로도 스크롤되고 검색도 강력하다. ("less is more"라는 농담의 어원.)

6) journalctl — systemd 로그

systemd가 깔린 모든 현대 리눅스(Ubuntu 16.04+, CentOS 7+, Debian 8+)에서는 가장 중요한 명령어다.

# 전체 (페이저로 자동 열림)
journalctl
 
# 마지막 100줄
journalctl -n 100
 
# 실시간 추적
journalctl -f
 
# 마지막 부팅부터
journalctl -b
 
# 특정 서비스
journalctl -u nginx
journalctl -u nginx -f         # 그 서비스 실시간
 
# 특정 시간 범위
journalctl --since "2026-05-10 00:00" --until "2026-05-10 12:00"
journalctl --since "1 hour ago"
journalctl --since yesterday
 
# 우선순위 (err 이상만)
journalctl -p err

우선순위 레벨: emerg (0) → alertcriterrwarningnoticeinfodebug (7).

자주 쓰는 조합

# 어떤 서비스가 30분 전부터 어떤 에러를 냈는지
journalctl -u my-app --since "30 min ago" -p err
 
# 부팅 직후 발생한 모든 메시지
journalctl -b -p warning
 
# 디스크 다 차지하는 로그 정리
sudo journalctl --vacuum-time=7d   # 7일 이전 삭제
sudo journalctl --vacuum-size=500M # 500MB 넘는 부분 삭제

7) dmesg — 커널 메시지

sudo dmesg
 
# 사람이 읽기 쉬운 시간 표시
sudo dmesg -T
 
# 실시간 추적
sudo dmesg -w

USB 꽂혔을 때, 디스크 IO 에러, OOM Killer 발동 등 커널 레벨의 메시지가 여기로 온다. 하드웨어 문제 의심될 때 가장 먼저 본다.

# OOM이 발동한 적 있는지
sudo dmesg -T | grep -i "out of memory"
 
# 디스크 에러
sudo dmesg -T | grep -i "i/o error"

8) docker logs — 컨테이너 로그

요즘은 서비스가 컨테이너로 돌아가는 경우가 많다. 이 경우 journalctl이나 /var/log/를 봐도 정작 앱 로그는 안 보인다 — Docker가 따로 관리하기 때문이다.

기본 사용

# 컨테이너 ID 또는 이름 확인
docker ps
 
# 전체 로그
docker logs my-api
 
# 마지막 100줄
docker logs --tail 100 my-api
 
# 실시간 추적 (tail -F 같은 역할)
docker logs -f my-api
 
# 마지막 100줄부터 실시간 추적
docker logs --tail 100 -f my-api
 
# 타임스탬프 추가
docker logs -t my-api

-f는 가장 자주 쓴다. 컨테이너가 종료돼도 마지막 로그는 남으니 사후 분석에도 유용.

시간 범위로 자르기

# 최근 10분
docker logs --since 10m my-api
 
# 특정 시각 이후
docker logs --since 2026-05-10T12:00:00 my-api
 
# 시각 범위
docker logs --since 1h --until 30m my-api

--since/--until1h, 30m, 2025-01-01 같은 표현을 모두 받는다.

docker compose — 여러 서비스를 한번에

# 전체 서비스 로그 (현재 디렉토리의 compose 프로젝트)
docker compose logs
 
# 특정 서비스만
docker compose logs web
 
# 실시간 추적
docker compose logs -f
 
# 여러 서비스 동시에
docker compose logs -f web db
 
# 마지막 50줄부터 추적
docker compose logs --tail=50 -f

서비스 이름별로 색깔이 다르게 표시돼서 어느 컨테이너에서 온 로그인지 한눈에 보인다.

실제 로그 파일은 어디 있나

JSON 파일 드라이버(기본)인 경우, 호스트의 다음 위치에 로그가 쌓인다.

/var/lib/docker/containers/<container-id>/<container-id>-json.log
# 컨테이너 ID 알아내기
docker inspect --format='{{.Id}}' my-api
 
# 직접 tail
sudo tail -F /var/lib/docker/containers/<id>/<id>-json.log

JSON 한 줄당 한 메시지 형식이라 그대로 읽기는 불편하다. jq를 거치면 깔끔.

sudo tail -F /var/lib/docker/containers/<id>/<id>-json.log | jq -r '.log'

로그 드라이버가 journald라면

# 컨테이너 이름으로 검색
journalctl CONTAINER_NAME=my-api -f
 
# Compose 환경에서
journalctl CONTAINER_NAME=myproject-web-1 --since "10 min ago"

운영 환경에서 systemd 통합을 선호하는 팀은 이 드라이버를 자주 쓴다. 그러면 journalctl의 모든 기능(시간 필터, 우선순위 등)을 그대로 쓸 수 있다.

로그가 디스크를 잡아먹을 때

기본 설정에서 Docker 로그는 무한히 커진다. 운영 중인 호스트가 갑자기 디스크 가득 찼다면 십중팔구 이거다.

# 컨테이너별 로그 크기 확인
sudo du -sh /var/lib/docker/containers/*/
 
# 실시간 디스크 점유 모니터링
docker system df -v

해결은 daemon.json에 로그 회전 설정.

/etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

이미 차오른 로그를 즉시 비우려면:

# 위험 — 컨테이너가 살아있는 상태에서 직접 truncate
sudo truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log

또는 컨테이너를 재시작하면 새 파일로 시작된다.

grep과 조합

docker logs도 그냥 stdout이라 파이프가 다 된다.

# ERROR만
docker logs my-api 2>&1 | grep ERROR
 
# 실시간 ERROR 추적
docker logs -f my-api 2>&1 | grep --line-buffered ERROR
 
# 5xx 응답만 (Nginx 컨테이너)
docker compose logs nginx 2>&1 | awk '$9 ~ /^5/'
⚠️

docker logs는 stdout과 stderr 둘 다 출력한다. stderr가 따로 흘러서 grep에 안 잡히는 경우가 흔하다 — 그래서 2>&1로 합쳐주는 습관. docker logs my-api | grep error만 치면 stderr가 새는 걸 보고 한참 헤맨다.

자주 쓰는 한 줄 요약

# 컨테이너 들어가기 전 마지막 로그 빠르게 보기
docker logs --tail 50 my-api
 
# 배포 직후 모니터링
docker compose logs -f --tail=100 web
 
# 어제 뭐가 일어났나
docker logs --since 24h --until 12h my-api | grep -i error

9) 필터링 — grep / awk 조합

대부분의 로그 작업은 결국 필터링이다.

grep 기본

# ERROR 포함 줄만
grep ERROR /var/log/app.log
 
# 대소문자 무시
grep -i error /var/log/app.log
 
# 앞뒤 컨텍스트 3줄
grep -B 3 -A 3 "ERROR" /var/log/app.log
grep -C 3 "ERROR" /var/log/app.log    # 위와 같음
 
# 줄 번호
grep -n ERROR /var/log/app.log
 
# 매칭 카운트
grep -c ERROR /var/log/app.log
 
# 정규식
grep -E "ERROR|WARN" /var/log/app.log

tail + grep — 실시간 필터

# 실시간으로 흐르는 로그에서 ERROR만
tail -F /var/log/app.log | grep --line-buffered ERROR

--line-buffered 필요하다. 안 붙이면 grep이 4KB씩 버퍼링해서 출력이 뚝뚝 끊긴다.

awk — 컬럼 단위 처리

Nginx access log를 예로 들면:

192.168.1.1 - - [10/May/2026:12:34:56 +0000] "GET /api/posts HTTP/1.1" 200 1234
# IP만 (1번째 컬럼)
awk '{print $1}' access.log
 
# IP별 카운트, 많은 순으로
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
 
# 5xx 응답만 (9번째 컬럼이 상태코드)
awk '$9 >= 500' access.log
 
# 응답 크기 합계 (10번째 컬럼)
awk '{sum += $10} END {print sum}' access.log

10) lnav — 로그 전용 페이저

less보다 한 단계 위. 로그 포맷을 자동 인식해서 색깔 입히고 시간 파싱까지 해준다.

brew install lnav
sudo apt install lnav
lnav /var/log/nginx/access.log
lnav /var/log/   # 디렉토리 통째로

기능:

  • 시간 기준으로 여러 파일 자동 머지
  • SQL 쿼리로 로그 분석 (;SELECT cs_uri FROM access_log WHERE sc_status >= 500;)
  • 자동 syntax 하이라이트
  • 정규식 필터링

서버 장애 조사가 일상이라면 한 번 깔아둘 만하다.

11) multitail — 여러 로그 동시 보기

brew install multitail
sudo apt install multitail
# 두 로그를 분할 화면으로
multitail /var/log/nginx/access.log /var/log/nginx/error.log
 
# 색깔 입혀서
multitail -cS apache /var/log/nginx/access.log

tail -F file1 file2는 줄이 섞이지만, multitail은 화면을 분할해서 보여준다. 모니터링 대시보드 흉내.

실전 패턴 6개

A. 배포 직후 5분 모니터링

# systemd 서비스
journalctl -u my-app -f -p warning
 
# 파일 기반
tail -F /var/log/app.log | grep --line-buffered -E "ERROR|WARN"
 
# Docker Compose
docker compose logs -f --tail=100 web

B. 30분 안에 발생한 5xx 응답 카운트

journalctl -u nginx --since "30 min ago" \
  | awk '$9 ~ /^5/ {print $9}' | sort | uniq -c

C. 디스크 갑자기 가득 참 — 무엇이 OOM 됐나

sudo dmesg -T | grep -i "killed process"
journalctl -k --since today | grep -i oom

D. SSH 무차별 대입 시도 흔적

sudo journalctl _COMM=sshd | grep "Failed password" | awk '{print $NF}' | sort | uniq -c | sort -rn

E. 특정 사용자의 활동 (Nginx)

grep "user-id-12345" /var/log/nginx/access.log | tail -100

F. 어제 자정~6시 사이의 ERROR

journalctl --since "yesterday 00:00" --until "yesterday 06:00" -p err

정리 — 어떤 걸 쓸까

일상: tail -F로 따라가기, less로 탐색, grep으로 필터링. 이 셋이면 90%.
systemd 환경: journalctl -u <service> -f가 첫 번째 손.
컨테이너 환경: docker logs -f 또는 docker compose logs -f <service>. stderr 합치는 2>&1 잊지 말 것.
커널/하드웨어 의심: sudo dmesg -T.
복잡한 분석: awk 한 줄짜리, 안 되면 lnav.

로그를 잘 보는 사람과 못 보는 사람의 차이는 장애 해결 시간으로 직결된다. 도구를 손에 익혀두자.

시리즈 정리

리눅스 명령어 시리즈도 이번 편으로 3편이다.

다음 편이 있다면 — 프로세스/리소스 모니터링(top, htop, ps, iostat)이나 네트워크 디버깅(netstat, ss, tcpdump) 정도가 자연스러운 흐름이다.

참고 자료

Comments