Python 크래시 로그 남기기 — 원격 서버에서 stderr 리다이렉션
1. 문제 정의
라즈베리 파이처럼 원격 머신에서 실행되는 Python 스크립트가 있습니다. 이 코드는 원래 영원히 계속 실행되어야 하는데, 몇 시간이 지나면 크래시됩니다.
문제는 스크립트가 원격 머신에서 돌기 때문에 크래시 순간의 오류 메시지를 직접 볼 수 없다는 점입니다.
질문 원문 (StackOverflow 15111249 요약)
“I am running a python code in a raspberry Pi. The code is supposed to last forever. However, after a few hours it crashes. Since it is running on a remote machine, I cannot see the message it gives during the crash. How can I store this message on a file so I can see what was the problem?”
즉, 해결해야 할 문제는 **“크래시 시점의 오류 메시지를 파일로 저장해 나중에 원인을 확인할 수 있게 하는 것”**입니다. 리눅스에서 이런 일이 자동으로 일어나는지, 아니면 오류를 파일로 내보내는 함수를 직접 작성해야 하는지가 핵심 질문입니다.
2. 원인 탐구
왜 크래시 메시지가 사라지는가?
Python 스크립트가 크래시를 일으키면 인터프리터는 traceback(역추적)을 stderr(표준 에러) 로 출력합니다. 이때 기본적으로 오류 메시지는:
- 터미널(콘솔)에만 출력되고,
- 별도 파일로 저장되지 않습니다.
원격 머신에서는 셸 세션을 직접 보고 있지 않거나, 프로세스가 백그라운드로 돌고 있기 때문에 이 출력을 놓치게 됩니다. 따라서 “리눅스가 자동으로 오류를 파일에 남겨주는가?“라는 질문에 대한 답은 일반적으로 아니오입니다. 원하는 형태로 오류를 파일에 남기려면 명시적으로 조치를 취해야 합니다.
두 가지 접근 방식
크래시 로그를 남기는 방법은 크게 두 갈래로 나뉩니다.
| 접근 | 방식 | 장점 | 단점 |
|---|---|---|---|
| 셸 리다이렉션 | 실행 시 >> file 2>&1 로 출력 방향 전환 | 코드 수정 불필요, 초보자도 쉬움 | 로그 형식/시간 기록 제어는 별도 |
| 코드 내 로깅 | logging.exception() / sys.exc_info() | 타임스탬프·형식·파일 관리 가능 | 소스 수정 필요 |
3. 근본 원인 분석
크래시 메시지를 볼 수 없는 근본 원인은 오류 출력(stderr)이 저장되지 않는 기본 동작 때문입니다. Python이 예외를 던지고 종료되면 traceback은 프로세스의 표준 에러 스트림으로만 흘러갑니다. 그 스트림이 파일로 연결되어 있지 않다면 메시지는 그냥 사라집니다.
따라서 근본 해결책은 “오류 출력이 파일로 흘러가도록 스트림을 연결” 하는 것입니다. 이는 두 층위에서 해결할 수 있습니다.
- 운영(셸) 층위: 프로세스를 띄울 때 파일로 리다이렉션.
- 코드 층위: Python 내부에서 예외를 잡아 파일에 직접 기록.
4. 코드 해결책
방법 A — 셸 리다이렉션 (가장 간단, 채택 답변)
소스를 전혀 건드리지 않고, 프로세스를 시작할 때 표준 출력과 표준 에러를 모두 로그 파일로 보냅니다.
| |
>>: 출력을 파일 끝에 이어붙이기(append).>는 파일을 덮어씁니다.2>&1: stderr(2)를 stdout(1)과 같은 곳으로 보냅니다. 이 한 줄 덕분에 크래시 traceback까지 파일에 남습니다.
>>를 쓰는 이유는 오래 실행되는 스크립트에서 이전 로그가 유지되도록 하기 위함입니다. 파일을 덮어쓰는 >는 재시작할 때마다 이전 로그를 지워버립니다.
방법 B — logging 모듈로 메인 함수 크래시 기록
로그 집계(예: Logstash 같은 도구)를 사용하고 있다면, 메인 함수를 try/except로 감싸 크래시 시점을 logging.exception()으로 남기는 방식이 더 유리합니다.
| |
logger.exception()는 예외 메시지뿐 아니라 전체 traceback과 타임스탬프까지 함께 로그로 남기므로, “언제 크래시했는지"를 포함해 원인을 추적하기 쉽습니다. 파일/형식/시간 관리는 logging 설정으로 제어합니다.
방법 C — sys.exc_info()로 크래시 로그 파일 직접 작성
별도 로그 파일(예: CRASH-<timestamp>.txt)에 오류 위치와 메시지를 기록하고 싶다면 sys.exc_info()를 활용할 수 있습니다. 아래는 Python 3 코드입니다.
| |
sys.exc_info()[-1].tb_lineno: 오류가 발생한 라인 번호time.time(): 크래시 시점의 유닉스 타임스탬프 (파일명에 포함 → 크래시 마다 개별 로그 생성)
방법 비교 요약
| 구분 | 방법 A (리다이렉션) | 방법 B (logging) | 방법 C (sys.exc_info) |
|---|---|---|---|
| 코드 수정 | 불필요 | 필요 | 필요 |
| 타임스탬프 기록 | 리다이렉션만으론 없음 | 자동 포함 | 수동 추가 |
| traceback 저장 | stderr 그대로 저장 | exception()이 포함 | 라인 번호만 직접 기록 |
| 적합한 상황 | 빠른 디버깅, 초보자 | 로그 집계 도구 사용 시 | 크래시별 개별 파일 필요 시 |
5. 향후 예방 조치
- 항상 리다이렉션을 습관화 — 원격에서 장시간 실행하는 스크립트는 시작 명령부터
>> 로그파일 2>&1을 붙여 오류 스트림이 파일로 가도록 하세요. >대신>>— 재시작해도 이전 로그를 지우지 않도록 append를 사용하세요.- 구조적인 로깅 도입 — 장기 실행 서비스라면
logging모듈로 파일 핸들러를 설정하고 메인 함수를try/except로 감싸logger.exception()을 남기세요. 타임스탬프와 traceback이 함께 남아 원인 추적이 쉽습니다. - 크래시가 잦은 경우 근본 원인 확인 — 로그가 남으면 “몇 시간 뒤 크래시"의 원인(메모리 누수, 리소스 고갈, 예외 미처리 등)을 traceback으로 분석해 코드를 고치세요.
출처