고수들이 절대 가르쳐 주지 않는 C/C++ 프로그래밍 팁 #7 - 로깅 라이브러리 기본 설계

Posted at 2007. 8. 6. 07:00 // in S/W개발/고절가주팁 // by 김윤수


고절가주팁 일곱번째 시간입니다. 요 근래 고절가주팁 글을 올리지 않고, 잠시 여기 저기 외도를 했습니다. 그래도 고절가주팁을 기다려 주신 여러분께 심심한 감사의 표시로 이렇게 인사 드리겠습니다.  꾸벅~ 그리 복잡하지 않은 로깅 라이브러리이지만 나름대로 고민을 할 필요가 있어서... 고민하는 동안은 이런 저런 글들로 때웠습니다. 다들 이해해 주시리라 믿습니다.

그럼 이만 각설하고, 지난 글까지는 로깅 라이브러리의 기능적 요구 사항분석해서 몇 가지 고수준의 개념적인 클래스들-Log Source, Log Filter, Log Formatter, Log Sink, Logging Policy-을 도출해 보고, 잘 드러나지 않는 비기능적인 요구 사항을 고려해서 설계 방향을 나름대로 마련해 보았습니다.

이제 로깅 라이브러리를 본격적으로 설계해 볼 시간이 온 것 같습니다. 본격적으로 설계하기 전에 요구사항, Design Goal, 설계 방향을 마지막으로 정리해 보도록 하겠습니다.

다음은 로깅 라이브러리의 요구 사항입니다.

1. 어떤 모듈, 어떤 소스 파일의 어느 함수, 어느 위치에서 발생한 정보인지 출력할 것. 주로 디버깅 시에 유용함
2. 로깅 정보가 다양한 목적지로 저장 또는 전송될 수 있을 것
3. 모듈별로 로깅을 활성화 또는 비활성화할 수 있을 것
4. 로깅의 상세한 정도를 조정할 수 있을 것
5. 아주 상세한 디버깅 정보들은 Production 시스템에서는 자동으로 제외될 것
6. 모듈별 로깅 활성화나 로깅의 상세한 정도는 실행시에도 조정할 수 있을 것
7. 원격지로 로깅을 전송할 때 암호화/복호화을 수행할 것(어쩌다가..코더 님)
8. 로그를 기록할 때 날짜와 시간을 기록할 것(추링님)
9. 로그를 날짜별, 시간별로 다른 파일로 기록할 것(추링님)
10. 로그 파일이 지정된 크기를 넘을 경우 자동으로 다른 파일에 기록을 시작할 것(추링님)
11. 반복되는 로깅 정보를 축약해서 기록할 것(추링님)

다음은 로깅 라이브러리 Design Goal입니다.

1. 로깅 라이브러리가 최대한 다양한 환경에서 사용될 수 있도록 한다.

2. 사용하기 쉽게 만든다.
3. 로깅 라이브러리의 크기를 최대한 작게 만든다.


다음은 로깅 라이브러리의 설계 방향입니다.

1. Log Source 간의 계층 구조를 지원
2. 로깅 수준은 DEBUG, TRACE, INFO, WARNING, ERROR, FAULT 로 나눔
3.
Log Sink, Log Formatter, Log Filter 를 위해 잘 정의된 Extension Mechanism 을 제공하되 기본적인 Log Sink, Log Formatter, Log Filter 를 제공

솔직히 이 정도로는 설계를 시작하기에는 아직도 모호한 측면이 있지만, 모호한 것들은 앞으로 더 상세하게 설계/구현해 가면서 그리고 사용자들의 Feedback 을 받으면서 명확히 해 나가면 되지 않을까 생각합니다.

자~ 그럼 여러분이라면 위와 같은 사항들을 고려하여 어떻게 설계를 하실 것 같나요 ? 우선 제 마음속에 있는 가장 근본적인 개념은 다음과 같은 그림입니다(HanRSS 로 보실 경우 이미지가 너무 작게 보이는 경우에는 제 블로그에 직접 방문하시면 보다 양질의 이미지를 보실 수 있습니다. RSS에서 원래 이미지대로 보이게 하는 옵션이 tistory에 보이질 않더군요).

사용자 삽입 이미지

로깅 라이브러리의 기본 개념


LogSource 에서 LoggingEvent 가 발생하고, LogFilter 에서는 설정된 필터링 조건에 따라 필터링을 하고, LogFormatter 에서는 필터된 후의 LoggingEvent 들을 설정된 형식에 맞게 최종 출력할 형식으로 바꾸어 준 후, LogSink 에게 보내면 LogSink 가 최종적으로 저장 혹은 전송하게 되는 것이지요. (위에서 화살표는 LoggingEvent 의 흐름이라고 보시면 됩니다) 이런 기본 흐름의 변형도 다양하게 나올 수 있을 것입니다. 예를 들어, 가장 간단한 예로 다음과 같이 LogSource 와 LogSink 를 바로 연결하는 예도 있을 수 있을 것입니다.

사용자 삽입 이미지

로깅 라이브러리의 연결 예 #1


또 다른 복잡한 예를 생각한다면 다음과 같은 경우도 가능할 것입니다.

사용자 삽입 이미지

로깅 라이브러리 연결 예 #2


그런데, 이러한 연결들을 자세히 뜯어 보면, 결국 LoggingEvent 를 보내는 쪽이 있고, LoggingEvent 를 받는 쪽이 있다는 걸 알 수 있습니다. LoggingEvent 를 보내는 쪽과 LoggingEvent 를 받는 쪽으로 나누어서 살펴 보면 위와 같은 그림이 다시 보이기 시작할 것입니다.

이 측면에서 살펴 보면 LogSource 는 LoggingEvent 를 보내기만 하는 쪽이구요, LogSink 를 LoggingEvent 를 받기만 하는 쪽입니다. 반면 LogFilter 와 LogFormatter 는 LoggingEvent 를 받는 쪽과 LoggingEvent 를 보내는 쪽 양면성을 가지고 있는 녀석입니다. 그렇다면 이 두 가지 근본적인 개념을 LogPublisher-LoggingEvent 를 보내는 쪽-와 LogListener-LoggingEvent 를 받는 쪽-라고 이름 지을 수 있지 않을까요 ? 그리고, 그 둘 간에는 다음과 같이 연결이 맺어질 수 있는 거죠.

사용자 삽입 이미지

로깅 라이브러리 기본 클래스


물론 이 둘간의 연결을 위해 별도의 연관(Association) 클래스를 따로 둘 수도 있겠으나, 이 둘 간의 연결을 관리하는 건 그냥 LogPublisher 의 구현 클래스에 두는 것으로 생각하겠습니다. (그리고, 원래 UML 에서는 인터페이스간 연관(association) 관계를 만드는 것은 허용되지 않나 봅니다. 위 그림은 편의상 설명을 위해 제가 강제로 그림 편집에서 연관 관계를 집어 넣은 것입니다. 제가 사용한 UML 툴은 ArgoUML이라고 상용 툴인 IBM Rational RoseTelelogic TAU에 비해서는 기능이 많이 떨어지지만 Open Source 제품인 것을 사용했습니다.)

LogPublisher 의 오른쪽 위 귀퉁이의 작은 네모 박스는 이런 연관 관계를 LogPublisher 쪽에서 유지한다는 걸 나타내기 위해 집어 넣은 것입니다. 그리고, 이 연관 관계를 유지하기 위한 순수 가상 멤버 함수로 LogPublisher 쪽에 subscribe() 와 unsubscribe() 를 선언했습니다. 그리고, LogListener 가 LoggingEvent 를 수신할 수 있도록 publish()라는 순수 가상 멤버 함수를 정의했습니다. 한 LogPublisher 에 여러 LogListener 가 subscribe 할 수 있으므로 LogPublisher 와 LogListener 간의 관계는 1:1..n 의 관계를 갖도록 했습니다.

다음 글에서는 이 LogPublisher 와 LogListener 인터페이스를 구현하는 구체 클래스로 LogSource 와 SimpleLogSink 및 이 둘 간의 주고 받을 데이터에 해당하는 LoggingEvent 를 설계해 보도록 하겠습니다. 덧붙여 사용자가 로깅 시에 사용하게 될 프로그래밍 인터페이스도 설계해 보도록 하겠습니다. 계속해서 로깅 라이브러리 설계 및 구현 글에 관심 가져 주시면 감사하겠습니다.

제가 제안한 설계에 대해 어떻게 생각하시는지, 다른 더 좋은 방법이 있을지, 위 설계 내용 중에 잘못된 부분이 있는지 여러분의 생각도 한 번 공유해 주시면 감사하겠습니다.

그럼 지금까지 제 글을 읽어 주셔서 감사드리고, 이 글이 맘에 드신다면 살짝 여기 저기 추천 부탁드립니다. 고절가주팁이 쭉~ 갈 수 있도록 많은 관심 부탁드립니다.

제 글이 유익하셨다면 오른쪽 버튼을 눌러 제 블로그를 구독하세요. ->
블로그를 구독하는 방법을 잘 모르시는 분은 2. RSS 활용을 클릭하세요.
RSS에 대해 잘 모르시는 분은 1. RSS란 무엇인가를 클릭하세요.