• 로그 메시지는 스트링일 뿐이라서 이론적으로는 어떤 문장이라도 남길 수 있지만, 깔끔하고 사용하고 편한 로그를 남기려면 앞으로 이 장에서 설명한 몇 가지 베스트 프랙티스를 따르는 것이 좋다.
  • 개발자가 문제를 조사할 때는 무엇보다 로그를 가장 먼저 확인해야 한다.
    • 로그를 보면 이상한 동작이 바로 보이기 때문에 어디서부터 조사를 시작해야 할지 정확하게 진단할 수 있기 때문이다.
    • 로그가 모든 질문에 정답을 제공하는 것은 아니지만, 출발점을 찾아내는 일은 매우 중요하다.

로그를 이용하여 조사하기

  • 로그는 과거 특정 기간의 앱 실행에 초점을 맞춘다. 로그 메시지는 시간과 아주 밀접한 관계가 있다.
  • 로그를 사용할 때에는 항상 앱이 실행되는 시스템의 표준 시간대를 제일 먼저 체크해야 한다. 표준 시간대가 달라지면 혼동이 생길 수 있다.
  • 로그를 통해 조사할 수 있는 내용
    • 로그에 기록된 예외 식별
    • 예외 스택 트레이스로 어디서 메서드를 호출했는지 식별
    • 멀티스레드 아키텍처에서 커맨드의 실행 시간 측정
    • 멀티스레드 아키텍처에서 커맨드 실행 문제 조사

로깅을 구현하는 방법

로그 메시지 저장

  • 비관계형 DB에 로그 저장
    • NoSQL DB를 사용하면 로그를 좀 더 성능 위주로 저장할 수 있기 때문에 로그 메시지가 소실되거나 앱이 기록한 순서대로 저장되지 않을 가능성이 있다.
    • 일반적으로 로그 메시지에는 메시지가 저장된 타임스탬프가 있고 메시지 시작부에 포함되어 있기 때문에 크게 문제되지 않는다.
    • 요즘은 NoSQL DB에 로그 메시지를 저장하는 것이 일반적이다. 이때 로그를 저장하고 로그 메시지를 조회, 검색, 분석하는 완전한 기능을 갖춘 ELK 스택, 스플렁크 등의 엔진을 사용하는 경우가 대부분이다.
  • 파일에 로그 저장
    • 에전에는 로그를 파일로 저장했다.
    • 아직도 로그 메시지를 파일에 직접 기록하는 오래된 앱이 많지만, 이 방식은 대개 속도가 떨어지고 로깅된 데이터를 검색하기 어렵기 때문에 사용 빈도는 점점 줄어들고 있다.
  • 관계형 DB에 로그 저장
    • 관계형 DB는 데이터 일관성을 확실히 보장하므로 로그 메시지가 소실되는 일은 없고, DB에 저장된 로그 메시지는 언제라도 조회할 수 있다.
    • 그러나 이러한 일관성에는 성능 저하라는 비용이 수반된다.
  • 대부분의 앱에서 로그 메시지가 한두 개 소실된다고 큰 문제가 되진 않으므로 일반적으로 일관성보다는 성능을 좀 더 우선시한다.
    • 예외적으로 금융 앱, 결제 기능 등에서는 로그 메시지에 대하여 엄격한 규 제를 가하는 추세여서 앱에서 절대로 없어지면 안 될 중요한 로그 메시지를 관계형 DB에 저장한다.

로깅 레벨을 정의하고 로깅 프레임워크를 사용하는 방법

  • 심각도라고도 하는 로깅 레벨은 조사 중요도에 따라 로그 메시지를 분류한 것이다.
    • Error: 아주 중대한 문제가 발생한 것으로, 이런 이벤트는 반드시 기록해야 한다. 보통 자바 앱에서 처리되지 않은 예외는 에러로 기록된다.
    • Warn: 잠재적으로 에러일 수 있으나 앱이 처리한 이벤트다. 예를 들어 타사 시스템과의 데이터 연동이 처음에는 실패했지만 두 번째 시도에는 성공했다면 경고로 기록한다.
    • Info: 상시 로그 메시지. 대부분의 상황에서 앱이 어떻게 작동하고 있는지 이해하는 데 유용한, 주요한 앱 실행 이벤트를 나타낸다.
    • Debug: Info 메시지만으로 불충분한 경우에 한하여 세분하된 정보를 남긴다.
  • 로그 메시지를 심각도에 따라 분류하면 앱이 저장하는 로그 메시지 수를 최소화할 수 있다.
    • 가장 연관성이 높은 세부 정보만 기록하되, 더 자세한 정보가 필요할 때에만 더 많은 로그를 남기도록 설정한다.
  • 로깅 레벨은 간단히 구현할 수 있다.
    • 오늘날 자바 세상에는 로그백, Log4j, 자바 로깅 API등의 다양한 로깅 프레임워크가 있다.
  • log4j2에 설정할 항목은 3가지
    • logger: 어떤 메시지를 어떤 어펜더에 기록하는가?
    • appender: 로그 메시지를 어디에 기록하는가?
    • formatter: 메시지를 어떻게 출력하는가?

로깅 때문에 발생하는 문제와 예방 조치

  • 보안 및 프라이버시 문제: 로그 메시지에 개인정보가 노출된다.
    • 로그 안의 내용은 액세스할 권한을 가진 사람이라면 누구나 볼 수 있다는 사실을 명심해야 한다.
    • 그가 해당 로그를 봐도 되는지, 다른 문제는 없는지 반드시 고려하라.
    • 토큰을 서명할 때 사용하는 패스워드나 개인키, 그 밖에 주고받은 정보는 어떤 일이 있어도 로그에기록하면 안 된다.
    • 어떤 경우라도 로그나 DB에 일반 텍스트로 패스워드를 저장하는 것은 위험천만한 일이다.
    • 로그 메시지에는 개인정보에 해당하는 전화번호가 노출되는 것 같이 세계적으로 개인정보의 사용은 엄격히 제한하는 추세다.
  • 성능 문제: 지나치게 큰 로그 메시지를 과도하게 생성하면 문제가 된다.
    • 로그를 쓰려면 세부 정보를 I/O 스트림을 통해 앱 외부의 어딘가로 보내야 한다.
    • 메시지를 로깅하는 시간은 조금이라도 걸리기 때문에 너무 많은 로그 메시지를 추가하면 앱 성능이 크게 떨어질 수 있다는 점을 기억해야 한다.
    • 메시지를 너무 많이 기록하면 안 된다. 꼭 필요한 메시지만 저장하라. 로깅 레벨을 이용하면 로그 메시지를 효과적으로 제한할 수 있다.
    • 서비스를 재시작하지 않아도 로깅 메커니즘을 켜고 끌 수 있도록 구현하라.
  • 유지보수 문제: 로그를 남기는 커맨드 때문에 소스 코드의 가독성이 떨어진다.
    • 코드에 있는 커맨드 하나하나를 로깅할 필요는 없다.
    • 메서드의 인숫값과 리턴값 정도만 남기도록 로깅 코드를 적당히 구현하라.
    • 프레임워크를 사용하면 일부 로깅 코드를 메서드에서 디퍼클링할 수 있다.
      • aspect 사용 등

로그와 원격 디버깅

  • 로그와 원격 디버깅은 상호 배타적인 관계가 아니다.
    • 어느 한 쪽이 다른 쪽보다 나은 경우도 있고, 함꼐 사용해야 더 빛을 발하는 경우도 있다.
  • 디버깅은 현재에 초점을 두고 실행을 일시 중단시켜 앱의 현재 상태를 관찰한다.
  • 로깅은 과거에 더 초점을 두고 로그 메시지를 수집하여 타임라인 위주로 실행을 분석한다.
  • 흔히 복잡한 문제를 조사할 때는 디버깅과 로깅을 함께 사용하는데, 둘 중 어느 것을 사용할지는 개발자의 취향과 선호도에 달려 있다.