로깅(Logging)은 Ruby 애플리케이션이 무엇을 하고 있는지에 대한 정보를 기록으로 남기는 과정입니다.
로깅은 개발 환경과 운영(프로덕션) 환경 모두에서 매우 유용하게 활용됩니다.
왜 그럴까요?
로깅의 핵심 목적은 무슨 일이 일어났는지, 무엇이 잘못되었는지, 무엇이 제대로 작동했는지에 대한 정보를 수집하는 데 있기 때문입니다.
로깅으로 기록할 수 있는 정보
다양한 종류의 정보를 로그로 남길 수 있습니다:
- 느린 SQL 쿼리 (문제를 찾아 수정할 수 있음)
- 오류 메시지
- 상세한 요청 정보 (URL, 컨트롤러, 파라미터, 뷰 등)
이러한 정보를 활용하면 애플리케이션의 문제를 더 빠르게 진단하고 원인을 파악할 수 있습니다.
Rails 앱에서는 로그가 /log 폴더 아래에 저장됩니다.
개발 모드에서는 development.log 파일이 사용되며, rails server를 실행 중인 터미널에서도 로그 출력을 실시간으로 확인할 수 있습니다.
자, 이제 Ruby의 Logger 클래스를 사용해 로깅을 구현하는 방법을 알아보겠습니다!
Logger 클래스 기본 사용법
Ruby의 Logger 클래스는 기본 출력 형식과 다양한 심각도(severity) 레벨을 갖춘 로그를 생성할 수 있는 방법을 제공합니다.
로그를 생성하는 방법은 다음과 같습니다:
logger = Logger.new("my_log.txt")
이렇게 하면 로그 메시지가 my_log.txt 파일에 기록됩니다.
만약 프로그램이 실행 중인 터미널에 메시지를 바로 출력하고 싶다면, STDOUT으로 로그를 남기면 됩니다.
예시:
logger = Logger.new(STDOUT)
로그 레벨 이해하기
로그 메시지는 중요도에 따라 서로 다른 레벨을 가질 수 있습니다.
레벨의 종류는 다음과 같습니다:
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- UNKNOWN
각 레벨에는 대응하는 인스턴스 메서드가 존재합니다. 예를 들어 "info" 레벨로 로그를 남기고 싶다면:
이렇게 작성합니다:
logger.info("I'm reading a RubyGuide about logging!")
실제 로그 항목은 아래와 같이 기록됩니다:
# I, [2019-08-08T19:22:00.152079 #642] INFO -- : I'm reading a RubyGuide about logging!
이처럼 여러 레벨을 활용하면 로그 파일을 필터링할 수 있어 원하는 정보를 더 빠르게 찾을 수 있습니다.
또한 시간이 지남에 따라 얼마나 많은 오류와 경고 메시지가 발생했는지 파악하는 데도 도움이 됩니다.
로그 형식 커스터마이징하기
Ruby 로그의 기본 출력 형식을 사용하면 이 형식을 기반으로 하는 다양한 도구를 활용할 수 있고, 다른 개발자들도 쉽게 이해할 수 있다는 장점이 있습니다.
앞서 본 로그 라인을 다시 살펴보겠습니다:
# I, [2019-08-08T19:22:00.152079 #642] INFO -- : test
이 로그는 몇 가지 구성 요소로 이루어져 있습니다:
- 맨 앞의 "I"는 심각도 레벨의 첫 글자를 나타냅니다
- 대괄호 안에는 타임스탬프와 해당 로그 메시지를 생성한 프로세스 ID(PID)가 포함됩니다
- INFO는 심각도 레벨의 전체 이름입니다
- 콜론 뒤에는 실제로 기록된 메시지가 위치합니다
하지만 이 형식이 모든 상황에 적합하지 않을 수 있습니다. 다행히 Ruby에서는 형식을 자유롭게 변경할 수 있습니다!
변경 방법은 다음과 같습니다:
logger.formatter = proc { |severity, datetime, progname, msg| "#{severity}, #{datetime}, #{msg}\n" }
결과적으로 아래와 같은 출력을 얻게 됩니다:
DEBUG, 2019-08-08 19:39:01 +0200, testing
이 기능을 활용하면 본인과 팀에 유용한 추가 정보를 로그에 포함시킬 수 있습니다.
로그 파일 크기 제한하기
로그 파일의 크기를 관리하지 않으면 파일이 계속 커질 수 있습니다.
로그 때문에 디스크 공간이 부족해지면 시스템에서 각종 이상 현상이 발생하기 시작합니다.
해결책이 있습니다!
로그의 최대 크기를 제한할 수 있습니다.
방법은 다음과 같습니다:
MEGABYTE = 1024 ** 2
ONE_HUNDRED_MEGABYTES = MEGABYTE * 100
logger = Logger.new("my_log.txt", 1, ONE_HUNDRED_MEGABYTES)
첫 번째 매개변수는 로그 메시지를 저장할 위치입니다.
두 번째 매개변수는 보관할 로그 파일의 개수(로그 로테이션)이며, 마지막 매개변수는 파일의 최대 크기입니다.
로그 읽기 및 필터링
이제 로그를 확보했습니다. 어떻게 읽고 필요한 정보를 찾을 수 있을까요?
로그는 일반 텍스트 파일이므로 원하는 텍스트 처리 도구를 자유롭게 사용할 수 있습니다.
대표적인 예가 grep입니다.
grep은 텍스트 파일을 필터링할 때 사용하는 명령줄 도구입니다.
사용 방법:
grep INFO my_log.txt
이 명령은 "INFO"로 기록된 모든 라인을 보여줍니다.
grep을 사용하면 로그 안에서 다른 어떤 텍스트든 검색할 수 있습니다. "INFO"를 찾고자 하는 단어로 바꾸기만 하면 됩니다.
또 다른 Unix 도구로는 less(-R 옵션 사용)가 있습니다.
키보드만으로 로그를 스크롤하며 볼 수 있고, 검색 기능도 제공합니다.
이 외에도…
로그 관리 및 검색에 특화된 전용 도구들도 활용할 수 있습니다.
예를 들어 Discourse의 logster 젬(gem)이 있습니다.
정리
지금까지 Ruby에서의 로깅이 무엇인지, 어떻게 동작하는지, 그리고 실제로 활용하는 방법까지 살펴보았습니다.
이제 직접 실습해 볼 차례입니다.
읽어주셔서 감사합니다!