본문 바로가기
Delphi Tip/+Tip

델파이 로고(logo) 관리

by MonoSoft 2022. 2. 15.
728x90
반응형

델파이 로고(logo) 관리

 

문제 1 - Runtime상에서 변수명을 보고 싶다

프로그램을 짜다 보면 중간에 어떠한 변수가 가지는

값을 확인해 보고 싶은 경우가 있습니다.

디버깅중이라면 컴파일러 IDE상에서 해당 변수를 보면 되겠지만

디버깅이 아닌 Runtime인 경우에는 해당 변수의 값을 보기가 힘이 듭니다.

 

 

해결 1 - printf를 이용해서 디버깅을 한다

이러한 경우에 가장 많이 사용하게 되는 것이

바로 출력과 관련된 API(printf, MessageBox 등)입니다.

int i;

// blah blah

printf("i=%d", i);

자, 이렇게 실시간으로 특정 변수의 값을 출력을 해서

Runtime상에서 변수의 값을  확인하게 됩니다.

 

 

문제 2 - 디버깅 다 끝났다, 그런데 언제 디버깅 코드를 다 지우지?

프로그램의 작성이 끝나고 나서는 해당 로그를 찍는 부분을 지워야 겠죠?

위와 같이 디버깅과 관련된 코드들을 일일이 찾아 가며 지우게 됩니다.

그러다가 또, 나중에 프로그램을 유지/보수하면서 기존에 만들어 놓았던

부분에 대해 또 다시 똑같은 디버깅 작업(변수 값을 출력해 보고 지우고,

출력해 보고 지우고...)을 해야 하게 생겼습니다. 예전과 똑같이

코드를 만들고 지우고, 코드를 만들고 지우고를 반복을 해야 합니다.

이러한 작업은 거의 노가다에 가깝습니다.

해결 2 - TRACE라는 macro를 이용한다

그래서 보통 생각을 하게 되죠. 한번으로

싹~ 디버깅 코드를 켰다가 껐다가 하는 방법이 없을까?

그래서 사용하는 것이 바로 TRACE라는 macro입니다.

 

#ifdef DEBUG

#define TRACE printf

#else

#define TRACE

#endif

 

이런 형식으로 사용을 해서 Release모드에서는

디버깅 관련 코드가 호출되지 않도록 하죠. 프로그램을 작성할 때에는

Debug모드로, 프로그램을 배포할 때에는

Release 모드로 컴파일을 해서 배포를 하게 됩니다.

 

 

문제 3 - 배포된 프로그램에서 에러가 난다

Release 모드로 배포는 되었는데 에러가 나 버렸습니다.

에러는 났는데, 디버깅 코드가 포함되어 있지 않으니

어디에서 에러가 났는지 감도 잡지 못합니다.

결국 개발자는 똑같은 환경을 구축하고 나서

이렇게 처음부터 다시 디버깅을 해 보게 됩니다

 

나중에 알고 보니 프로그램상의 문제라기 보다는

사용자 컴퓨터 세팅상의 문제였습니다.

프로그램 개발자는 프로그램 사용자에게 왜 컴퓨터 세팅을 이렇게 했느냐고 하고 난 후,

세팅을 이렇게 하라고 당부를 합니다.

아, 그런데 또 다른 사용자에게서 또 다른 에러가 나 버렸습니다.

두번 이런 사고 접수를 하다 보면 귀찮고 힘들어 지죠.

이럴 때 이러한 생각이 듭니다.

 

해결 3 - 디버깅 코드를 포함해서 프로그램을 배포한다

TRACE 디버깅 코드 부분을 Debug 모드뿐 아니 Release모드에서도

출력이 될 수 있도록 수정을 해서 배포를 합니다.

 

문제 4 - 디버깅 코드때문에 프로그램 실행이 이상해 진다

디버깅을 이용해서 무수히 많은 디버깅 코드때문에

프로그램의 실행 속도가 느려 집니다.

특히 멀티미디어와 같이 고속 처리와 관련된 모듈을 작성하는 경우에는

이러한 문제점이 더 심각합니다.

 

"아~ 이 문제를 어떻게 처리를 할까?"

 

해결 4 - 로그 출력 레벨 정책을 macro로 정의한다

로그를 찍는 레벨을 다음과 같은 macro를 두게 됩니다

(apach log4net 참조).

 

DEBUG : Release모드에서는 필요 없는 디버깅을 한다.
INFO    : 중요 정보를 출력한다.
WARN : 경고를 출력한다.
ERROR : 에러를 출력한다.
FATAL : 절대 일어나지 말아야 할 심각한 에러를 출력한다.

 

위와 같은 디버깅 레벨 정책을 수립하고 나서

프로그램을 작성할 때에는 전부 On을 시키고 프로그램을 배포할 때에는

DEBUG macro만 Off시켜서 컴파일하여 배포를 합니다.

 

문제 5 - 그래도 사용자에게 에러가 난다

또 에러가 났네요. 확인을 해 봐야 겠죠?

개발자는 DEBUG macro를 켠 다음 프로그램 컴파일을

다시 해서 에러가 난 사용자의 컴퓨터에서 돌려 보게 됩니다.

아, 문제점을 알아 내었습니다. 오류가 나는 코드를 수정했습니다.

 

"아~ 귀찮아... 프로그램을 재컴파일하지 않고

배포된 프로그램 상태 그대로 디버깅 코드를 확인해 볼 수 없을까?"

 

 

해결 5 - 로그 출력 레벨 정책을 환경 변수를 이용해서 결정한다

배포되는 프로그램은 log.xml, 혹은 log.ini 등의 환경 변수를 이용해서

프로그램은 실시간으로 로그 출력 레벨 정책을 결정하게 됩니다.

사용자 컴퓨터에서 에러가 나도, 세팅만 바꿔 주면

에러가 나는 부분을 쉽게 찾을 수 있습니다.

아, 이제 지금까지의 문제가 어느 정도 해결이 되었군요.

 

문제 6 - 다양한 방법으로 로그를 출력하고 싶다

이번에 회사에서 새로운 플랫폼에서 구현되는 프로젝트가 결정이 되었습니다.

RTOS 장비에서 구현을 해야 합니다.

아... 그래서 printf를 사용하지 못합니다. 화면 출력 자체가 없습니다. ㅠㅠ

 

해결 6 - 화면 출력 API를 전부 파일 저장 API로 바꾼다

고민을 하다가 출력 관련 API를 파일 저장 API로 전부 바꾸게 됩니다.

printf 부분을 전부 fprintf 로 바꿉니다.

물론 그 이후로 로그 저장 잘 되고 합니다.

 

문제 7 - 로그 찍히는 것을 실시간으로 보고 싶다

로그 출력이 실시간이 아닌 경우에는 디버깅하기가 불편합니다.

그래서 프로그램을 짤 때에는 로그 출력을 실시간으로 보고 싶어 집니다.

 

 

해결 7 - 네트워크상으로 로그를 출력하는 부분을 추가한다

네트워크(TCP, UDP 등)를 이용한 로그 전송 등 모듈을 만듭니다.

덕분에 파일 저장도 되고 실시간으로 로그도 볼 수 있습니다.

 

문제 8 - 로그 출력 모듈이 너무 많아 졌다

로그 찍는 모듈을 죽~ 정리를 해 보니

 

1. 화면 출력(printf) 디버깅 코드.

2. 파일 저장(fprintf) 디버깅 코드.

3. RS232 출력 디버깅 코드.

4. 네트워크 송신(sendto) 디버깅 코드.

5. DBWin32(OutputDebugString) 디버깅 코드.

6. 그외 로그를 출력할 수 있는 모든 디버깅 코드.

 

등으로 되어 있습니다.

컴파일 환경 플랫폼마다 #ifdef 써 가면서 컴파일 세팅을 바꿔 줘야 합니다.

이정도가 되면 욕심이 생깁니다.

"어떻게 하면 로그 찍는 부분을 어떻게 하면 다양화하면서도 관리를 잘 할 수 있을까?"

 

 

해결 8 - 클래스로 만든다

다음과 같이 Log라는 interface만 정의해 놓은 클래스를 만듭니다.

 

class Log

{

public:

   void trace(char* fmt, ...) // 외부에서는 이 함수만 이용하게 된다.

   {

     write(msg...); // 내부적으로 write라는 함수를 호출하게 된다.

   }

protected:

   virtual void write(char* msg) = 0; // 구현은 아래 클래스에서 해라!!! 나는 모른다!!!

};

 

그 다음에 Log라는 클래스를 상속을 받아서 구현(implementation)을 합니다.

class LogFile: public Log

{

   virtual void write(char* msg)

   {

     fwrite(msg...); // 파일로 저장해야 하니까 fwrite를 이용한다.

   }

};

 

예를 들어 보겠습니다.

     Log : 상위(interface) 클래스

     LogFile : 파일을 이용한 로그 출력

     LogDbWin32 : OutputDebugString을 이용한 로그 출력

     LogRs232 : RS232를 이용한 로그 출력

     LogStderr : stderr를 이용한 로그 출력

     LogStdout : stdout을 이용한 로그 출력

     LogCopyData : COPY_DATA 메세지를 이용한 로그 출력

     LogTcp : TCP 통신을 이용한 로그 출력

     LogUdp : UDP 통신을 이용한 로그 출력

     LogList : 동시에 여러개를 한꺼번에 이용할 수 있는 로그 출력

등등등...

 

이렇게 코드를 만들어 놓고 나면(write 함수만 구현하면)

다양한 플랫폼에서 프로그램 코드의 수정 없이 사용할 수가 있습니다.

 

Windows OS에서 DBWin32 를 사용하고자 할 때

main()

{

   Log::setLog(new LogDbWin32); // 나는 OutputDebugString 사용할꼬얌!!!

   ...

}

 

Unix 서버에서 stdout를 사용하고자 할 때

main()

{

   Log::setLog(new LogStdout); // 나는 stdout 사용할꼬얌!!!

   ...

}

 

파일 저장도 하고 UDP로 실시간으로도 로그 출력을 하고자 할 때

main()

{

   LogList logList;

   logList.add(new LogFile); // 로그를 파일로도 저장하고 싶고

   logList.add(new LogUdp); // 로그를 UDP로도 전송하고 싶고

   Log::setLog(&logList); // 2개 로그 객체를 전부 사용할꼬얌!!!

   ...

}

 

결론

인터넷상으로 로그를 찍는 방법에 대해 제시를

한 방법론과 코드들을 보면서 개인적으로 정리를 해 보았습니다.

Log4net이라는 모듈을 많이 검토해 보았는데, 상당히 훌륭하더군요.

 

로그를 관리하는 방법에 대해서 어떠한 방법이

제일 좋을 것인지는 100% 정답은 없는 듯 합니다.

각자 개인의 취향에 맞게, 혹은 회사의 개발 프로세스에 맞게 사용하는 것이 좋을 듯 합니다.

 

현재, VDream이라는 개인적으로 판매하고 있는

라이브러리를 업그레이드하면서,

로그 찍는 부분에 대해서도 정리를 하고 있는 중입니다.

이 부분에 대해서는 개발자 여러분들과 공유를 하는 것이

바람직할 것 같아서 지금까지 만들어 진 소스 코드를 공개해 봅니다.

 

아직 pseudo code 레벨이므로 100% 컴파일이 되지는 않습니다.

그냥 참고만 하시기 바랍니다. ^^

 

728x90
반응형

댓글