rein's world

log4cxx

Apache Software Foundation(ASF)log4cxx는 유명한 java 기반 logging library인 log4j 의 C++ 버젼이다. 사실 log4j도 ASF 산하의 프로젝트고, log4net, log4php 등등의 자매 프로젝트들이 널려 있는, MT지원이 잘 되는 로깅 라이브러리들이다.

이걸 써보려고 몇 가지 테스트(?)겸 해서 돌아가던 소스에 붙여 봤다. 일단 장점 몇 가지,

  • 설정이 간편하다 – 쌩노가다로 코드에 구조를 쳐 넣을 수도 있지만, log4j 및 기타 자매 프로젝트들과 동일한 형식을 갖는 설정 파일 혹은 xml 로 표현된 설정파일을 읽어서 로거의 출력 구조를 결정할 수도 있다.
  • 연동되는 툴들이 있다 – chainsaw 라는 java 기반의 GUI 로그 뷰어를 제공한다 (필터링이나 기타 등등 왠간한 로그 뷰어에서 필요한 기능은 대부분 제공한다)
  • 다양한 출력 포맷을 설정 파일 인자 변경만으로 지원한다 – 출력하는 형식( 날짜 -스레드 번호 – 내용으로 한다거나 날짜 – 스레드 번호 – 로거 이름 – 컨텍스트 – 내용 – 스레드 로 한다거나 하는 등등)을 조절하는 일이 설정 파일에서 간단히 이루어진다.
  • 다양한 출력 수단(media)를 제공한다 – 콘솔 출력, 파일 기반 출력(시간 기준 혹은 파일 크기 기준의 log-rotation을 지원)을 지원한다. 그리고 XML 기반의 출력이나, 출력을 소켓을 통해 보내거나 하는 것도 가능하다. Win32 플랫폼을 쓴다면 기반 라이브러리를 몇 개 가져다가 Windows NT event logger에 나오게 할 수도 있다.
  • Threading 기반의 프로그램에서 로깅을 사용하기가 편하다 – 기본적으로 스레딩을 사용하는 프로그램의 로깅을 위해 작성된 라이브러리다.
  • 출력 루틴들이 std::string, std::wstring에 기반으로 해서 돌고(C++스럽다!) 예외 기반의 오류 처리를 한다.

물론 단점도 존재한다.

  • 현재 stable 버젼(0.9.7)으로 나와있는 것은 의미가 없다 – 다음 버젼이 나올 때까지 그냥 svn HEAD를 가져다가 쓰라고 권고하고 있다.
  • Stream-like 출력을 지원하기는 하지만 완전하지 않고, 빠질 가능성도 있다(메일링 아키이브에 이 내용이 있다)
  • 비동기 출력을 지원하는 부분이 있는데(모든 방식에서 스레딩이 지원되긴 한다), 이 것에서 무한정 블락당하는 경우가 존재한다 – APR(apache portable runtime)을 사용해서 포팅하는 중에 완전히 안된 부분이 있다한다(0.9.8이 정식으로 나올 때가 되어야 완전히 해결될 듯; 내 경우에는 이 출력 방식을 안써서 확인할 수 없었다)
  • 일부 메모리 릭이 생기는 경우가 있다고 한다 – 아직까지 실제로 보지는 못했다.
  • 빌드하기 위해 필요한 것들이 많다 – 직접적으로 dependency가 걸린 apr, apr-util과 빌드하기 위한 각종 툴들(java sdk, ant, cppunit, cpptask, GNU patch/sed 등등)이 있어야한다.
  • 소스 트리가 난감할 정도로 크고, 빌드 툴이 apache ant기반이라 시험적으로 제공되는 msvc 8.0 proj 파일 생성기능을 써야 했다(물론 이것도 ant로 생성한다). 난감하게도 이건 절대 경로 기반의 include path와 DEBUG 버젼의 설정만 만들어진다 – 그래서 현재는 APR, APR-UTIL, log4cxx를 ant 출력 파일을 해석해서 변환하는 스크립트 작업을 시작했다.

프로그래밍 작업을 하는 동안, 눈에 띄는 장점은 char, wchar_t를 모두 기본적으로 지원하기 때문에 쓰기가 편하고 — 가끔 wchar_t를 쓸 수 없는 라이브러리들이 있다 — 각 로거를 구조적으로 구분해서 관리할 수 있게 해준다는 점이다.

예를 들어 2종류의 DB와 연동되어 돌아가는 3개의 CGI 프로그램이 붙어있는 웹서버라고 하자. 그럼 아래와 비슷한 계층 구조를 생각할 수 있다.

  • Web server (Webserver)
    • Sub system 1 (Webserver.subsystem1)
    • Sub system 2 (Webserver.subsystem2)
  • DB (DB)
    • MySql (DB.MySql)
    • PostgreSql (DB.Postgre)
  • CGI (CGI)
    • Web board (CGI.board)
    • Web-admin. tool (CGI.admin)
    • RPC-interface (CGI.RPC)

이런 식으로 붙어있을 때, 각각에 대해서 괄호친 부분처럼 로거를 달아줄 수 있고, 각 단계별로 로깅을 출력할 수 있다. (그리고 chainsaw에서 별도로 필터링해서 볼 수 있다)

그리고 실제로 로그를 출력하는 부분은 정의된 몇 가지 매크로를 사용할 것을 권하고 있는데, 다 다음과 같은 형태다.

LOG4CXX_<LevelName>( logger, message ) \
if( logger의 LevelName에 해당하는게 활성화 되어있으면 ) { message를 출력단으로 전송 }

이런 식이라서 특정 레벨(Debug, Info, Warn, Error, Fatal)이 지정되어 있을 때만 실제로 message에 해당하는 부분(포맷팅 루틴이 들어갈 가능성이 매우 크니 -_-; 부하가 그런대로 있을 것이다)이 계산되서 부하가 그런대로 줄어든다. 이런 방식을 택하게되면 전체 코드에 로깅 루틴은 그냥 놔두고(예를 들어 DEBUG), 설정 파일에서 Info 이상만 출력되게 한다는 등의 방식으로 로그를 남기게 할 수 있다 – 그리고 이 설정 레벨 역시 로거 별로 별도로 부여할 수 있다)

몇 가지 단점이 있긴 하지만, 잘 작성되고, 테스트되고 있고, (아마도) 앞으로 유지 보수가 장기간 지속될거라고 믿을 수 있는 thread-safe한 로깅 라이브러리인 것 같다.(프로그래밍 하기도 편한 편이다) 일단은 지금 하고 있는 일에 적용 시켜보고(local branch에만 일단 올려 본 상태), 잘 되는 것 같으면 전체 프로젝트 소스 트리에 적용해봐도 될 것 같다.

ps. 일단 라이센스 자체는 apache software license라서 closed-source 상용 소프트웨어에 사용하는데 문제가 없어 보인다. 노출되야 하는 인터페이스 수도 매우 적고, 전체 소스 트리가 좀 과하게 크다는 것만 빼면, 통일된 로그 인터페이스를 만들어서 쓰기에 매우 좋다고 생각한다.