본문 바로가기
프로그래밍/iOS

디버깅시 NSLog 팁

by 백룡화검 2012. 7. 17.

출처 : http://reysion.tistory.com/19


디버깅시 유용한 팁이다.

우리는 로그를 출력할 때 NSLog를 사용하는데 파일명과 함수명 그리고 라인넘버를 출력하려면 다음과 같은 코드를 쓰면된다.

NSLog(@"%s :: %s :: %d" , __FILE__,__FUNCTION__,__LINE__);

위처럼 로그를 찍으면 해당 소스파일의 위치와 라인을 Debugger Console에서 다음과 같이 출력해 준다.
[Session started at 2011-01-24 17:03:41 +0900.]
2011-01-24 17:03:47.009 adidas[2614:207] /Users/reysion/Documents/ex/Classes/LoginView.m :: -[LoginView loginAction] :: 44
LoginView.m 파일(loginAction함수)의 44라인에서 찍은 로그라는 것을 알수 있다.

여기서 한발 더 나아가보자.
디버그 모드일 때만 로그 메시지를 보여주고 중복되는 부분을 생략해서 쉽게 로그코드를 작성할 수 없을까?
메크로를 사용하면 쉽게 해결된다.

OtherSources폴더에 있는 [프로젝트명]_prefix.pch 파일에 다음과 같이 메크로를 만든다.
#ifdef DEBUG
#define NSLog( s, ... ) NSLog( @"[%@ %s(%d)] %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __PRETTY_FUNCTION__, __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define NSLog( s, ... )
#endif

위 메크로의 내용은 Build Configuration이 Debug 일때만 NSLog를 출력하며
그 출력 값은 파일명, 함수명, 라인넘버이고 포맷은 "[파일명 함수명(라인넘버)]" 형태로 출력된다는 내용이다.

세부적으로 파일명을 "__FILE__"을 써서 그냥 출력하면 파일의 위치경로까지 출력되기 때문에 로그가 지져분해지고 가독성이 떨어지게 된다.
그래서 lastPathComponent를 써서 파일명만 출력하게 하였다.

Build Configuration이 Debug일 때라는 것은 디버그 모드일 때 즉 project Info -> Build 창에서 Configuration이 Debug로 설정 되었을 때를 말하며
Build Setting에서 "OTHER_CFLAGS" 값을 "-DDEBUG=1"로 설정해 줘야 위에서 정의한 메크로를 사용할 수 있다.
설정 방법은검색바에 "OTHER_CFLAGS"를 검색해서 값을 수정해 주면 된다.


 만약에 없다면 좌측 하단에 톱니바퀴 버튼을 클릭하여 Add User-Defined Setting에서 "OTHER_CFLAGS" 항목을 추가하고 값을 입력하면 된다.




다음은 메크로를 이용하여 NSLog를 출력했을 때 결과다.
2011-01-27 13:14:19.380 project[10058:207] <LoginView.m -[LoginView loginAction](49)> ^-^


다른 개발자들은 보통 어떻게 사용하고 있나 봤더니 다음과 같이 사용하는 개발자도 있었다. 참고해서 자기 스타일에 맞게 사용하자
/*
* There are three levels of logging: debug, info and error, and each can be enabled independently
* via the LOGGING_LEVEL_DEBUG, LOGGING_LEVEL_INFO, and LOGGING_LEVEL_ERROR switches below, respectively.
* In addition, ALL logging can be enabled or disabled via the LOGGING_ENABLED switch below.
*
* To perform logging, use any of the following function calls in your code:
*
*  LogDebug(fmt, …) ? will print if LOGGING_LEVEL_DEBUG is set on.
*  LogInfo(fmt, …) ? will print if LOGGING_LEVEL_INFO is set on.
*  LogError(fmt, …) ? will print if LOGGING_LEVEL_ERROR is set on.
*
* Each logging entry can optionally automatically include class, method and line information by
* enabling the LOGGING_INCLUDE_CODE_LOCATION switch.
*
* Logging functions are implemented here via macros, so disabling logging, either entirely,
* or at a specific level, removes the corresponding log invocations from the compiled code,
* thus completely eliminating both the memory and CPU overhead that the logging calls would add.
*/

// Set this switch to enable or disable ALL logging.
#define LOGGING_ENABLED  1

// Set any or all of these switches to enable or disable logging at specific levels.
#define LOGGING_LEVEL_DEBUG  1
#define LOGGING_LEVEL_INFO  1
#define LOGGING_LEVEL_ERROR  1

// Set this switch to set whether or not to include class, method and line information in the log entries.
#define LOGGING_INCLUDE_CODE_LOCATION 1

// ***************** END OF USER SETTINGS ***************

#if !(defined(LOGGING_ENABLED) && LOGGING_ENABLED)
#undef LOGGING_LEVEL_DEBUG
#undef LOGGING_LEVEL_INFO
#undef LOGGING_LEVEL_ERROR
#endif

// Logging format
#define LOG_FORMAT_NO_LOCATION(fmt, lvl, …) NSLog((@”[%@] ” fmt), lvl, ##__VA_ARGS__)
#define LOG_FORMAT_WITH_LOCATION(fmt, lvl, …) NSLog((@”%s [Line %d] [%@] ” fmt), __PRETTY_FUNCTION__, __LINE__, lvl, ##__VA_ARGS__)

#if defined(LOGGING_INCLUDE_CODE_LOCATION) && LOGGING_INCLUDE_CODE_LOCATION
#define LOG_FORMAT(fmt, lvl, …) LOG_FORMAT_WITH_LOCATION(fmt, lvl, ##__VA_ARGS__)
#else
#define LOG_FORMAT(fmt, lvl, …) LOG_FORMAT_NO_LOCATION(fmt, lvl, ##__VA_ARGS__)
#endif

// Debug level logging
#if defined(LOGGING_LEVEL_DEBUG) && LOGGING_LEVEL_DEBUG
#define LogDebug(fmt, …) LOG_FORMAT(fmt, @”debug”, ##__VA_ARGS__)
#else
#define LogDebug(…)
#endif

// Info level logging
#if defined(LOGGING_LEVEL_INFO) && LOGGING_LEVEL_INFO
#define LogInfo(fmt, …) LOG_FORMAT(fmt, @”info”, ##__VA_ARGS__)
#else
#define LogInfo(…)
#endif

// Error level logging
#if defined(LOGGING_LEVEL_ERROR) && LOGGING_LEVEL_ERROR
#define LogError(fmt, …) LOG_FORMAT(fmt, @”***ERROR***”, ##__VA_ARGS__)
#else
#define LogError(…)
#endif

더 알고 싶다면 아래 사이트를 참고!
http://developer.apple.com/library/mac/#documentation/DeveloperTools/gcc-4.0.1/cpp/Standard-Predefined-Macros.html
http://www.cimgf.com/2010/05/02/my-current-prefix-pch-file/
http://iphoneincubator.com/blog/debugging/the-evolution-of-a-replacement-for-nslog