ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Tizen] 타이젠 앱에 디버그 로그 심어보기
    IT/Tizen 2015. 5. 30. 14:50

    개발자로서 살아온 기간내내

    함수 안팎으로 로그를 '신나게' 심어왔습니다.


    코드에 로그가 없는 경우는,

    - 아직 로그를 심기 직전이거나

    - 동작을 확인하고 로그를 지운 뒤겠죠.


    앱이 물고 있는 프레임워크의 동작이 언제나 완벽하다면,

    앱에서 로그를 출력할 일은 크게 줄어들 것입니다.


    하지만, 언제나 프레임워크는 개발막바지가 되어야 비로소 쓸만해지죠. :)

    모든 함수에 에러체크는 필수이고,

    에러로그는 가장 쉬우면서 확실한 디버깅 방법입니다.


    안녕하세요, 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.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

    위의 메뉴와 친해지도록 하세요. :)


    끝_

Designed by Tistory.