개발자로서 살아온 기간내내
함수 안팎으로 로그를 '신나게' 심어왔습니다.
코드에 로그가 없는 경우는,
- 아직 로그를 심기 직전이거나
- 동작을 확인하고 로그를 지운 뒤겠죠.
앱이 물고 있는 프레임워크의 동작이 언제나 완벽하다면,
앱에서 로그를 출력할 일은 크게 줄어들 것입니다.
하지만, 언제나 프레임워크는 개발막바지가 되어야 비로소 쓸만해지죠. :)
모든 함수에 에러체크는 필수이고,
에러로그는 가장 쉬우면서 확실한 디버깅 방법입니다.
안녕하세요, Tizen 개발자 윤진입니다.
타이젠은 플랫폼 차원에서 로그를 남길 수 있는 방법을 제공합니다.
printf(), fprintf()도 사용할 수 있지만,
- SDK에 로그를 노출하거나,
- 시스템 로그들과 함께 사용하기 위해서는,
타이젠에서 제공하는 dlog를 사용해야 합니다.
타이젠 개발자 사이트(http://developer.tizen.org)에 들어가서,
하단의 API References 메뉴에 들어갑니다.
왼편에 있는 프레임에서,
API References > Native Application > Native API Reference > System > dlog
위의 항목을 선택합니다.
플랫폼에서 지원하는 로그를 위한 함수는 2가지가 있습니다.
dlog_print()와 dlog_vprint()입니다.
위의 두 함수는 공통적으로 priority와 tag를 입력해주어야 합니다.
priority는 출력할 로그의 종류에 따라 선택하면 되죠.
개발자의 필요에 따라 사용하는 로그이므로,
priority 사용에 제한을 두진 않습니다.
(소스 출처 : git://review.tizen.org/framework/system/dlog, tizen_2.3)
* @brief log priority values, in ascending priority order.
* @since_tizen 2.3
*/
typedef enum {
DLOG_UNKNOWN = 0, /**< Keep this always at the start */
DLOG_DEFAULT, /**< Default */
DLOG_VERBOSE, /**< Verbose */
DLOG_DEBUG, /**< Debug */
DLOG_INFO, /**< Info */
DLOG_WARN, /**< Warning */
DLOG_ERROR, /**< Error */
DLOG_FATAL, /**< Fatal */
DLOG_SILENT, /**< Silent */
DLOG_PRIO_MAX /**< Keep this always at the end. */
} log_priority;
tag는 일반적으로 자신의 모듈명을 입력합니다.
플랫폼에서 사용하는 tag는 대부분 대문자와 '_'(underscore)를 사용하고 있네요.
#define LOG_TAG "BUNDLE"
#define LOG_TAG "PKGMGR"
#define LOG_TAG "CAPI_APPFW_APPLICATION"
#define LOG_TAG "VOLUME"
하지만, 소문자만으로 태그이름을 정한 모듈도 있긴 합니다.
이를 보건대 태그이름에 대한 정책적인 제약은 없는 것으로 보이네요.
모듈로그는 태그명으로 검색할 수 있으므로 차후를 위해 잘 기억해두세요.
/**
* @addtogroup CAPI_SYSTEM_DLOG
* @{
*/
/**
* @brief Send log with priority and tag.
* @details for application
* @since_tizen 2.3
* @param[in] prio log_priority
* @param[in] tag tag
* @param[in] fmt format string
* @return On success, the function returns the number of bytes written.
* On error, a negative errno-style error code
* @retval #DLOG_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #DLOG_ERROR_NOT_PERMITTED Operation not permitted
* @pre none
* @post none
* @see dlog_vprint
* @code
#include<dlog.h>
int main(void)
{
int integer = 21;
char string[] = "test dlog";
dlog_print(DLOG_INFO, "USR_TAG", "test dlog");
dlog_print(DLOG_INFO, "USR_TAG", "%s, %d", string, integer);
return 0;
}
* @endcode
*/
int dlog_print(log_priority prio, const char *tag, const char *fmt, ...);
/**
* @brief Send log with priority, tag and va_list.
* @details for application
* @since_tizen 2.3
* @param[in] prio log_priority
* @param[in] tag tag
* @param[in] fmt format string
* @param[in] ap va_list
* @return On success, the function returns the number of bytes written.
* On error, a negative errno-style error code
* @retval #DLOG_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #DLOG_ERROR_NOT_PERMITTED Operation not permitted
* @pre none
* @post none
* @see dlog_print
* @code
#include<dlog.h>
void my_debug_print(char *format, ...)
{
va_list ap;
va_start(ap, format);
dlog_vprint(DLOG_INFO, "USR_TAG", format, ap);
va_end(ap);
}
int main(void)
{
my_debug_print("%s", "test dlog");
my_debug_print("%s, %d", "test dlog", 21);
return 0;
}
* @endcode
*/
int dlog_vprint(log_priority prio, const char *tag, const char *fmt, va_list ap);
함수 정의를 살펴보면,
priority와 tag를 지정한 뒤,
로그로 출력하고자 하는 포맷을 넣어주면 됩니다.
두 함수 모두 내부적으로 동일한 루틴을 따르네요.
static int __write_to_log_sd_journal(log_id_t log_id, log_priority prio, const char *tag, const char *msg)
{
pid_t tid = (pid_t)syscall(SYS_gettid);
/* XXX: sd_journal_sendv() with manually filed iov-s might be faster */
return sd_journal_send("MESSAGE=%s", msg,
"PRIORITY=%i", dlog_pri_to_journal_pri(prio),
"LOG_TAG=%s", tag,
"LOG_ID=%s", dlog_id_to_string(log_id),
"TID=%d", tid,
NULL);
}
dlog는 내부적으로 systemd의 journal을 사용합니다.
sd-journal(systemd's journal)은 systemd에서 관리하는 로그를 위한 서비스입니다.
dlog는 sd_journal_send()를 사용하여 sd-journal에 로그를 입력하고 있습니다.
sd-journal에 대한 이야기는 기회가 있을때 다시 하기로 하죠.
앱에서는 로그를 수도 없이 사용하므로,
간단히 사용할 수 있는 매크로 함수를 사용하기로 합니다.
실제 타이젠의 시스템 앱에서 사용하고 있는 매크로 함수가 있습니다.
시스템 앱에서 사용하고 있는 매크로 함수를,
SDK로 개발하는 앱에서 사용할 수 있도록 수정하였습니다.
(소스출처 : http://www.freedesktop.org/software/systemd/man/sd-journal.html, master)
#ifndef __YOUR_APPLICATION_LOG_H__
#define __YOUR_APPLICATION_LOG_H__
#include <dlog.h>
#undef LOG_TAG
#define LOG_TAG "YOUR_APPLICATION"
#define COLOR_RED "\033[0;40;31m"
#define COLOR_LIGHTBLUE "\033[1;40;34m"
#define COLOR_YELLOW "\033[1;40;33m"
#define COLOR_END "\033[0;m"
#define COLOR_GRAY "\033[1;40;30m"
#define COLOR_MAGENTA "\033[1;35m"
#if !defined(_D)
#define _D(fmt, arg...) dlog_print(DLOG_DEBUG, LOG_TAG, COLOR_YELLOW fmt COLOR_END"\n", ##arg)
#endif
#if !defined(_W)
#define _W(fmt, arg...) dlog_print(DLOG_WARN, LOG_TAG, COLOR_MAGENTA fmt COLOR_END"\n", ##arg)
#endif
#if !defined(_E)
#define _E(fmt, arg...) dlog_print(DLOG_ERROR, LOG_TAG, COLOR_RED fmt COLOR_END"\n", ##arg)
#endif
#define retv_if(expr, val) do { \
if(expr) { \
_E("(%s) -> %s() return", #expr, __FUNCTION__); \
return (val); \
} \
} while (0)
#define ret_if(expr) do { \
if(expr) { \
_E("(%s) -> %s() return", #expr, __FUNCTION__); \
return; \
} \
} while (0)
#define goto_if(expr, val) do { \
if(expr) { \
_E("(%s) -> goto", #expr); \
goto val; \
} \
} while (0)
#define break_if(expr) { \
if(expr) { \
_E("(%s) -> break", #expr); \
break; \
} \
}
#define continue_if(expr) { \
if(expr) { \
_E("(%s) -> continue", #expr); \
continue; \
} \
}
#endif /* __YOUR_APPLICATION_LOG_H__ */
헤더를 사용하려면 아래 로그헤더를 다운로드 받으시면 됩니다.
헤더를 사용하시려면,
- LOG_TAG에 자신의 모듈이름을 적어주세요.
- 일반적인 디버그 상황에서 사용하는 디버그 로그는 _D 매크로 함수를 사용하고,
_D("%s's GEOMETRY : [%d, %d, %d, %d]", (const char *) data, x, y, w, h);
- 에러는 아니지만, 일반적인 디버그 로그보다 위험한 경우는 워닝 로그 _W 매크로 함수를 사용하고,
_W("cannot find index.");
- 에러 상황에서는 _E 매크로 함수를 사용하시면 됩니다.
_E("Failed to remove vconf %s", "memory/menuscreen/mapbuf");
로그를 위한 장치가 준비되었으니,
이제 본격적으로 코딩을 하며 '신나게' 로그를 삽입하면 됩니다.
SDK > 하단 프레임 > Log
위의 메뉴와 친해지도록 하세요. :)
끝_
'IT > Tizen' 카테고리의 다른 글
[Tizen] 타이젠 gbs의 모든 것 (0) | 2015.06.07 |
---|---|
[Tizen] 우분투에 타이젠 플랫폼 툴인 gbs & sdb 설치해보기 (4) | 2015.06.04 |
[Tizen] 타이젠에 대한 문의는 여기서 하자! (0) | 2015.06.04 |
[Tizen] 타이젠 앱에서 SQLite를 이렇게 사용해볼까요? (2) | 2015.06.02 |
[Tizen] 타이젠 앱 DB는 언제 어디서 초기화하는게 좋을까요? (1) | 2015.06.02 |
[Tizen] 타이젠 플랫폼이 선택한 DB는 SQLite (0) | 2015.05.28 |
[Tizen] 타이젠 앱 Life cycle은 어떤 모습일까 (0) | 2015.05.27 |
[Tizen] 타이젠 SDK와 Git repository 연동하기 (0) | 2015.05.25 |
[Tizen] 타이젠 SDK 네이티브 앱 디렉토리 구조 (0) | 2015.05.25 |
[Tizen] 타이젠 스토어 182개국 오픈 중 3개국 유료판매가능 (0) | 2015.05.25 |