반응형

 

    솔루션이나 Application을 개발하다보면 debugging등을 위하여 log를 생성한다. 로그를 찍기 위하여 간단하게는 printf() 함수나 파일로 남기기 위해서는 fprintf()함수를 통하여 로그를 남긴다. 로그를 남길 때에 프로그램의 파일명, 함수명, 로그 위치, 로그 생성 시간 등을 함께 남겨야 debugging에 도움이 된다. log 생성 시간 등은 log와 log 구간 사이의 소요시간을 알 수 있어서 성능 등을 파악할 수 있다.

    그러나 상세한 부가적인 log를 생성하기 위하여 아래와 같이 기존의 printf(3)함수나 fprintf(3)함수로 log를 찍는다면 로그를 찍는 곳마다 부가적인 정보를 남기기 위해 코드량이 많아지게 된다. 

 

 

 

printf(3) 등과 같은 기존 함수로 로그를 남기는 경우

struct timeval tv;
struct tm *tm1;

gettimeofday(&tv, NULL);
tm1 = localtime(&tv.tv_sec);

printf("%04d%02d%02d:%02d%02d%02d%06d:",   
             1900 + tm1->tm_year, tm1->tm_mon + 1, 
             tm1->tm_mday, tm1->tm_hour, tm1->tm_min, tm1->tm_sec, tv.tv_usec);

printf("%s:%s:%05d:%s\n", __FILE__, __func__, __LINE__, "aaa 함수를 시작합니다.");

 

    이를 해결하기 위해 보통은 별도의 log를 생성하는 함수를 구현하여야 한다. 그렇게 하기 위해서는 printf와 비슷한 (파라미터의 갯수, 타입이 정해지지 않은) 동적 파라미터 함수를 개발해야 하는 데, 이를 가능하게 하는 함수들은 stdarg.h 에 포함되어 있다.

 


관련 함수

 

1. 동적 파라미터를 관리하는 함수

#include <stdarg.h>

/* 동적 파라미터의 위치를 지정하는 함수 */
void va_start(va_list ap, last);

/* 동적 파라미터의 사용을 해제하는 함수 */
void va_end(va_list ap);

.


 

2. printf/fprintf/sprintf/snprintf 함수들을 customizing하는 함수

#include <stdarg.h>

/* printf()처럼 화면에 출력하는 동적 함수를 만들 때 사용*/
int vprintf(const char *format, va_list ap);

/* fprintf()처럼 파일에 출력하는 동적 함수를 만들 때 사용*/
int vfprintf(FILE *stream, const char *format, va_list ap);

/* sprintf()처럼 문자열을 formatting하는 동적 함수를 만들 때 사용*/
int vsprintf(char *str, const char *format, va_list ap);

/* snprintf()처럼 길이 제한 문자열을 formatting하는 동적 함수를 만들 때 사용*/
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

 


사용 예제

 

만약에 
int logging(const char *file, const char *func, int line_num, const char *format, ...);
이라는 동적 파라미터의 함수를 만든다고 하면 아래와 같은 형식으로 개발을 하면 된다.

 

Example 1 : 화면에 정해진 format으로 로그를 생성하는 함수 

int logging(const char *file, const char *func, int line_num, const char *format, ...)
{
    va_arg args;
    int sz = 0;
    struct timeval tv;
    struct tm *tm1;

    sz = printf("%15s:%20s:%05d: ", file, func, line_num);
    gettimeofday(&tv, NULL);
    tm1 = localtime(&tv.tv_sec);

    sz += printf("%04d%02d%02d:%02d%02d%02d%06d:", 
                        1900 + tm1->tm_year, tm1->tm_mon + 1, tm1->tm_mday, 
                        tm1->tm_hour, tm1->tm_min, tm1->tm_sec, tv.tv_usec);


    /* 동적 파라미터 ...이 format 다음부터라는 것을 설정합니다. */
    va_start(args, format);

    /* ...을 args로 대체하게 하는 함수들이 v*printf 계열 함수입니다. */
    sz += vprintf(format, args);

    /* 사용한 동적 파라미터의 사용을 해제합니다. */
    va_end(args);

    return sz;
}

 

함수를 호출할 때에는 

logging(__FILE__, __func__, __LINE__, "생년월일이 날짜 format이 아닙니다. 생년월일:[%s]\n", birthday);

처럼 사용한다.

 


 

Example 2 : 파일에 정해진 format으로 로그를 생성하는 함수 

int logging(FILE *fp, const char *file, const char *func, int line_num, const char *format, ...)
{
    va_arg args;
    int sz = 0;
    struct timeval tv;
    struct tm *tm1;

    sz = fprintf(fp, "%15s:%20s:%05d: ", file, func, line_num);
    gettimeofday(&tv, NULL);
    tm1 = localtime(&tv.tv_sec);

    sz += fprintf(fp, "%04d%02d%02d:%02d%02d%02d%06d:", 
                        1900 + tm1->tm_year, tm1->tm_mon + 1, tm1->tm_mday, 
                        tm1->tm_hour, tm1->tm_min, tm1->tm_sec, tv.tv_usec);


    /* 동적 파라미터 ...이 format 다음부터라는 것을 설정합니다. */
    va_start(args, format);

    /* ...을 args로 대체하게 하는 함수들이 v*printf 계열 함수입니다. */
    sz += vfprintf(format, args);

    /* 사용한 동적 파라미터의 사용을 해제합니다. */
    va_end(args);

    return sz;
}

 

함수를 호출할 때에는 

logging(log_fp, __FILE__, __func__, __LINE__, "생년월일이 날짜 format이 아닙니다. 생년월일:[%s]\n", birthday); 

처럼 사용한다.

 


LOG 처리에서의 확장

 

위의 __FILE__, __func__, __LINE__는 아래의 의미로 예약된 preprocessor입니다.

__FILE__ : 소스 파일명을 의미하는 예약된 preprocessor
__func__ : 소스 상에 현재 라인이 어느 함수인지를 나타내는 예약된 preprocessor
__LINE__ : 소스 상에 현재 라인이 몇번째 라인인지를 나타내는 예약된 preprocessor

 

위의 함수를 호출할 때 마다 파일명, 함수명, 라인번호를 parameter로 계속 입력하는 것도 귀챦은 일이다. 이를 위해서 이렇게 항상 정해진 호출은 Macro 함수의 형태로 제공해주면 훨씬 사용하기 편리하다.

#define LOGGING(format, ...) \
	logging(global_log_fp, __FILE__, __func__, __LINE__, format, __VA_ARGS__)

__VA_ARGS__ 예약어는 매크로 함수의 ...을 의미합니다.

 

 

이렇게 header file에 macro 함수로 선언해 두었다면, 

LOGGING("생년월일이 날짜 format이 아닙니다. 생년월일:[%s]\n", birthday);

로 쉽게 호출할 수 있습니다. 

 

 

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,