나는 현대적인 로그 시스템이 너무 복잡하여 이거슬 꽤 어려워 하는 사람이다. 그리하야 빵이 없으면 stderr을 쓰면 될텐데 같은 사람이다.
간단하게 로그 시스템을 만들어보자.
로그는 그냥 stderr로 보낸다. 파이썬 출력이 멀티쓰레드에서 쓰까서 나간다고 본 것 같으니 안전하게 Lock도 잡았다. 리눅스에서 fprintf는 그냥 MT-Safe지만 파이썬은 왠지 그렇다고 한다. 간단한 로그 레벨도 넣자. 터미널 이스케이프 문자에 의한 컬러링은 넣지 말자. 처음에만 뿌듯할 뿐이더라.
#-*- coding: utf-8 -*-
import os
import sys
import datetime
from threading import Lock
lock = Lock()
INFO = 0
WARN = 1
lev = {}
lev[INFO] = 'INFO'
lev[WARN] = 'WARN'
print_log_level = WARN
def log(log_level, msg):
if print_log_level >= log_level:
with lock:
sys.stderr.write('[%s] %s::%s\n' % (lev.get(log_level, '----'), datetime.datetime.now().strftime('%Y%m%d-%H%M%S'), msg))
def main():
print 'stdout!!!!' # 테스트 용
log(WARN, '워닝 테스트')
log(INFO, '인포 테스트')
if __name__ == '__main__':
main()
이제 stderr로만 쏴대면 된다. 도메인/스코프 구분이 필요하다면 로그 문자열 헤더에 잘-_-쓰자.
파일로 저장해야 하는데 날짜 구분해서 저장하고 싶으니 bash를 짜자. 아파치 rotatelog로 걍 보내서 할라구 했는데 마침 아파치도 안 깔려있다.. -_-..
#!/bin/bash
F='%Y%m%d'
hs=`hostname`
pwd=`pwd`
while read line
do
echo "$line" >> $pwd/data_log/$hs_mylog_`date +$F`.log
done < "${1:-/dev/stdin}"
위 스크립트는 stdin을 받아서 저장하는 것이다. 이 로그 시스템은 stderr을 쓰니까 다음과 같은 방법으로 처리할 수 있다.
pypy log.py 2>&1 | ./log_date.sh
이러면 print로 찍는 문자열(stdout)까지 모두 같이 저장되므로 stderr만 저장하고 싶은 요건에 위배가 된다.
pypy log.py 2>&1 > /dev/null | ./log_date.sh
이러면 print로 찍는 문자열은 날아가버리고 stderr만 저장하니까 의도대로 되었-_-으나.. 프로그래머가 뭘 찍어보려했던 stdout의 것들이 안 보인다. 통상 데몬모드 프로그래밍을 할테니 stdout은 그리 의미가 없을 수도 있는데 테스트 할 때에는 포그라운드 모드에서 print를 찍어보는 법이다. stdout도 처리해야 한다.
pypy log.py 3>&2 2>&1 1>&3- | ./log_date.sh
이러면 된다. 동작원리는 이렇다.
stderr를 stdout으로 보내서 파일에 기록한다.
stdout을 3번 FD로 보낸다. 그런데 얘는 실체가 없으므로 다시 stderr(2번)으로 보낸다. 그러면 터미널이 화면에 뿌려준다. 마지막 -기호는 3번을 닫으라는 뜻인데 데몬모드로 프로그램을 돌릴 때 시점은 잘 모르겠다. 데몬이 끝날 때 FD가 닫히려나?
이제 logrotate 를 쓰던가 백업 스크립트를 짜서 cron에 등록하면 땡.
.
이 글을 개발자 10000명이 싫어합니다.